user-manager change

This commit is contained in:
2025-04-03 20:08:40 +08:00
parent 8fafe74fa3
commit d97053a443
12 changed files with 297 additions and 22 deletions

View File

@@ -0,0 +1,149 @@
import { app } 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';
export const checkUsername = (username: string) => {
if (username.length > 30) {
throw new CustomError(400, 'Username cannot be too long');
}
if (!/^[a-zA-Z0-9_@]+$/.test(username)) {
throw new CustomError(400, 'Username cannot contain special characters');
}
if (username.includes(' ')) {
throw new CustomError(400, 'Username cannot contain spaces');
}
};
export const checkUsernameShort = (username: string) => {
if (username.length < 3) {
throw new CustomError(400, 'Username cannot be too short');
}
};
app
.route({
path: 'user',
key: 'changeName',
middleware: ['auth-admin'],
})
.define(async (ctx) => {
const { id, newName } = ctx.query.data || {};
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;
try {
await user.save();
// 迁移文件数据
await backupUserA(oldName, user.id); // 备份文件数据
await mvUserAToUserB(oldName, newName, true); // 迁移文件数据
} catch (error) {
console.error('迁移文件数据失败', error);
ctx.throw(500, 'Failed to change username');
}
ctx.body = user;
})
.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({ where: { 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({ where: { 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 user.destroy();
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);

View File

@@ -9,4 +9,6 @@ import './init.ts'
import './web-login.ts'
import './org-user/list.ts'
import './org-user/list.ts'
import './admin/user.ts';

View File

@@ -1,6 +1,8 @@
import { app } from '@/app.ts';
import { User } from '@/models/user.ts';
import { CustomError } from '@kevisual/router';
import { checkUsername } from './admin/user.ts';
import { nanoid } from 'nanoid';
app
.route({
@@ -18,7 +20,6 @@ app
})
.addTo(app);
app
.route({
path: 'user',
@@ -28,9 +29,12 @@ app
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { id, username, password, description } = ctx.query.data || {};
if (!id) {
throw new CustomError(400, 'id is required');
}
const user = await User.findByPk(id);
if (user.id !== tokenUser.id) {
throw new CustomError(401, 'Permission denied');
throw new CustomError(403, 'Permission denied');
}
if (!user) {
@@ -59,21 +63,26 @@ app
.route({
path: 'user',
key: 'add',
middleware: ['auth'],
middleware: ['auth-admin'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { username, password, description } = ctx.query.data || {};
if (!username) {
throw new CustomError(400, 'username is required');
}
const user = await User.createUser(username, password, description);
const token = await user.createToken();
checkUsername(username);
const findUserByUsername = await User.findOne({ where: { username } });
if (findUserByUsername) {
throw new CustomError(400, 'username already exists');
}
const pwd = password || nanoid(6);
const user = await User.createUser(username, pwd, description);
ctx.body = {
id: user.id,
username: user.username,
description: user.description,
needChangePassword: user.needChangePassword,
token,
password: pwd,
};
});
})
.addTo(app);

View File

@@ -209,13 +209,13 @@ app
const { id } = tokenUser;
const user = await User.findByPk(id);
if (!user) {
ctx.throw(500, 'user not found');
ctx.throw(404, 'user not found');
}
user.setTokenUser(tokenUser);
if (username) {
user.username = username;
}
if (password) {
if (password && user.type !== 'org') {
user.createPassword(password);
}
if (description) {