feat: add CNB login functionality and user management

- Introduced `cnb-login` route to handle user login via CNB token.
- Created `CnbServices` class for managing CNB user interactions.
- Added `findByCnbId` method in the User model to retrieve users by CNB ID.
- Updated error handling to provide more structured error messages.
- Enhanced user creation logic to handle CNB users.
- Added tests for the new CNB login functionality.
This commit is contained in:
2026-02-20 23:30:53 +08:00
parent 1782a9ef19
commit 366a21d621
16 changed files with 392 additions and 40 deletions

View File

@@ -9,18 +9,18 @@ import { eq } from 'drizzle-orm';
export const checkUsername = (username: string) => {
if (username.length > 30) {
throw new CustomError(400, '用户名不能过长');
throw new CustomError(400, { message: '用户名不能过长' });
}
if (!/^[a-zA-Z0-9_@]+$/.test(username)) {
throw new CustomError(400, '用户名包含非法字符');
throw new CustomError(400, { message: '用户名包含非法字符' });
}
if (username.includes(' ')) {
throw new CustomError(400, '用户名不能包含空格');
throw new CustomError(400, { message: '用户名不能包含空格' });
}
};
export const checkUsernameShort = (username: string) => {
if (username.length <= 3) {
throw new CustomError(400, '用户名不能过短');
throw new CustomError(400, { message: '用户名不能过短' });
}
};
@@ -31,13 +31,13 @@ export const toChangeName = async (opts: { id: string; newName: string; admin?:
}
const user = await User.findByPk(id);
if (!user) {
ctx.throw(404, 'User not found');
ctx.throw(404, { message: 'User not found' });
}
const oldName = user.username;
checkUsername(newName);
const findUserByUsername = await User.findOne({ username: newName });
if (findUserByUsername) {
ctx.throw(400, 'Username already exists');
ctx.throw(400, { message: 'Username already exists' });
}
user.username = newName;
const data = user.data || {};
@@ -65,7 +65,7 @@ export const toChangeName = async (opts: { id: string; newName: string; admin?:
}
} catch (error) {
console.error('迁移文件数据失败', error);
ctx.throw(500, 'Failed to change username');
ctx.throw(500, { message: 'Failed to change username' });
}
return user;
}
@@ -79,13 +79,13 @@ app
const { id, newName } = ctx.query.data || {};
try {
if (!id || !newName) {
ctx.throw(400, '参数错误');
ctx.throw(400, { message: '参数错误' });
}
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');
ctx.throw(500, { message: 'Failed to change username' });
}
})
.addTo(app);
@@ -99,7 +99,7 @@ app
.define(async (ctx) => {
const { username } = ctx.query.data || {};
if (!username) {
ctx.throw(400, 'Username is required');
ctx.throw(400, { message: 'Username is required' });
}
checkUsername(username);
const user = await User.findOne({ username });
@@ -121,7 +121,7 @@ app
const { id, password } = ctx.query.data || {};
const user = await User.findByPk(id);
if (!user) {
ctx.throw(404, 'User not found');
ctx.throw(404, { message: 'User not found' });
}
let pwd = password || nanoid(6);
user.createPassword(pwd);
@@ -149,7 +149,7 @@ app
checkUsername(username);
const findUserByUsername = await User.findOne({ username });
if (findUserByUsername) {
ctx.throw(400, 'Username already exists');
ctx.throw(400, { message: 'Username already exists' });
}
let pwd = password || nanoid(6);
const user = await User.createUser(username, pwd, description);
@@ -172,7 +172,7 @@ app
const { id } = ctx.query.data || {};
const user = await User.findByPk(id);
if (!user) {
ctx.throw(404, 'User not found');
ctx.throw(404, { message: 'User not found' });
}
await db.delete(schema.cfUser).where(eq(schema.cfUser.id, user.id));
backupUserA(user.username, user.id);