feat(P25-1/P25-5/P26-1/P26-3/P24-1/P29-4): 落地内部OA够用版后端与试运行能力

关联 Issue: P25-1, P25-2, P25-3, P25-4, P25-5, P26-1, P26-3, P24-1, P24-2, P29-1, P29-2, P29-3, P29-4
这个提交包含在:
2026-03-21 18:03:20 +08:00
父节点 9b7cd818f7
当前提交 e39e41287e
共修改 45 个文件,包含 4571 行新增和 328 行删除
+54
查看文件
@@ -497,6 +497,60 @@ async function authRoutes(fastify) {
return sendData(reply, request, { accessToken });
});
fastify.get("/sso/providers", async (request, reply) => {
if (!fastify.config.oidcEnabled) {
return reply.send({ requestId: request.id, data: [] });
}
return reply.send({
requestId: request.id,
data: [
{
id: "oidc",
label: process.env.OIDC_PROVIDER_LABEL || "企业统一登录",
enabled: true,
loginUrl: "/api/auth/oidc/login",
},
],
});
});
fastify.get("/deeplink", async (request, reply) => {
const token = String(request.query?.token ?? "").trim();
const verified = verifyToken(token, fastify.config.jwtSecret);
if (!verified.ok) {
throw new AppError({
code: "AUTH_001",
message: "无效深链",
statusCode: 401,
details: { token: verified.reason },
});
}
const payload = verified.payload && typeof verified.payload === "object" ? verified.payload : {};
const targetPath = typeof payload.targetPath === "string" ? payload.targetPath.trim() : "";
const exp = Number(payload.exp ?? 0);
if (!targetPath || !targetPath.startsWith("/")) {
throw new AppError({
code: "VALID_001",
message: "参数校验失败",
statusCode: 400,
details: { targetPath: "invalid_target_path" },
});
}
if (!Number.isFinite(exp) || Date.now() / 1000 > exp) {
throw new AppError({
code: "AUTH_001",
message: "深链已过期",
statusCode: 401,
details: { token: "expired" },
});
}
const origin = String(fastify.config.appOrigin || "").split(",")[0].trim().replace(/\/+$/, "");
return reply.redirect(origin ? `${origin}${targetPath}` : targetPath);
});
fastify.get("/oidc/login", async (request, reply) => {
if (!fastify.config.oidcEnabled) {
throw new AppError({