|
|
@@ -0,0 +1,192 @@
|
|
|
+import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi';
|
|
|
+import { ErrorSchema } from '../middleware/errorHandler';
|
|
|
+import { AppDataSource } from '../data-source';
|
|
|
+import { User } from '../modules/users/user.entity';
|
|
|
+import { Role } from '../modules/users/role.entity';
|
|
|
+import * as bcrypt from 'bcrypt';
|
|
|
+import { generateJwtSecret } from '../utils/env-init';
|
|
|
+import { writeFileSync } from 'fs';
|
|
|
+
|
|
|
+const initRouter = new OpenAPIHono();
|
|
|
+
|
|
|
+// Zod 验证模式
|
|
|
+const DatabaseConfigSchema = z.object({
|
|
|
+ host: z.string(),
|
|
|
+ port: z.number(),
|
|
|
+ username: z.string(),
|
|
|
+ password: z.string(),
|
|
|
+ database: z.string()
|
|
|
+});
|
|
|
+
|
|
|
+const AdminUserSchema = z.object({
|
|
|
+ username: z.string().min(3),
|
|
|
+ password: z.string().min(8)
|
|
|
+});
|
|
|
+
|
|
|
+const InitConfigSchema = z.object({
|
|
|
+ dbConfig: DatabaseConfigSchema,
|
|
|
+ adminUser: AdminUserSchema
|
|
|
+});
|
|
|
+
|
|
|
+// 检查初始化状态路由
|
|
|
+const statusRoute = createRoute({
|
|
|
+ method: 'get',
|
|
|
+ path: '/status',
|
|
|
+ responses: {
|
|
|
+ 200: {
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: z.object({
|
|
|
+ initialized: z.boolean()
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ description: '返回系统初始化状态'
|
|
|
+ },
|
|
|
+ 500: {
|
|
|
+ description: '初始化状态检查失败',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: ErrorSchema
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+initRouter.openapi(statusRoute, async (c) => {
|
|
|
+ try {
|
|
|
+ const isInitialized = await checkInitialization();
|
|
|
+ return c.json({ initialized: isInitialized }, 200);
|
|
|
+ } catch (error) {
|
|
|
+ return c.json({
|
|
|
+ code: 500,
|
|
|
+ message: '初始化状态检查失败'
|
|
|
+ }, 500);
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+// 提交配置路由
|
|
|
+const configRoute = createRoute({
|
|
|
+ method: 'post',
|
|
|
+ path: '/config',
|
|
|
+ request: {
|
|
|
+ body: {
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: InitConfigSchema
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ responses: {
|
|
|
+ 200: {
|
|
|
+ description: '系统初始化成功',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: z.object({
|
|
|
+ success: z.boolean(),
|
|
|
+ jwtSecret: z.string().optional()
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 400: {
|
|
|
+ description: '初始化失败',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: ErrorSchema
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 500: {
|
|
|
+ description: '服务器内部错误',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: ErrorSchema
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+initRouter.openapi(configRoute, async (c) => {
|
|
|
+ const { dbConfig, adminUser } = c.req.valid('json');
|
|
|
+
|
|
|
+ if (await checkInitialization()) {
|
|
|
+ return c.json({
|
|
|
+ code: 400,
|
|
|
+ message: '系统已初始化'
|
|
|
+ }, 400);
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ await validateDatabase(dbConfig);
|
|
|
+ await generateEnvFile(dbConfig);
|
|
|
+ const jwtSecret = generateJwtSecret();
|
|
|
+ await createAdminUser(adminUser);
|
|
|
+
|
|
|
+ return c.json({
|
|
|
+ success: true,
|
|
|
+ jwtSecret
|
|
|
+ }, 200);
|
|
|
+ } catch (error) {
|
|
|
+ return c.json({
|
|
|
+ code: 400,
|
|
|
+ message: error instanceof Error ? error.message : '未知错误'
|
|
|
+ }, 400);
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+export { initRouter };
|
|
|
+
|
|
|
+
|
|
|
+async function checkInitialization(): Promise<boolean> {
|
|
|
+ // 检查数据库是否已初始化
|
|
|
+ return AppDataSource.isInitialized;
|
|
|
+}
|
|
|
+
|
|
|
+async function validateDatabase(config: any) {
|
|
|
+ // 使用TypeORM验证数据库连接
|
|
|
+ const testDataSource = AppDataSource.setOptions(config);
|
|
|
+ await testDataSource.initialize();
|
|
|
+ await testDataSource.destroy();
|
|
|
+}
|
|
|
+
|
|
|
+async function generateEnvFile(config: any) {
|
|
|
+ const envContent = `DB_HOST=${config.host}
|
|
|
+DB_PORT=${config.port}
|
|
|
+DB_USERNAME=${config.username}
|
|
|
+DB_PASSWORD=${config.password}
|
|
|
+DB_DATABASE=${config.database}
|
|
|
+`;
|
|
|
+ writeFileSync('.env', envContent);
|
|
|
+}
|
|
|
+
|
|
|
+async function createAdminUser(userData: any) {
|
|
|
+ const userRepo = AppDataSource.getRepository(User);
|
|
|
+ const roleRepo = AppDataSource.getRepository(Role);
|
|
|
+
|
|
|
+ // 检查是否已存在管理员
|
|
|
+ const existingAdmin = await userRepo.findOne({ where: { username: userData.username } });
|
|
|
+ if (existingAdmin) {
|
|
|
+ throw new Error('管理员用户已存在');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建管理员角色
|
|
|
+ let adminRole = await roleRepo.findOne({ where: { name: 'admin' } });
|
|
|
+ if (!adminRole) {
|
|
|
+ adminRole = roleRepo.create({ name: 'admin', permissions: ['*'] });
|
|
|
+ await roleRepo.save(adminRole);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建管理员用户
|
|
|
+ const hashedPassword = await bcrypt.hash(userData.password, 10);
|
|
|
+ const adminUser = userRepo.create({
|
|
|
+ username: userData.username,
|
|
|
+ password: hashedPassword,
|
|
|
+ roles: [adminRole]
|
|
|
+ });
|
|
|
+
|
|
|
+ await userRepo.save(adminUser);
|
|
|
+}
|