Move admin auth local
This commit is contained in:
+114
-14
@@ -1,8 +1,25 @@
|
||||
import express from 'express';
|
||||
import { listUsagePaged, listLedgerPaged } from './pagination.mjs';
|
||||
|
||||
function asyncHandler(handler) {
|
||||
return (req, res, next) => Promise.resolve(handler(req, res, next)).catch(next);
|
||||
}
|
||||
|
||||
function wrapRouterAsync(router) {
|
||||
for (const method of ['get', 'post', 'put', 'patch', 'delete']) {
|
||||
const original = router[method].bind(router);
|
||||
router[method] = (path, ...handlers) =>
|
||||
original(
|
||||
path,
|
||||
...handlers.map((handler) =>
|
||||
typeof handler === 'function' && handler.length < 4 ? asyncHandler(handler) : handler,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function createAdminApp(services) {
|
||||
const { userAuth, llmProviderService, pool, ready } = services;
|
||||
const { userAuth, llmProviderService, pool, ready, wechatAdmin } = services;
|
||||
const app = express();
|
||||
app.set('trust proxy', 1);
|
||||
|
||||
@@ -12,11 +29,13 @@ export function createAdminApp(services) {
|
||||
|
||||
let cookieHelpers = null;
|
||||
const cookieReady = ready.then(async () => {
|
||||
const [userAuthMod, authMod] = await Promise.all([
|
||||
services.importUserAuthModule(),
|
||||
services.importAuthModule(),
|
||||
]);
|
||||
cookieHelpers = { ...userAuthMod, parseCookies: authMod.parseCookies };
|
||||
cookieHelpers = {
|
||||
parseCookies: services.parseCookies,
|
||||
USER_COOKIE: services.USER_COOKIE,
|
||||
userLoginCookies: services.userLoginCookies,
|
||||
clearUserSessionCookie: services.clearUserSessionCookie,
|
||||
resolveCookieDomainForRequest: services.resolveCookieDomainForRequest,
|
||||
};
|
||||
});
|
||||
|
||||
function userToken(req) {
|
||||
@@ -44,15 +63,19 @@ export function createAdminApp(services) {
|
||||
res.json({ ok: true, service: 'memind_adm' });
|
||||
});
|
||||
|
||||
app.get('/auth/status', async (req, res) => {
|
||||
app.get('/healthz', (_req, res) => {
|
||||
res.json({ ok: true, service: 'memind_adm' });
|
||||
});
|
||||
|
||||
app.get('/auth/status', asyncHandler(async (req, res) => {
|
||||
await cookieReady;
|
||||
await ready;
|
||||
const me = await userAuth.getMe(userToken(req));
|
||||
if (!me) return res.json({ authenticated: false, mode: 'user' });
|
||||
return res.json({ authenticated: true, user: me, mode: 'user' });
|
||||
});
|
||||
}));
|
||||
|
||||
app.post('/auth/login', jsonBody, async (req, res) => {
|
||||
app.post('/auth/login', jsonBody, asyncHandler(async (req, res) => {
|
||||
await cookieReady;
|
||||
await ready;
|
||||
const { username, password } = req.body ?? {};
|
||||
@@ -69,27 +92,28 @@ export function createAdminApp(services) {
|
||||
}
|
||||
setUserLoginCookies(res, req, result.token);
|
||||
return res.json({ authenticated: true, user: result.user, mode: 'user' });
|
||||
});
|
||||
}));
|
||||
|
||||
app.post('/auth/logout', async (req, res) => {
|
||||
app.post('/auth/logout', asyncHandler(async (req, res) => {
|
||||
await cookieReady;
|
||||
await ready;
|
||||
await userAuth.revoke(userToken(req));
|
||||
clearUserLoginCookies(res, req);
|
||||
res.status(204).end();
|
||||
});
|
||||
}));
|
||||
|
||||
const adminApi = express.Router();
|
||||
wrapRouterAsync(adminApi);
|
||||
adminApi.use(jsonBody);
|
||||
|
||||
adminApi.use(async (req, res, next) => {
|
||||
adminApi.use(asyncHandler(async (req, res, next) => {
|
||||
await cookieReady;
|
||||
await ready;
|
||||
const me = await userAuth.getMe(userToken(req));
|
||||
if (!me) return res.status(401).json({ message: '未登录' });
|
||||
req.currentUser = me;
|
||||
next();
|
||||
});
|
||||
}));
|
||||
|
||||
const requireAdmin = (req, res, next) => {
|
||||
if (!req.currentUser || req.currentUser.role !== 'admin') {
|
||||
@@ -99,6 +123,14 @@ export function createAdminApp(services) {
|
||||
next();
|
||||
};
|
||||
|
||||
function resolveSessionUser(req, res, next) {
|
||||
if (!req.currentUser) {
|
||||
res.status(401).json({ message: '未授权,请重新登录' });
|
||||
return;
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
adminApi.get('/users', requireAdmin, async (req, res) => {
|
||||
const result = await userAuth.listUsers({
|
||||
page: Number(req.query.page) || 1,
|
||||
@@ -161,6 +193,52 @@ export function createAdminApp(services) {
|
||||
res.json(result);
|
||||
});
|
||||
|
||||
adminApi.get('/wechat/summary', requireAdmin, async (_req, res) => {
|
||||
if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
|
||||
res.json(await wechatAdmin.getSummary());
|
||||
});
|
||||
|
||||
adminApi.get('/wechat/bindings', requireAdmin, async (req, res) => {
|
||||
if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
|
||||
res.json(await wechatAdmin.listBindings(req.query));
|
||||
});
|
||||
|
||||
adminApi.get('/wechat/messages', requireAdmin, async (req, res) => {
|
||||
if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
|
||||
res.json(await wechatAdmin.listMessages(req.query));
|
||||
});
|
||||
|
||||
adminApi.get('/wechat/digests', requireAdmin, async (req, res) => {
|
||||
if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
|
||||
res.json(await wechatAdmin.listDigests(req.query));
|
||||
});
|
||||
|
||||
adminApi.get('/wechat/deliveries', requireAdmin, async (req, res) => {
|
||||
if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
|
||||
res.json(await wechatAdmin.listDeliveries(req.query));
|
||||
});
|
||||
|
||||
adminApi.post('/wechat/users/:userId/route/clear', requireAdmin, async (req, res) => {
|
||||
if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
|
||||
const result = await wechatAdmin.clearRouteForUser(req.params.userId);
|
||||
if (!result.ok) return res.status(404).json({ message: result.message ?? '清除失败' });
|
||||
res.json(result);
|
||||
});
|
||||
|
||||
adminApi.post('/wechat/digests/:id/cancel', requireAdmin, async (req, res) => {
|
||||
if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
|
||||
const result = await wechatAdmin.cancelDigest(req.params.id);
|
||||
if (!result.ok) return res.status(404).json({ message: '订阅不存在或已暂停' });
|
||||
res.json(result);
|
||||
});
|
||||
|
||||
adminApi.post('/wechat/digests/:id/resume', requireAdmin, async (req, res) => {
|
||||
if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
|
||||
const result = await wechatAdmin.resumeDigest(req.params.id);
|
||||
if (!result.ok) return res.status(404).json({ message: result.message ?? '恢复失败' });
|
||||
res.json(result);
|
||||
});
|
||||
|
||||
adminApi.get('/capabilities/catalog', requireAdmin, (_req, res) => {
|
||||
res.json({ catalog: userAuth.capabilityCatalog });
|
||||
});
|
||||
@@ -363,5 +441,27 @@ export function createAdminApp(services) {
|
||||
|
||||
app.use('/admin-api', adminApi);
|
||||
|
||||
if (services.createOpsApi) {
|
||||
const opsApi = express.Router();
|
||||
wrapRouterAsync(opsApi);
|
||||
opsApi.use(asyncHandler(async (req, res, next) => {
|
||||
await cookieReady;
|
||||
await ready;
|
||||
const me = await userAuth.getMe(userToken(req));
|
||||
if (!me) return res.status(401).json({ message: '未授权,请重新登录' });
|
||||
req.currentUser = me;
|
||||
next();
|
||||
}));
|
||||
opsApi.use(resolveSessionUser);
|
||||
opsApi.use('/ops/v1', services.createOpsApi({ jsonBody, plazaOps: services.plazaOps }));
|
||||
app.use('/api', opsApi);
|
||||
}
|
||||
|
||||
app.use((err, _req, res, _next) => {
|
||||
console.error('Admin API request failed:', err);
|
||||
if (res.headersSent) return;
|
||||
res.status(500).json({ message: err instanceof Error ? err.message : '管理后台请求失败' });
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user