feat(auth): add authentication routes and user token handling

- Implemented user authentication routes in `auth.ts` for fetching current user info and admin verification.
- Added caching mechanism for user tokens to improve performance.
- Created middleware for admin authentication.

feat(opencode): create OpenCode client route

- Added `opencode-cnb` route for creating OpenCode clients with session management.
- Integrated OpenCode SDK for client operations and session handling.

refactor(client): encapsulate OpenCode client creation

- Created a utility function `getClient` in `client.ts` to initialize OpenCode clients.

test(opencode): add tests for OpenCode routes

- Implemented test cases for OpenCode routes in `list.ts` to validate functionality.
- Created common utilities for testing in `common.ts`.
This commit is contained in:
xiongxiao
2026-03-13 04:04:47 +08:00
committed by cnb
parent bd0ce0058e
commit 8d85e83418
11 changed files with 440 additions and 335 deletions

View File

@@ -1,4 +1,4 @@
import { app, assistantConfig } from '../app.ts';
import './config/index.ts';
import './client/index.ts';
import './shop-install/index.ts';
@@ -11,121 +11,8 @@ import './remote/index.ts';
// import './kevisual/index.ts'
import './cnb-board/index.ts';
import { authCache } from '@/module/cache/auth.ts';
import './auth.ts';
import { getTokenUserCache, checkAuth } from './auth.ts';
export { getTokenUserCache, checkAuth }
import { logger } from '@/module/logger.ts';
const getTokenUser = async (token: string) => {
const query = assistantConfig.query
const res = await query.post({
path: 'user',
key: 'me',
token: token,
});
return res;
}
export const getTokenUserCache = async (token: string) => {
const tokenUser = await authCache.get(token);
if (tokenUser) {
return {
code: 200,
data: tokenUser,
};
}
const res = await getTokenUser(token);
if (res.code === 200) {
authCache.set(token, res.data);
}
return res;
}
export const checkAuth = async (ctx: any, isAdmin = false) => {
const config = assistantConfig.getConfig();
const { auth = {} } = config;
const token = ctx.query.token;
logger.debug('checkAuth', ctx.query, { token });
if (!token) {
return {
code: 401,
message: '未登录',
}
}
// 鉴权代理
let tokenUser = await authCache.get(token);
if (!tokenUser) {
const tokenUserRes = await getTokenUser(token);
if (tokenUserRes.code !== 200) {
return {
code: tokenUserRes.code,
message: '验证失败' + tokenUserRes.message,
}
} else {
tokenUser = tokenUserRes.data;
}
authCache.set(token, tokenUser);
}
ctx.state = {
...ctx.state,
token,
tokenUser,
};
const { username } = tokenUser;
if (!auth.username) {
// 初始管理员账号
auth.username = username;
assistantConfig.setConfig({ auth });
}
if (isAdmin && auth.username) {
const admins = config.auth?.admin || [];
let isCheckAdmin = false;
const admin = auth.username;
if (admin === username) {
isCheckAdmin = true;
}
if (!isCheckAdmin && admins.length > 0 && admins.includes(username)) {
isCheckAdmin = true;
}
if (!isCheckAdmin) {
return {
code: 403,
message: '非管理员用户',
}
}
}
return {
code: 200,
data: { tokenUser, token }
}
};
app
.route({
path: 'auth',
id: 'auth',
description: '获取当前登录用户信息, 第一个登录的用户为管理员用户',
})
.define(async (ctx) => {
if (!ctx.query?.token && ctx.appId === app.appId) {
return;
}
const authResult = await checkAuth(ctx);
if (authResult.code !== 200) {
ctx.throw(authResult.code, authResult.message);
}
})
.addTo(app);
app
.route({
path: 'auth-admin',
id: 'auth-admin',
description: '管理员鉴权, 获取用户信息,并验证是否为管理员。',
})
.define(async (ctx) => {
// logger.debug('query', ctx.query);
if (!ctx.query?.token && ctx.appId === app.appId) {
return;
}
ctx.state.isAdmin = true;
const authResult = await checkAuth(ctx, true);
if (authResult.code !== 200) {
ctx.throw(authResult.code, authResult.message);
}
})
.addTo(app);