diff --git a/assistant/package.json b/assistant/package.json index d7c9015..cd7150d 100644 --- a/assistant/package.json +++ b/assistant/package.json @@ -20,7 +20,8 @@ ], "scripts": { "dev": "bun run src/run.ts ", - "dev:server": "ASSISTANT_CONFIG_DIR=/workspace bun --watch src/run-server.ts ", + "dev:server": "bun --watch src/run-server.ts ", + "dev:cnb": "ASSISTANT_CONFIG_DIR=/workspace bun --watch src/run-server.ts ", "dev:share": "bun --watch src/test/remote-app.ts ", "build:lib": "bun run bun-lib.config.mjs", "postbuild:lib": "dts -i src/lib.ts -o assistant-lib.d.ts -d libs -t", diff --git a/assistant/src/module/assistant/config/index.ts b/assistant/src/module/assistant/config/index.ts index 8522753..2750626 100644 --- a/assistant/src/module/assistant/config/index.ts +++ b/assistant/src/module/assistant/config/index.ts @@ -70,7 +70,7 @@ export type ReturnInitConfigType = ReturnType; type AuthPermission = { type?: 'auth-proxy' | 'public' | 'private' | 'project'; username?: string; // 用户名 - admin?: Omit; + admin?: string[]; }; export type AssistantConfigData = { pageApi?: string; // https://kevisual.cn diff --git a/assistant/src/routes/index.ts b/assistant/src/routes/index.ts index 3da549f..b321698 100644 --- a/assistant/src/routes/index.ts +++ b/assistant/src/routes/index.ts @@ -14,7 +14,7 @@ export const getTokenUser = async (ctx: any) => { const res = await query.post({ path: 'user', key: 'me', - token: ctx.state.token, + token: ctx.state.token || ctx.query.token, }); if (res.code !== 200) { return ctx.throw(401, 'not login'); @@ -26,7 +26,7 @@ const checkAuth = async (ctx: any, isAdmin = false) => { const config = assistantConfig.getConfig(); const { auth = {} } = config; const token = ctx.query.token; - + console.log('checkAuth', ctx.query, { token }); if (!token) { return ctx.throw(401, 'not login'); } @@ -47,8 +47,17 @@ const checkAuth = async (ctx: any, isAdmin = false) => { auth.username = username; assistantConfig.setConfig({ auth }); } - if (isAdmin) { - if (auth.username && auth.username !== username) { + 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 ctx.throw(403, 'not admin user'); } } @@ -70,6 +79,7 @@ app description: '管理员鉴权, 获取用户信息,并验证是否为管理员。', }) .define(async (ctx) => { + console.log('query', ctx.query); await checkAuth(ctx, true); }) .addTo(app); diff --git a/assistant/src/routes/user/index.ts b/assistant/src/routes/user/index.ts index a2de261..150ac1f 100644 --- a/assistant/src/routes/user/index.ts +++ b/assistant/src/routes/user/index.ts @@ -6,29 +6,64 @@ app.route({ description: '管理员用户登录', }).define(async (ctx) => { const { username, password } = ctx.query; - const query = assistantConfig.query; const auth = assistantConfig.getConfig().auth || {}; - const res = await query.post({ - path: 'user', - key: 'login', - data: { - username, - password, - }, - }) - if (res.code !== 200) { - return ctx.throw(401, 'login failed'); - } - const loginUser = res.data.username; - if (auth.username && loginUser !== auth.username) { + if (auth && auth.username && auth.username !== username) { return ctx.throw(403, 'login user is not admin user'); } - if (!auth.username) { - // 初始管理员账号 - auth.username = loginUser; - assistantConfig.setConfig({ auth }); - } - // 保存配置 + // 发起请求,转发客户端 cookie + const res = await fetch(`${assistantConfig.baseURL}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + path: 'user', + key: 'login', + username, + password, + }), + }); - ctx.body = res.data; + // 转发上游服务器返回的所有 set-cookie(支持多个 cookie) + const setCookieHeaders = res.headers.getSetCookie?.() || []; + if (setCookieHeaders.length > 0) { + // 设置多个 cookie 到原生 http.ServerResponse + ctx.res.setHeader('Set-Cookie', setCookieHeaders); + } else { + // 兼容旧版本,使用 get 方法 + const setCookieHeader = res.headers.get('set-cookie'); + if (setCookieHeader) { + ctx.res.setHeader('Set-Cookie', setCookieHeader); + } + } + + const responseData = await res.json(); + console.debug('admin login response', { res: responseData }); + if (responseData.code !== 200) { + console.debug('admin login failed', { res: responseData }); + return ctx.throw(401, 'login failed'); + } + const me = await assistantConfig.query.post({ + path: 'user', + key: 'me', + token: responseData.data.token, + }) + if (me.code === 200) { + const loginUser = me.data.username; + if (auth.username && loginUser !== auth.username) { + return ctx.throw(403, 'login user is not admin user'); + } + if (!auth.username) { + // 初始管理员账号 + auth.username = loginUser; + if (!auth.type) { + auth.type = 'public'; + } + assistantConfig.setConfig({ auth }); + console.log('set first admin user', { username: loginUser }); + } + // 保存配置 + } + + ctx.body = responseData.data; }).addTo(app); diff --git a/assistant/src/services/init/index.ts b/assistant/src/services/init/index.ts index f829562..71e4bf8 100644 --- a/assistant/src/services/init/index.ts +++ b/assistant/src/services/init/index.ts @@ -50,6 +50,9 @@ export class AssistantInit extends AssistantConfig { } return this.#query; } + get baseURL() { + return `${this.getConfig()?.pageApi || 'https://kevisual.cn'}/api/router`; + } setQuery(query?: Query) { this.#query = query || new Query({ url: `${this.getConfig()?.pageApi || 'https://kevisual.cn'}/api/router`, diff --git a/assistant/src/services/proxy/proxy-page-index.ts b/assistant/src/services/proxy/proxy-page-index.ts index 0ce99ef..e5992f2 100644 --- a/assistant/src/services/proxy/proxy-page-index.ts +++ b/assistant/src/services/proxy/proxy-page-index.ts @@ -8,15 +8,21 @@ localProxy.initFromAssistantConfig(assistantConfig); export const proxyRoute = async (req: http.IncomingMessage, res: http.ServerResponse) => { const _assistantConfig = assistantConfig.getCacheAssistantConfig(); - const auth = _assistantConfig?.auth; - if (!auth.username) { + const home = _assistantConfig?.home || '/root/home'; + const auth = _assistantConfig?.auth || {}; + let noAdmin = !auth.username; + const toSetting = () => { res.writeHead(302, { Location: `/root/cli/setting/` }); - return res.end(); + res.end(); + return true; } const url = new URL(req.url, 'http://localhost'); const pathname = decodeURIComponent(url.pathname); - if (pathname === '/' && _assistantConfig?.home) { - res.writeHead(302, { Location: `${_assistantConfig?.home}/` }); + if (pathname === '/') { + if (noAdmin) { + return toSetting(); + } + res.writeHead(302, { Location: `${home}/` }); return res.end(); } if (pathname.startsWith('/favicon.ico')) { @@ -25,8 +31,7 @@ export const proxyRoute = async (req: http.IncomingMessage, res: http.ServerResp return; } if (pathname.startsWith('/client')) { - logger.info('url', { url: req.url }); - console.debug('handle by router'); + logger.debug('handle by router', { url: req.url }); return; } // client, api, v1, serve 开头的拦截 @@ -47,6 +52,9 @@ export const proxyRoute = async (req: http.IncomingMessage, res: http.ServerResp res.end('Not Found Proxy'); return; } + if (noAdmin) { + return toSetting(); + } if (_app && urls.length === 3) { // 重定向到 res.writeHead(302, { Location: `${req.url}/` });