Files
koc-loop/app/api/wecom-oauth/route.ts
ABAPPLO bd80b2d7d8 feat: 项目负责人+内部任务群+portal 内部身份领取+群主链接归属
- 迁移 0012:projects.owner_userid / tasks.task_chat_id / claims.owner_partner_id
- lib/task-group-service.ts:懒建 appchat 任务群(首个内部群主领取时创建,成员 diff 补员),播报/卡片文案
- wecom-client 新增 appchat create/update/get/send 封装
- OAuth ticket 增可选 uid(v:1 兼容),内部成员领取按 wecom_userid 解析群主身份
- 外部 OAuth 领取人 upsert 打 partner_type='group_owner'
- portal 链接带 ?owner= 穿透 OAuth 往返,claims 记录归属群主
- 后台任务卡「群主链接」面板:复制链接/推送内部群主/任务群状态
- 催办与超期升级同步播报进任务群
2026-09-03 11:30:26 +08:00

76 lines
2.4 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { getRuntimeEnv } from "../../../lib/runtime-env";
import {
exchangeWecomOAuthCode,
fetchWecomExternalContactName,
resolveWecomConfig,
WecomClientError,
type WecomBindings,
} from "../../../lib/wecom-client";
import { signWecomIdentityTicket } from "../../../lib/wecom-oauth-service";
import { partnerOptions, withPartnerCors } from "../../../lib/partner-cors";
const env = getRuntimeEnv();
export function OPTIONS(request: Request) {
return partnerOptions(request);
}
async function handlePost(request: Request) {
try {
const body = (await request.json().catch(() => ({}))) as { code?: string };
const code = String(body.code ?? "").trim();
const config = resolveWecomConfig(env as unknown as WecomBindings);
if (!config.corpId || !config.agentId || !config.secret) {
return Response.json(
{ error: "未配置企业微信授权(WECOM_CORP_ID / WECOM_AGENT_ID / WECOM_SECRET)" },
{ status: 501 },
);
}
if (!code) {
return Response.json({ error: "缺少授权 code" }, { status: 400 });
}
const identity = await exchangeWecomOAuthCode(config, code);
if (identity.externalUserId) {
const displayName =
(await fetchWecomExternalContactName(config, identity.externalUserId)) ??
"企微客户";
const { ticket, expiresIn } = await signWecomIdentityTicket(
identity.externalUserId,
displayName,
);
return Response.json({
userType: "external",
identityTicket: ticket,
displayName,
expiresIn,
});
}
const userId = identity.userId ?? "";
if (!userId) {
return Response.json({ error: "企业微信未返回有效身份" }, { status: 400 });
}
const { ticket, expiresIn } = await signWecomIdentityTicket("", "", {
userId,
});
return Response.json({
userType: "internal",
userId,
identityTicket: ticket,
displayName: userId,
expiresIn,
});
} catch (error) {
const status = error instanceof WecomClientError ? error.status : 500;
return Response.json(
{ error: error instanceof Error ? error.message : "授权失败" },
{ status },
);
}
}
export async function POST(request: Request) {
const response = await handlePost(request);
response.headers.set("Cache-Control", "private, no-store");
return withPartnerCors(request, response);
}