Files
memind/server/portal-account-feedback-routes.mjs
T

305 lines
7.5 KiB
JavaScript

export function attachPortalAccountFeedbackRoutes({
app,
jsonBody,
userAuthReady = Promise.resolve(),
getUserAuth = () => null,
getLegacyAuth = () => null,
getSubscriptionService = () => null,
getFeedbackService = () => null,
userToken,
legacySessionToken,
clearUserLoginCookies,
resolveSkillRuntimeForClient = async () => null,
resolveAgentCodeRunForClient = async () => null,
logger = console,
} = {}) {
if (
!app ||
typeof jsonBody !== 'function' ||
typeof userToken !== 'function' ||
typeof legacySessionToken !== 'function' ||
typeof clearUserLoginCookies !== 'function'
) {
throw new Error(
'attachPortalAccountFeedbackRoutes requires route dependencies',
);
}
app.get('/auth/me', async (req, res) => {
await userAuthReady;
const userAuth = getUserAuth();
if (!userAuth) {
return res.status(503).json({
message: '未启用用户系统',
});
}
const me = await userAuth.getMe(userToken(req));
if (!me) {
return res.status(401).json({
message: '未登录',
});
}
const subscriptionService =
getSubscriptionService();
const [
paths,
capabilityState,
subscription,
skillRuntime,
agentCodeRun,
] = await Promise.all([
userAuth.listPathGrants(me.id),
userAuth.resolveUserCapabilities(
await userAuth.getUserById(me.id),
),
subscriptionService
? subscriptionService.getActiveSubscription(
me.id,
)
: null,
resolveSkillRuntimeForClient(),
resolveAgentCodeRunForClient(me.id),
]);
return res.json({
user: {
...me,
subscription,
},
paths,
capabilities: capabilityState.capabilities,
grantedSkills:
capabilityState.grantedSkills ?? [],
unrestricted: capabilityState.unrestricted,
skillRuntime,
agentCodeRun,
});
});
app.post('/auth/logout', async (req, res) => {
await userAuthReady;
const userAuth = getUserAuth();
const legacyAuth = getLegacyAuth();
if (userAuth) {
await userAuth.revoke(userToken(req));
}
if (legacyAuth) {
legacyAuth.revoke(legacySessionToken(req));
}
if (userAuth || legacyAuth) {
clearUserLoginCookies(res, req);
}
res.status(204).end();
});
app.get('/auth/usage', async (req, res) => {
await userAuthReady;
const userAuth = getUserAuth();
if (!userAuth) {
return res.status(503).json({
message: '未启用用户系统',
});
}
const me = await userAuth.getMe(userToken(req));
if (!me) {
return res.status(401).json({
message: '未登录',
});
}
const records = await userAuth.listUsageRecords({
userId: me.id,
limit: 30,
});
res.json({ records });
});
app.post(
'/auth/feedback',
jsonBody,
async (req, res) => {
await userAuthReady;
const userAuth = getUserAuth();
const feedbackService = getFeedbackService();
if (!userAuth || !feedbackService) {
return res.status(503).json({
message: '反馈服务未启用',
});
}
const me = await userAuth.getMe(userToken(req));
if (!me) {
return res.status(401).json({
message: '未登录',
});
}
try {
const result = await feedbackService.submit(
me.id,
{
type: req.body?.type,
title: req.body?.title,
description: req.body?.description,
contact: req.body?.contact,
images: req.body?.images,
context: req.body?.context,
},
);
res.status(201).json({
feedback: result,
});
} catch (error) {
const code =
error &&
typeof error === 'object' &&
'code' in error
? String(error.code)
: '';
if (code === 'invalid_input') {
return res.status(400).json({
message:
error instanceof Error
? error.message
: '提交内容无效',
});
}
logger.warn(
'Submit feedback failed:',
error instanceof Error
? error.message
: error,
);
return res.status(500).json({
message: '反馈提交失败,请稍后重试',
});
}
},
);
app.get('/auth/feedback', async (req, res) => {
await userAuthReady;
const userAuth = getUserAuth();
const feedbackService = getFeedbackService();
if (!userAuth || !feedbackService) {
return res.status(503).json({
message: '反馈服务未启用',
});
}
const me = await userAuth.getMe(userToken(req));
if (!me) {
return res.status(401).json({
message: '未登录',
});
}
const limit = Math.min(
Math.max(Number(req.query?.limit) || 20, 1),
50,
);
const items = await feedbackService.listForUser(
me.id,
{ limit },
);
res.json({ items });
});
app.get(
'/auth/feedback/board',
async (req, res) => {
await userAuthReady;
const userAuth = getUserAuth();
const feedbackService = getFeedbackService();
if (!userAuth || !feedbackService) {
return res.status(503).json({
message: '反馈服务未启用',
});
}
const me = await userAuth.getMe(userToken(req));
if (!me) {
return res.status(401).json({
message: '未登录',
});
}
const page = Math.max(
Number(req.query?.page) || 1,
1,
);
const limit = Math.min(
Math.max(
Number(req.query?.limit) || 10,
1,
),
10,
);
try {
const result =
await feedbackService.listAll({
page,
limit,
});
res.json(result);
} catch (error) {
logger.warn(
'List feedback board failed:',
error instanceof Error
? error.message
: error,
);
return res.status(500).json({
message: '反馈列表加载失败',
});
}
},
);
app.get(
'/auth/feedback/:feedbackId',
async (req, res) => {
await userAuthReady;
const userAuth = getUserAuth();
const feedbackService = getFeedbackService();
if (!userAuth || !feedbackService) {
return res.status(503).json({
message: '反馈服务未启用',
});
}
const me = await userAuth.getMe(userToken(req));
if (!me) {
return res.status(401).json({
message: '未登录',
});
}
try {
const item = await feedbackService.getById(
req.params.feedbackId,
me.id,
);
res.json({
item,
isMine: item.userId === me.id,
});
} catch (error) {
const code =
error &&
typeof error === 'object' &&
'code' in error
? String(error.code)
: '';
if (code === 'feedback_not_found') {
return res.status(404).json({
message:
error instanceof Error
? error.message
: '反馈不存在',
});
}
logger.warn(
'Get feedback detail failed:',
error instanceof Error
? error.message
: error,
);
return res.status(500).json({
message: '反馈详情加载失败',
});
}
},
);
}