import { app, db, schema } from '@/app.ts'; import { User } from '@/models/user.ts'; import { nanoid } from 'nanoid'; import { CustomError } from '@kevisual/router'; import { backupUserA, deleteUser, mvUserAToUserB } from '@/routes/file/index.ts'; import { AppHelper } from '@/routes/app-manager/module/index.ts'; import { eq } from 'drizzle-orm'; // import { mvAppFromUserAToUserB } from '@/routes/app-manager/admin/mv-user-app.ts'; export const checkUsername = (username: string) => { if (username.length > 30) { throw new CustomError(400, '用户名不能过长'); } if (!/^[a-zA-Z0-9_@]+$/.test(username)) { throw new CustomError(400, '用户名包含非法字符'); } if (username.includes(' ')) { throw new CustomError(400, '用户名不能包含空格'); } }; export const checkUsernameShort = (username: string) => { if (username.length <= 3) { throw new CustomError(400, '用户名不能过短'); } }; export const toChangeName = async (opts: { id: number; newName: string; admin?: boolean, ctx: any }) => { const { id, newName, admin = false, ctx } = opts; if (!admin) { checkUsernameShort(newName); } const user = await User.findByPk(id); if (!user) { ctx.throw(404, 'User not found'); } const oldName = user.username; checkUsername(newName); const findUserByUsername = await User.findOne({ where: { username: newName } }); if (findUserByUsername) { ctx.throw(400, 'Username already exists'); } user.username = newName; const data = user.data || {}; data.canChangeUsername = false; user.data = data; try { await user.save(); // 迁移文件数据 await backupUserA(oldName, user.id); // 备份文件数据 await mvUserAToUserB(oldName, newName, true); // 迁移文件数据 // await mvAppFromUserAToUserB(oldName, newName); // 迁移应用数据 if (['org', 'user'].includes(user.type)) { const type = user.type === 'org' ? 'org' : 'user'; await User.clearUserToken(user.id, type); // 清除旧token } // 更新应用数据中的用户名 const apps = await db.select().from(schema.kvApp).where(eq(schema.kvApp.user, oldName)); for (const appItem of apps) { const appData = appItem.data as any; const newFiles = await AppHelper.getNewFiles(appData.files || [], { oldUser: oldName, newUser: newName }); await db.update(schema.kvApp) .set({ user: newName, data: { ...appData, files: newFiles }, updatedAt: new Date().toISOString() }) .where(eq(schema.kvApp.id, appItem.id)); } } catch (error) { console.error('迁移文件数据失败', error); ctx.throw(500, 'Failed to change username'); } return user; } app .route({ path: 'user', key: 'changeName', middleware: ['auth-admin'], }) .define(async (ctx) => { const { id, newName } = ctx.query.data || {}; try { if (!id || !newName) { ctx.throw(400, '参数错误'); } const user = await toChangeName({ id, newName, admin: true, ctx }); ctx.body = await user?.getInfo?.(); } catch (error) { console.error('changeName error', error); ctx.throw(500, 'Failed to change username'); } }) .addTo(app); app .route({ path: 'user', key: 'checkUserExist', middleware: ['auth'], }) .define(async (ctx) => { const { username } = ctx.query.data || {}; if (!username) { ctx.throw(400, 'Username is required'); } checkUsername(username); const user = await User.findOne({ username }); ctx.body = { id: user?.id, username: user?.username, }; }) .addTo(app); app .route({ path: 'user', key: 'resetPassword', middleware: ['auth-admin'], }) .define(async (ctx) => { const { id, password } = ctx.query.data || {}; const user = await User.findByPk(id); if (!user) { ctx.throw(404, 'User not found'); } let pwd = password || nanoid(6); user.createPassword(pwd); await user.save(); ctx.body = { id: user.id, username: user.username, password: !password ? pwd : undefined, }; }) .addTo(app); app .route({ path: 'user', key: 'createNewUser', middleware: ['auth-admin'], }) .define(async (ctx) => { const { username, password, description } = ctx.query.data || {}; if (!username) { ctx.throw(400, 'Username is required'); } checkUsername(username); const findUserByUsername = await User.findOne({ username }); if (findUserByUsername) { ctx.throw(400, 'Username already exists'); } let pwd = password || nanoid(6); const user = await User.createUser(username, pwd, description); ctx.body = { id: user.id, username: user.username, description: user.description, password: pwd, }; }) .addTo(app); app .route({ path: 'user', key: 'deleteUser', middleware: ['auth-admin'], }) .define(async (ctx) => { const { id } = ctx.query.data || {}; const user = await User.findByPk(id); if (!user) { ctx.throw(404, 'User not found'); } await db.delete(schema.cfUser).where(eq(schema.cfUser.id, user.id)); backupUserA(user.username, user.id); deleteUser(user.username); // TODO: EXPIRE 删除token ctx.body = { id: user.id, username: user.username, message: 'User deleted successfully', }; }) .addTo(app);