211 lines
5.6 KiB
TypeScript
211 lines
5.6 KiB
TypeScript
import './routes/index.ts';
|
||
import './aura/index.ts';
|
||
import { app } from './app.ts';
|
||
import type { App } from '@kevisual/router';
|
||
import { User } from './models/user.ts';
|
||
import { createCookie, getSomeInfoFromReq } from './routes/user/me.ts';
|
||
import { toJSONSchema } from '@kevisual/router';
|
||
import { pick } from 'es-toolkit';
|
||
/**
|
||
* 验证上下文中的 App ID 是否与指定的 App ID 匹配
|
||
* @param {any} ctx - 上下文对象,可能包含 appId 属性
|
||
* @param {string} appId - 需要验证的目标 App ID
|
||
* @returns {boolean} 如果 ctx 中包含 appId 且匹配则返回 true,否则返回 false
|
||
* @throws {Error} 如果 ctx 中包含 appId 但不匹配,则抛出 403 错误
|
||
*/
|
||
const checkAppId = (ctx: any, appId: string) => {
|
||
const _appId = ctx?.app?.appId;
|
||
if (_appId) {
|
||
if (_appId !== appId) {
|
||
ctx.throw(403, 'Invalid App ID');
|
||
}
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 添加auth中间件, 用于验证token
|
||
* 添加 id: auth 必须需要user成功
|
||
* 添加 id: auth-can 可以不需要user成功,有则赋值
|
||
*
|
||
* @param app
|
||
*/
|
||
export const addAuth = (app: App) => {
|
||
app
|
||
.route({
|
||
path: 'auth',
|
||
rid: 'auth',
|
||
description: '验证token,必须成功, 错误返回401,正确赋值到ctx.state.tokenUser',
|
||
})
|
||
.define(async (ctx) => {
|
||
const token = ctx.query.token;
|
||
// if (checkAppId(ctx, app.appId)) {
|
||
// ctx.state.tokenUser = {
|
||
// username: 'default',
|
||
// }
|
||
// return;
|
||
// }
|
||
// 已经有用户信息则直接返回,不需要重复验证
|
||
if (ctx.state.tokenUser) {
|
||
return;
|
||
}
|
||
if (!token) {
|
||
ctx.throw(401, 'Token is required');
|
||
}
|
||
const user = await User.getOauthUser(token);
|
||
if (!user) {
|
||
ctx.throw(401, 'Token is invalid');
|
||
return;
|
||
}
|
||
// console.log(`auth user: ${user.username} (${user.id})`);
|
||
const someInfo = getSomeInfoFromReq(ctx);
|
||
if (someInfo.isBrowser && !ctx.req?.cookies?.['token']) {
|
||
createCookie({ accessToken: token }, ctx);
|
||
}
|
||
ctx.state.tokenUser = user;
|
||
})
|
||
.addTo(app);
|
||
|
||
app
|
||
.route({
|
||
path: 'auth',
|
||
key: 'can',
|
||
rid: 'auth-can',
|
||
description: '验证token,可以不成功,错误不返回401,正确赋值到ctx.state.tokenUser,失败赋值null',
|
||
})
|
||
.define(async (ctx) => {
|
||
// if (checkAppId(ctx, app.appId)) {
|
||
// ctx.state.tokenUser = {
|
||
// username: 'default',
|
||
// }
|
||
// return;
|
||
// }
|
||
// 已经有用户信息则直接返回,不需要重复验证
|
||
if (ctx.state.tokenUser) {
|
||
return;
|
||
}
|
||
if (ctx.query?.token) {
|
||
const token = ctx.query.token;
|
||
const user = await User.getOauthUser(token);
|
||
if (token) {
|
||
ctx.state.tokenUser = user;
|
||
const someInfo = getSomeInfoFromReq(ctx);
|
||
if (someInfo.isBrowser && !ctx.req?.cookies?.['token']) {
|
||
createCookie({ accessToken: token }, ctx);
|
||
}
|
||
} else {
|
||
ctx.state.tokenUser = null;
|
||
}
|
||
}
|
||
})
|
||
.addTo(app);
|
||
};
|
||
addAuth(app);
|
||
|
||
app
|
||
.route({
|
||
path: 'auth',
|
||
key: 'admin',
|
||
rid: 'auth-admin',
|
||
isDebug: true,
|
||
middleware: ['auth'],
|
||
description: '验证token,必须是admin用户, 错误返回403,正确赋值到ctx.state.tokenAdmin',
|
||
})
|
||
.define(async (ctx) => {
|
||
// if (checkAppId(ctx, app.appId)) {
|
||
// ctx.state.tokenUser = {
|
||
// username: 'default',
|
||
// }
|
||
// return;
|
||
// }
|
||
const tokenUser = ctx.state.tokenUser;
|
||
if (!tokenUser) {
|
||
ctx.throw(401, 'No User For authorized');
|
||
}
|
||
console.log('auth-admin tokenUser', ctx.state);
|
||
if (typeof ctx.state.isAdmin !== 'undefined' && ctx.state.isAdmin === true) {
|
||
return;
|
||
}
|
||
try {
|
||
const user = await User.findOne({
|
||
id: tokenUser.id,
|
||
});
|
||
if (!user) {
|
||
ctx.throw(404, 'user not found');
|
||
}
|
||
user.setTokenUser(tokenUser);
|
||
const orgs = await user.getOrgs();
|
||
if (orgs.includes('admin')) {
|
||
ctx.body = 'admin';
|
||
} else {
|
||
ctx.throw(403, 'forbidden');
|
||
}
|
||
ctx.state.isAdmin = true;
|
||
} catch (e) {
|
||
console.error(`auth-admin error`, e);
|
||
console.error('tokenUser', tokenUser?.id, tokenUser?.username, tokenUser?.uid);
|
||
ctx.throw(500, e.message);
|
||
}
|
||
})
|
||
.addTo(app);
|
||
|
||
app
|
||
.route({
|
||
path: 'auth-check',
|
||
key: 'admin',
|
||
rid: 'check-auth-admin',
|
||
middleware: ['auth'],
|
||
})
|
||
.define(async (ctx) => {
|
||
const tokenUser = ctx.state.tokenUser;
|
||
if (!tokenUser) {
|
||
ctx.throw(401, 'No User For authorized');
|
||
}
|
||
if (typeof ctx.state.isAdmin !== 'undefined') {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const user = await User.findOne({
|
||
id: tokenUser.id,
|
||
});
|
||
if (!user) {
|
||
ctx.throw(404, 'user not found');
|
||
}
|
||
user.setTokenUser(tokenUser);
|
||
const orgs = await user.getOrgs();
|
||
if (orgs.includes('admin')) {
|
||
ctx.body = 'admin';
|
||
ctx.state.isAdmin = true;
|
||
ctx.state.tokenAdmin = {
|
||
id: user.id,
|
||
username: user.username,
|
||
orgs,
|
||
};
|
||
return;
|
||
} else {
|
||
ctx.state.isAdmin = false;
|
||
}
|
||
ctx.body = 'not admin';
|
||
} catch (e) {
|
||
console.error(`auth-admin error`, e);
|
||
console.error('tokenUser', tokenUser?.id, tokenUser?.username, tokenUser?.uid);
|
||
ctx.throw(500, e.message);
|
||
}
|
||
})
|
||
.addTo(app);
|
||
|
||
app.createRouteList({
|
||
middleware: ['auth-can']
|
||
})
|
||
|
||
app.route({
|
||
path: 'system',
|
||
key: 'version'
|
||
}).define(async (ctx) => {
|
||
ctx.body = {
|
||
version: '0.0.1',
|
||
name: 'KeVisual Backend System',
|
||
}
|
||
}).addTo(app); |