import { eq } from "drizzle-orm"; import { jsonFailure, jsonSuccess, readJsonBody } from "@/modules/core/server/api"; import { recordAuditLog } from "@/modules/core/server/audit"; import { requirePermission } from "@/modules/core/server/auth"; import { getDatabase } from "@/modules/core/server/db"; import { getRoleDefinitions, hasPermission } from "@/modules/core/server/permissions"; import { rolePermissions, roles } from "@/modules/core/server/schema"; import { PERMISSIONS } from "@/modules/core/types"; import type { Permission } from "@/modules/core/types"; type RouteContext = { params: Promise<{ id: string; }>; }; const PERMISSION_SET = new Set(PERMISSIONS); function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } function readString(source: Record, key: string): string { const value = source[key]; return typeof value === "string" ? value.trim() : ""; } function readBoolean(source: Record, key: string): boolean | null { const value = source[key]; return typeof value === "boolean" ? value : null; } function readPermissionArray(source: Record): Permission[] | null { const value = source.permissions; if (!Array.isArray(value)) { return null; } const permissions = value.filter((item): item is string => typeof item === "string"); if (permissions.length !== value.length || permissions.some((permission) => !PERMISSION_SET.has(permission))) { return null; } return permissions as Permission[]; } function createRoleKey(label: string): string { return label.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, ""); } async function getEditableRole(id: string, organizationId: string | undefined, canManagePlatform: boolean) { const database = getDatabase(); const rows = await database.select().from(roles).where(eq(roles.id, id)).limit(1); const role = rows[0]; if (!role) { return { success: false as const, response: jsonFailure("角色不存在", 404) }; } if (role.isSystem) { return { success: false as const, response: jsonFailure("内置角色不可编辑", 400) }; } if (!canManagePlatform && role.organizationId !== organizationId) { return { success: false as const, response: jsonFailure("角色不存在", 404) }; } return { success: true as const, role }; } export async function PATCH(request: Request, context: RouteContext): Promise { const { id } = await context.params; const auth = await requirePermission("role:manage", { action: "role.update", targetType: "role", targetId: id, }); if (!auth.success) { return auth.response; } const body = await readJsonBody(request); if (!isRecord(body)) { return jsonFailure("请求数据格式无效"); } const organizationId = auth.context.organization?.id; const editable = await getEditableRole(id, organizationId, hasPermission(auth.context.permissions, "platform:manage")); if (!editable.success) { return editable.response; } const label = readString(body, "label"); const key = readString(body, "key") || (label ? createRoleKey(label) : ""); const description = readString(body, "description"); const isEnabled = "isEnabled" in body ? readBoolean(body, "isEnabled") : null; if ("isEnabled" in body && isEnabled === null) { return jsonFailure("角色状态无效"); } const permissions = "permissions" in body ? readPermissionArray(body) : null; if ("permissions" in body && permissions === null) { return jsonFailure("权限列表无效"); } if (!label && !key && !description && isEnabled === null && permissions === null) { return jsonFailure("请至少填写一个要更新的字段"); } const updateValues: { label?: string; key?: string; description?: string; isEnabled?: boolean; updatedAt: Date; } = { updatedAt: new Date(), }; if (label) { updateValues.label = label; } if (key) { updateValues.key = key; } if (description) { updateValues.description = description; } if (isEnabled !== null) { updateValues.isEnabled = isEnabled; } const database = getDatabase(); try { await database.transaction(async (transaction) => { await transaction.update(roles).set(updateValues).where(eq(roles.id, id)); if (permissions) { await transaction.delete(rolePermissions).where(eq(rolePermissions.roleId, id)); if (permissions.length > 0) { await transaction.insert(rolePermissions).values( permissions.map((permissionId) => ({ roleId: id, permissionId, })), ); } } }); } catch (error) { const message = error instanceof Error ? error.message : ""; if (message.includes("roles_key_organization_unique")) { return jsonFailure("角色标识已存在", 409); } return jsonFailure("角色更新失败", 500); } await recordAuditLog({ actor: auth.context.account, organizationId: editable.role.organizationId ?? organizationId, action: "role.update", targetType: "role", targetId: id, result: "success", reason: `更新角色:${label || editable.role.label}`, }); const roleRows = await getRoleDefinitions(editable.role.organizationId ?? organizationId); const role = roleRows.find((item) => item.id === id); if (!role) { return jsonFailure("角色更新后读取失败", 500); } return jsonSuccess("角色已更新", { role }); } export async function DELETE(_request: Request, context: RouteContext): Promise { const { id } = await context.params; const auth = await requirePermission("role:manage", { action: "role.disable", targetType: "role", targetId: id, }); if (!auth.success) { return auth.response; } const organizationId = auth.context.organization?.id; const editable = await getEditableRole(id, organizationId, hasPermission(auth.context.permissions, "platform:manage")); if (!editable.success) { return editable.response; } const database = getDatabase(); const rows = await database .update(roles) .set({ isEnabled: false, updatedAt: new Date() }) .where(eq(roles.id, id)) .returning(); const role = rows[0]; if (!role) { return jsonFailure("角色不存在", 404); } await recordAuditLog({ actor: auth.context.account, organizationId: role.organizationId ?? organizationId, action: "role.disable", targetType: "role", targetId: role.id, result: "success", reason: `停用角色:${role.label}`, }); return jsonSuccess("角色已停用", { role }); }