var __defProp = Object.defineProperty; var __getOwnPropNames = Object.getOwnPropertyNames; var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; // server/_core/vite.ts var vite_exports = {}; __export(vite_exports, { serveStatic: () => serveStatic, setupVite: () => setupVite }); import express from "express"; import fs from "fs"; import { nanoid } from "nanoid"; import path from "path"; import { fileURLToPath } from "url"; import { createServer as createViteServer } from "vite"; async function setupVite(app, server) { const __dirname = path.dirname(fileURLToPath(import.meta.url)); const serverOptions = { middlewareMode: true, hmr: { server }, allowedHosts: true }; const vite = await createViteServer({ configFile: path.resolve(__dirname, "../../vite.config.ts"), server: serverOptions, appType: "custom" }); app.use(vite.middlewares); app.use("*", async (req, res, next) => { const url = req.originalUrl; try { const clientTemplate = path.resolve( __dirname, "../..", "client", "index.html" ); let template = await fs.promises.readFile(clientTemplate, "utf-8"); template = template.replace( `src="/src/main.tsx"`, `src="/src/main.tsx?v=${nanoid()}"` ); const page = await vite.transformIndexHtml(url, template); res.status(200).set({ "Content-Type": "text/html" }).end(page); } catch (e) { vite.ssrFixStacktrace(e); next(e); } }); } function serveStatic(app) { const __dirname = path.dirname(fileURLToPath(import.meta.url)); const distPath = process.env.NODE_ENV === "development" ? path.resolve(__dirname, "../..", "dist", "public") : path.resolve(__dirname, "public"); if (!fs.existsSync(distPath)) { console.error( `Could not find the build directory: ${distPath}, make sure to build the client first` ); } app.use(express.static(distPath)); app.use("*", (_req, res) => { res.sendFile(path.resolve(distPath, "index.html")); }); } var init_vite = __esm({ "server/_core/vite.ts"() { "use strict"; } }); // server/_core/static.ts var static_exports = {}; __export(static_exports, { serveStatic: () => serveStatic2 }); import express2 from "express"; import fs2 from "fs"; import path2 from "path"; import { fileURLToPath as fileURLToPath2 } from "url"; function serveStatic2(app) { const __filename = fileURLToPath2(import.meta.url); const __dirname = path2.dirname(__filename); const distPath = path2.resolve(__dirname, "public"); if (!fs2.existsSync(distPath)) { console.error( `[Static] Could not find build directory: ${distPath}` ); console.error(`[Static] Make sure to run 'pnpm build' first`); app.use("*", (_req, res) => { res.status(503).send("Service starting up, please wait..."); }); return; } console.log(`[Static] Serving files from: ${distPath}`); app.use(express2.static(distPath)); app.use("*", (_req, res) => { res.sendFile(path2.resolve(distPath, "index.html")); }); } var init_static = __esm({ "server/_core/static.ts"() { "use strict"; } }); // server/_core/index.ts import "dotenv/config"; import express3 from "express"; import cookieParser from "cookie-parser"; import { createServer } from "http"; import net from "net"; import { createExpressMiddleware } from "@trpc/server/adapters/express"; // server/routers.ts import { z as z2 } from "zod"; import { TRPCError as TRPCError3 } from "@trpc/server"; // shared/const.ts var ONE_YEAR_MS = 1e3 * 60 * 60 * 24 * 365; var UNAUTHED_ERR_MSG = "Please login (10001)"; var NOT_ADMIN_ERR_MSG = "You do not have required permission (10002)"; // server/_core/trpc.ts import { initTRPC, TRPCError } from "@trpc/server"; import superjson from "superjson"; var t = initTRPC.context().create({ transformer: superjson }); var router = t.router; var publicProcedure = t.procedure; var requireUser = t.middleware(async (opts) => { const { ctx, next } = opts; if (!ctx.user) { throw new TRPCError({ code: "UNAUTHORIZED", message: UNAUTHED_ERR_MSG }); } return next({ ctx: { ...ctx, user: ctx.user } }); }); var protectedProcedure = t.procedure.use(requireUser); var adminProcedure = t.procedure.use( t.middleware(async (opts) => { const { ctx, next } = opts; if (!ctx.user || ctx.user.role !== "admin") { throw new TRPCError({ code: "FORBIDDEN", message: NOT_ADMIN_ERR_MSG }); } return next({ ctx: { ...ctx, user: ctx.user } }); }) ); // server/_core/systemRouter.ts import { z } from "zod"; // server/_core/notification.ts import { TRPCError as TRPCError2 } from "@trpc/server"; var TITLE_MAX_LENGTH = 1200; var CONTENT_MAX_LENGTH = 2e4; var trimValue = (value) => value.trim(); var isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0; var validatePayload = (input) => { if (!isNonEmptyString(input.title)) { throw new TRPCError2({ code: "BAD_REQUEST", message: "Notification title is required." }); } if (!isNonEmptyString(input.content)) { throw new TRPCError2({ code: "BAD_REQUEST", message: "Notification content is required." }); } const title = trimValue(input.title); const content = trimValue(input.content); if (title.length > TITLE_MAX_LENGTH) { throw new TRPCError2({ code: "BAD_REQUEST", message: `Notification title must be at most ${TITLE_MAX_LENGTH} characters.` }); } if (content.length > CONTENT_MAX_LENGTH) { throw new TRPCError2({ code: "BAD_REQUEST", message: `Notification content must be at most ${CONTENT_MAX_LENGTH} characters.` }); } return { title, content }; }; async function notifyOwner(payload) { const { title, content } = validatePayload(payload); const webhookUrl = process.env.NAC_NOTIFY_WEBHOOK_URL; if (webhookUrl) { try { const response = await fetch(webhookUrl, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ title, content, timestamp: (/* @__PURE__ */ new Date()).toISOString() }) }); if (response.ok) { console.log(`[Notification] \u901A\u77E5\u5DF2\u53D1\u9001: ${title}`); return true; } console.warn(`[Notification] Webhook\u53D1\u9001\u5931\u8D25 (${response.status}): ${title}`); } catch (error) { console.warn("[Notification] Webhook\u8C03\u7528\u5931\u8D25:", error); } } console.log(`[Notification] \u7CFB\u7EDF\u901A\u77E5 - ${title}: ${content.slice(0, 200)}`); return true; } // server/_core/systemRouter.ts var systemRouter = router({ health: publicProcedure.input( z.object({ timestamp: z.number().min(0, "timestamp cannot be negative") }) ).query(() => ({ ok: true })), notifyOwner: adminProcedure.input( z.object({ title: z.string().min(1, "title is required"), content: z.string().min(1, "content is required") }) ).mutation(async ({ input }) => { const delivered = await notifyOwner(input); return { success: delivered }; }) }); // server/nacAuth.ts import mysql from "mysql2/promise"; import bcrypt from "bcryptjs"; import jwt from "jsonwebtoken"; // server/secrets.ts function readSecret(key) { return process.env[key]; } function getNacMysqlUrl() { const val = readSecret("NAC_MYSQL_URL"); if (!val) throw new Error("[Secrets] NAC_MYSQL_URL \u672A\u914D\u7F6E"); return val; } function getNacMongoUrl() { const val = readSecret("NAC_MONGO_URL"); if (!val) throw new Error("[Secrets] NAC_MONGO_URL \u672A\u914D\u7F6E"); return val; } function getNacJwtSecret() { const val = readSecret("NAC_JWT_SECRET"); if (!val) throw new Error("[Secrets] NAC_JWT_SECRET \u672A\u914D\u7F6E"); return val; } // server/nacAuth.ts var pool = null; function getNacMysqlPool() { if (pool) return pool; const url = new URL(getNacMysqlUrl()); pool = mysql.createPool({ host: url.hostname, port: parseInt(url.port || "3306"), user: url.username, password: url.password, database: url.pathname.slice(1), waitForConnections: true, connectionLimit: 5, connectTimeout: 1e4 }); return pool; } function resolveRole(kyc_level, node_status) { if (kyc_level >= 2 && node_status === "constitutional") return "admin"; if (kyc_level >= 1) return "legal"; return "reviewer"; } async function loginWithNacCredentials(email, password) { const pool2 = getNacMysqlPool(); const [rows] = await pool2.execute( "SELECT id, name, email, password, kyc_level, node_status, is_active FROM users WHERE email = ? AND is_active = 1", [email] ); if (rows.length === 0) return null; const dbUser = rows[0]; const valid = await bcrypt.compare(password, dbUser.password); if (!valid) return null; const role = resolveRole(dbUser.kyc_level, dbUser.node_status); const user = { id: dbUser.id, name: dbUser.name, email: dbUser.email, kyc_level: dbUser.kyc_level, node_status: dbUser.node_status, is_active: dbUser.is_active, role }; const token = jwt.sign( { id: user.id, email: user.email, role: user.role }, getNacJwtSecret(), { expiresIn: "24h" } ); return { user, token }; } function verifyNacToken(token) { try { return jwt.verify(token, getNacJwtSecret()); } catch { return null; } } async function getNacUserById(id) { const pool2 = getNacMysqlPool(); const [rows] = await pool2.execute( "SELECT id, name, email, kyc_level, node_status, is_active FROM users WHERE id = ? AND is_active = 1", [id] ); if (rows.length === 0) return null; const r = rows[0]; return { ...r, role: resolveRole(r.kyc_level, r.node_status) }; } async function listNacUsers(limit = 50, offset = 0) { const pool2 = getNacMysqlPool(); const [rows] = await pool2.execute( "SELECT id, name, email, kyc_level, node_status, is_active, created_at, last_login_at FROM users ORDER BY id DESC LIMIT ? OFFSET ?", [limit, offset] ); return rows.map((r) => ({ ...r, role: resolveRole(r.kyc_level, r.node_status) })); } async function getNacUserCount() { const pool2 = getNacMysqlPool(); const [rows] = await pool2.execute("SELECT COUNT(*) as cnt FROM users"); return rows[0]?.cnt || 0; } // server/mongodb.ts import { MongoClient } from "mongodb"; var client = null; var db = null; async function getMongoDb() { if (db) return db; try { client = new MongoClient(getNacMongoUrl(), { serverSelectionTimeoutMS: 5e3, connectTimeoutMS: 5e3 }); await client.connect(); db = client.db("nac_knowledge_engine"); console.log("[MongoDB] Connected to nac_knowledge_engine"); return db; } catch (error) { console.error("[MongoDB] Connection failed:", error.message); return null; } } var COLLECTIONS = { COMPLIANCE_RULES: "compliance_rules", CRAWLERS: "crawlers", CRAWLER_LOGS: "crawler_logs", APPROVAL_CASES: "approval_cases", TAG_RULES: "tag_rules", PROTOCOL_REGISTRY: "protocol_registry", AUDIT_LOGS: "audit_logs", KNOWLEDGE_STATS: "knowledge_stats" }; // server/routers.ts import { ObjectId } from "mongodb"; // server/_core/cookies.ts function isSecureRequest(req) { if (req.protocol === "https") return true; const forwardedProto = req.headers["x-forwarded-proto"]; if (!forwardedProto) return false; const protoList = Array.isArray(forwardedProto) ? forwardedProto : forwardedProto.split(","); return protoList.some((proto) => proto.trim().toLowerCase() === "https"); } function getSessionCookieOptions(req) { return { httpOnly: true, path: "/", sameSite: "none", secure: isSecureRequest(req) }; } // server/i18nTranslation.ts var SUPPORTED_LANGUAGES = ["zh", "en", "ar", "ja", "ko", "fr", "ru"]; var LANGUAGE_NAMES = { zh: "\u4E2D\u6587\uFF08\u7B80\u4F53\uFF09", en: "English", ar: "\u0627\u0644\u0639\u0631\u0628\u064A\u0629", ja: "\u65E5\u672C\u8A9E", ko: "\uD55C\uAD6D\uC5B4", fr: "Fran\xE7ais", ru: "\u0420\u0443\u0441\u0441\u043A\u0438\u0439" }; var RTL_LANGUAGES = ["ar"]; function isRTL(lang) { return RTL_LANGUAGES.includes(lang); } function isAiTranslationConfigured() { return !!(process.env.NAC_AI_API_URL && process.env.NAC_AI_API_KEY); } async function callAiTranslation(systemPrompt, userPrompt) { const apiUrl = process.env.NAC_AI_API_URL; const apiKey = process.env.NAC_AI_API_KEY; const model = process.env.NAC_AI_MODEL || "gpt-3.5-turbo"; if (!apiUrl || !apiKey) { throw new Error( "[AI\u7FFB\u8BD1] \u672A\u914D\u7F6EAI\u63A5\u53E3\u3002\u8BF7\u5728 .env \u6587\u4EF6\u4E2D\u8BBE\u7F6E NAC_AI_API_URL \u548C NAC_AI_API_KEY\u3002\n\u652F\u6301\u4EFB\u4F55OpenAI\u517C\u5BB9\u63A5\u53E3\uFF08\u5982 OpenAI\u3001Azure OpenAI\u3001\u56FD\u5185\u5927\u6A21\u578B\u7B49\uFF09\u3002" ); } const endpoint = `${apiUrl.replace(/\/$/, "")}/v1/chat/completions`; const response = await fetch(endpoint, { method: "POST", headers: { "content-type": "application/json", authorization: `Bearer ${apiKey}` }, body: JSON.stringify({ model, messages: [ { role: "system", content: systemPrompt }, { role: "user", content: userPrompt } ], max_tokens: 2048, temperature: 0.3 // 低温度保证翻译一致性 }) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`AI\u63A5\u53E3\u8C03\u7528\u5931\u8D25: ${response.status} ${response.statusText} \u2013 ${errorText}`); } const result = await response.json(); const content = result.choices?.[0]?.message?.content; return (typeof content === "string" ? content : "").trim(); } async function translateText(sourceText, sourceLang, targetLang, context) { if (sourceLang === targetLang) return sourceText; if (!sourceText.trim()) return sourceText; const contextHint = context ? ` \u80CC\u666F\u4FE1\u606F\uFF08\u4EC5\u4F9B\u53C2\u8003\uFF0C\u4E0D\u8981\u7FFB\u8BD1\uFF09\uFF1A${context}` : ""; const arabicHint = targetLang === "ar" ? "\n\u7279\u522B\u8BF4\u660E\uFF1A\u7FFB\u8BD1\u6210\u6807\u51C6\u73B0\u4EE3\u963F\u62C9\u4F2F\u8BED\uFF08MSA\uFF09\uFF0C\u4F7F\u7528Unicode\u963F\u62C9\u4F2F\u5B57\u7B26\uFF0C\u6587\u672C\u65B9\u5411\u4E3A\u4ECE\u53F3\u5230\u5DE6\uFF08RTL\uFF09\u3002" : ""; const systemPrompt = `\u4F60\u662FNAC\uFF08NewAssetChain\uFF09\u516C\u94FE\u7684\u4E13\u4E1A\u6CD5\u5F8B\u5408\u89C4\u7FFB\u8BD1\u4E13\u5BB6\u3002 NAC\u662F\u4E00\u6761\u4E13\u6CE8\u4E8ERWA\uFF08\u771F\u5B9E\u4E16\u754C\u8D44\u4EA7\uFF09\u7684\u539F\u751F\u516C\u94FE\uFF0C\u4F7F\u7528Charter\u667A\u80FD\u5408\u7EA6\u8BED\u8A00\u3001NVM\u865A\u62DF\u673A\u3001CBPP\u5171\u8BC6\u534F\u8BAE\u3002 \u4F60\u7684\u4EFB\u52A1\u662F\u5C06\u5408\u89C4\u89C4\u5219\u6587\u672C\u4ECE${LANGUAGE_NAMES[sourceLang]}\u7FFB\u8BD1\u6210${LANGUAGE_NAMES[targetLang]}\u3002 \u8981\u6C42\uFF1A 1. \u4FDD\u6301\u6CD5\u5F8B\u672F\u8BED\u7684\u51C6\u786E\u6027\u548C\u4E13\u4E1A\u6027 2. \u4FDD\u7559\u4E13\u6709\u540D\u8BCD\uFF08\u5982\uFF1ANAC\u3001RWA\u3001Charter\u3001NVM\u3001CBPP\u3001CSNP\u3001CNNL\u3001ACC-20\u3001GNACS\u3001XTZH\uFF09\u4E0D\u7FFB\u8BD1 3. \u4FDD\u7559\u673A\u6784\u540D\u79F0\uFF08\u5982\uFF1ASEC\u3001SFC\u3001MAS\u3001ESMA\u3001DFSA\u3001DLD\uFF09\u4E0D\u7FFB\u8BD1 4. \u53EA\u8FD4\u56DE\u7FFB\u8BD1\u7ED3\u679C\uFF0C\u4E0D\u8981\u6DFB\u52A0\u4EFB\u4F55\u89E3\u91CA\u6216\u6CE8\u91CA${arabicHint}`; const userPrompt = `\u8BF7\u5C06\u4EE5\u4E0B\u6587\u672C\u7FFB\u8BD1\u6210${LANGUAGE_NAMES[targetLang]}\uFF1A ${sourceText}${contextHint}`; try { const translated = await callAiTranslation(systemPrompt, userPrompt); return translated || sourceText; } catch (error) { console.error(`[Translation] \u7FFB\u8BD1\u5230 ${targetLang} \u5931\u8D25:`, error.message); return sourceText; } } async function generateRuleTranslations(ruleName, description, sourceLang = "zh", existingTranslations) { const ruleNameI18n = { ...existingTranslations?.ruleNameI18n || {} }; const descriptionI18n = { ...existingTranslations?.descriptionI18n || {} }; ruleNameI18n[sourceLang] = ruleName; descriptionI18n[sourceLang] = description; const targetLangs = SUPPORTED_LANGUAGES.filter( (lang) => lang !== sourceLang && !ruleNameI18n[lang] ); await Promise.all( targetLangs.map(async (targetLang) => { const [translatedName, translatedDesc] = await Promise.all([ translateText(ruleName, sourceLang, targetLang, `\u8FD9\u662F\u4E00\u6761${description.slice(0, 50)}...\u7684\u5408\u89C4\u89C4\u5219`), translateText(description, sourceLang, targetLang) ]); ruleNameI18n[targetLang] = translatedName; descriptionI18n[targetLang] = translatedDesc; }) ); return { ruleNameI18n, descriptionI18n }; } async function migrateRuleToMultiLang(rule) { const sourceLang = "zh"; return generateRuleTranslations( rule.ruleName, rule.description, sourceLang, { ruleNameI18n: rule.ruleNameI18n, descriptionI18n: rule.descriptionI18n } ); } var ARABIC_RTL_TEST_CASES = [ { id: "ar-rtl-001", sourceLang: "zh", targetLang: "ar", sourceText: "\u4E0D\u52A8\u4EA7\u767B\u8BB0\u8BC1\u8981\u6C42", expectedContains: ["\u0639\u0642\u0627\u0631", "\u062A\u0633\u062C\u064A\u0644", "\u0634\u0647\u0627\u062F\u0629"], // 应包含房产/登记/证书相关词汇 isRTL: true, description: "\u623F\u4EA7\u767B\u8BB0\u8BC1\u8981\u6C42\uFF08\u963F\u62C9\u4F2F\u8BEDRTL\u6D4B\u8BD5\uFF09" }, { id: "ar-rtl-002", sourceLang: "en", targetLang: "ar", sourceText: "RWA asset compliance verification", expectedContains: ["\u0627\u0645\u062A\u062B\u0627\u0644", "\u0623\u0635\u0648\u0644", "\u0627\u0644\u062A\u062D\u0642\u0642"], // 应包含合规/资产/验证相关词汇 isRTL: true, description: "RWA\u8D44\u4EA7\u5408\u89C4\u9A8C\u8BC1\uFF08\u963F\u62C9\u4F2F\u8BEDRTL\u6D4B\u8BD5\uFF09" }, { id: "ar-rtl-003", sourceLang: "zh", targetLang: "ar", sourceText: "NAC\u516C\u94FE\u667A\u80FD\u5408\u7EA6\u5BA1\u6279\u6D41\u7A0B", expectedContains: ["NAC", "\u0639\u0642\u062F", "\u0645\u0648\u0627\u0641\u0642\u0629"], // NAC不翻译,包含合约/审批相关词汇 isRTL: true, description: "NAC\u4E13\u6709\u540D\u8BCD\u4FDD\u7559\u6D4B\u8BD5\uFF08\u963F\u62C9\u4F2F\u8BEDRTL\uFF09" } ]; async function runArabicRTLTests() { if (!isAiTranslationConfigured()) { return { passed: 0, failed: ARABIC_RTL_TEST_CASES.length, results: ARABIC_RTL_TEST_CASES.map((tc) => ({ id: tc.id, description: tc.description, passed: false, translated: "", isRTL: true, issues: ["AI\u7FFB\u8BD1\u670D\u52A1\u672A\u914D\u7F6E\uFF0C\u8BF7\u8BBE\u7F6E NAC_AI_API_URL \u548C NAC_AI_API_KEY"] })) }; } const results = await Promise.all( ARABIC_RTL_TEST_CASES.map(async (tc) => { const issues = []; let translated = ""; try { translated = await translateText(tc.sourceText, tc.sourceLang, tc.targetLang); const hasArabicChars = /[\u0600-\u06FF]/.test(translated); if (!hasArabicChars) { issues.push("\u7FFB\u8BD1\u7ED3\u679C\u4E0D\u5305\u542B\u963F\u62C9\u4F2F\u5B57\u7B26"); } if (tc.sourceText.includes("NAC") && !translated.includes("NAC")) { issues.push("\u4E13\u6709\u540D\u8BCDNAC\u88AB\u9519\u8BEF\u7FFB\u8BD1\uFF0C\u5E94\u4FDD\u7559\u82F1\u6587"); } if (!translated.trim()) { issues.push("\u7FFB\u8BD1\u7ED3\u679C\u4E3A\u7A7A"); } } catch (error) { issues.push(`\u7FFB\u8BD1\u5931\u8D25: ${error.message}`); } return { id: tc.id, description: tc.description, passed: issues.length === 0, translated, isRTL: tc.isRTL, issues }; }) ); const passed = results.filter((r) => r.passed).length; const failed = results.filter((r) => !r.passed).length; return { passed, failed, results }; } // server/routers.ts var nacAuthProcedure = publicProcedure.use(async ({ ctx, next }) => { const token = ctx.req.cookies?.["nac_admin_token"] || ctx.req.headers["x-nac-token"]; if (!token) throw new TRPCError3({ code: "UNAUTHORIZED", message: "\u8BF7\u5148\u767B\u5F55" }); const payload = verifyNacToken(token); if (!payload) throw new TRPCError3({ code: "UNAUTHORIZED", message: "\u767B\u5F55\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55" }); return next({ ctx: { ...ctx, nacUser: payload } }); }); var nacAdminProcedure = nacAuthProcedure.use(async ({ ctx, next }) => { if (ctx.nacUser?.role !== "admin") { throw new TRPCError3({ code: "FORBIDDEN", message: "\u9700\u8981\u7BA1\u7406\u5458\u6743\u9650" }); } return next({ ctx }); }); async function writeAuditLog(action, userId, email, detail) { try { const db2 = await getMongoDb(); if (!db2) return; await db2.collection(COLLECTIONS.AUDIT_LOGS).insertOne({ action, userId, email, detail, timestamp: /* @__PURE__ */ new Date(), immutable: true }); } catch (e) { console.error("[AuditLog] Failed:", e.message); } } async function ensureKnowledgeBaseData() { const db2 = await getMongoDb(); if (!db2) return; const protocolCount = await db2.collection(COLLECTIONS.PROTOCOL_REGISTRY).countDocuments(); if (protocolCount === 0) { await db2.collection(COLLECTIONS.PROTOCOL_REGISTRY).insertMany([ { name: "nac-charter-compiler", type: "contract_validation", version: "1.0.0", endpoint: "charter.newassetchain.io", trigger: "asset_type in ALL", status: "active", createdAt: /* @__PURE__ */ new Date() }, { name: "nac-cnnl-validator", type: "constitutional_check", version: "1.0.0", endpoint: "cnnl.newassetchain.io", trigger: "asset_type in ALL", status: "active", createdAt: /* @__PURE__ */ new Date() }, { name: "nac-acc20-engine", type: "compliance_approval", version: "1.0.0", endpoint: "acc20.newassetchain.io", trigger: "asset_type in ALL", status: "active", createdAt: /* @__PURE__ */ new Date() }, { name: "nac-gnacs-classifier", type: "asset_classification", version: "1.0.0", endpoint: "gnacs.newassetchain.io", trigger: "asset_type in ALL", status: "active", createdAt: /* @__PURE__ */ new Date() }, { name: "nac-valuation-ai", type: "valuation_model", version: "0.9.0", endpoint: "valuation.newassetchain.io", trigger: "asset_type is RealEstate", status: "pending", createdAt: /* @__PURE__ */ new Date() } ]); } const crawlerCount = await db2.collection(COLLECTIONS.CRAWLERS).countDocuments(); if (crawlerCount === 0) { await db2.collection(COLLECTIONS.CRAWLERS).insertMany([ { name: "CN-CSRC\u6CD5\u89C4\u91C7\u96C6\u5668", jurisdiction: "CN", type: "external", source: "http://www.csrc.gov.cn", category: "regulation", frequency: "daily", status: "active", lastRun: null, successRate: 0, totalCollected: 0, createdAt: /* @__PURE__ */ new Date() }, { name: "HK-SFC\u6CD5\u89C4\u91C7\u96C6\u5668", jurisdiction: "HK", type: "external", source: "https://www.sfc.hk", category: "regulation", frequency: "daily", status: "active", lastRun: null, successRate: 0, totalCollected: 0, createdAt: /* @__PURE__ */ new Date() }, { name: "US-SEC\u6CD5\u89C4\u91C7\u96C6\u5668", jurisdiction: "US", type: "external", source: "https://www.sec.gov", category: "regulation", frequency: "daily", status: "active", lastRun: null, successRate: 0, totalCollected: 0, createdAt: /* @__PURE__ */ new Date() }, { name: "EU-ESMA\u6CD5\u89C4\u91C7\u96C6\u5668", jurisdiction: "EU", type: "external", source: "https://www.esma.europa.eu", category: "regulation", frequency: "weekly", status: "active", lastRun: null, successRate: 0, totalCollected: 0, createdAt: /* @__PURE__ */ new Date() }, { name: "SG-MAS\u6CD5\u89C4\u91C7\u96C6\u5668", jurisdiction: "SG", type: "external", source: "https://www.mas.gov.sg", category: "regulation", frequency: "daily", status: "active", lastRun: null, successRate: 0, totalCollected: 0, createdAt: /* @__PURE__ */ new Date() }, { name: "AE-DFSA\u6CD5\u89C4\u91C7\u96C6\u5668", jurisdiction: "AE", type: "external", source: "https://www.dfsa.ae", category: "regulation", frequency: "weekly", status: "active", lastRun: null, successRate: 0, totalCollected: 0, createdAt: /* @__PURE__ */ new Date() }, { name: "CN-\u88C1\u5224\u6587\u4E66\u7F51\u91C7\u96C6\u5668", jurisdiction: "CN", type: "external", source: "https://wenshu.court.gov.cn", category: "credit", frequency: "weekly", status: "active", lastRun: null, successRate: 0, totalCollected: 0, createdAt: /* @__PURE__ */ new Date() }, { name: "\u5185\u90E8\u4E0A\u94FE\u6587\u4EF6\u91C7\u96C6\u5668", jurisdiction: "ALL", type: "internal", source: "internal://onboarding", category: "asset_document", frequency: "realtime", status: "active", lastRun: null, successRate: 0, totalCollected: 0, createdAt: /* @__PURE__ */ new Date() } ]); } const ruleCount = await db2.collection(COLLECTIONS.COMPLIANCE_RULES).countDocuments(); if (ruleCount === 0) { await db2.collection(COLLECTIONS.COMPLIANCE_RULES).insertMany([ { jurisdiction: "CN", assetType: "RealEstate", ruleName: "\u4E0D\u52A8\u4EA7\u767B\u8BB0\u8BC1\u8981\u6C42", description: "\u4E2D\u56FD\u5883\u5185\u623F\u5730\u4EA7\u4E0A\u94FE\u5FC5\u987B\u63D0\u4F9B\u4E0D\u52A8\u4EA7\u767B\u8BB0\u8BC1", ruleNameI18n: { zh: "\u4E0D\u52A8\u4EA7\u767B\u8BB0\u8BC1\u8981\u6C42", en: "Real Estate Registration Certificate Requirement", ar: "\u0645\u062A\u0637\u0644\u0628\u0627\u062A \u0634\u0647\u0627\u062F\u0629 \u062A\u0633\u062C\u064A\u0644 \u0627\u0644\u0639\u0642\u0627\u0631\u0627\u062A", ja: "\u4E0D\u52D5\u7523\u767B\u8A18\u8A3C\u8981\u4EF6", ko: "\uBD80\uB3D9\uC0B0 \uB4F1\uAE30\uC99D \uC694\uAC74", fr: "Exigence de certificat d'enregistrement immobilier", ru: "\u0422\u0440\u0435\u0431\u043E\u0432\u0430\u043D\u0438\u0435 \u043A \u0441\u0432\u0438\u0434\u0435\u0442\u0435\u043B\u044C\u0441\u0442\u0432\u0443 \u043E \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043D\u0435\u0434\u0432\u0438\u0436\u0438\u043C\u043E\u0441\u0442\u0438" }, descriptionI18n: { zh: "\u4E2D\u56FD\u5883\u5185\u623F\u5730\u4EA7\u4E0A\u94FE\u5FC5\u987B\u63D0\u4F9B\u4E0D\u52A8\u4EA7\u767B\u8BB0\u8BC1", en: "Real estate assets on-chain in China must provide a real estate registration certificate", ar: "\u064A\u062C\u0628 \u0639\u0644\u0649 \u0627\u0644\u0623\u0635\u0648\u0644 \u0627\u0644\u0639\u0642\u0627\u0631\u064A\u0629 \u0627\u0644\u0645\u0633\u062C\u0644\u0629 \u0639\u0644\u0649 \u0627\u0644\u0633\u0644\u0633\u0644\u0629 \u0641\u064A \u0627\u0644\u0635\u064A\u0646 \u062A\u0642\u062F\u064A\u0645 \u0634\u0647\u0627\u062F\u0629 \u062A\u0633\u062C\u064A\u0644 \u0627\u0644\u0639\u0642\u0627\u0631\u0627\u062A", ja: "\u4E2D\u56FD\u56FD\u5185\u306E\u4E0D\u52D5\u7523\u30C1\u30A7\u30FC\u30F3\u767B\u9332\u306B\u306F\u4E0D\u52D5\u7523\u767B\u8A18\u8A3C\u306E\u63D0\u51FA\u304C\u5FC5\u8981", ko: "\uC911\uAD6D \uB0B4 \uBD80\uB3D9\uC0B0 \uC628\uCCB4\uC778 \uB4F1\uB85D \uC2DC \uBD80\uB3D9\uC0B0 \uB4F1\uAE30\uC99D \uC81C\uCD9C \uD544\uC218", fr: "Les actifs immobiliers enregistr\xE9s sur la cha\xEEne en Chine doivent fournir un certificat d'enregistrement immobilier", ru: "\u041D\u0435\u0434\u0432\u0438\u0436\u0438\u043C\u043E\u0441\u0442\u044C, \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043C\u0430\u044F \u0432 \u0431\u043B\u043E\u043A\u0447\u0435\u0439\u043D\u0435 \u0432 \u041A\u0438\u0442\u0430\u0435, \u0434\u043E\u043B\u0436\u043D\u0430 \u043F\u0440\u0435\u0434\u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0441\u0432\u0438\u0434\u0435\u0442\u0435\u043B\u044C\u0441\u0442\u0432\u043E \u043E \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043D\u0435\u0434\u0432\u0438\u0436\u0438\u043C\u043E\u0441\u0442\u0438" }, required: true, status: "active", tags: ["CN", "RealEstate", "Document", "Required"], createdAt: /* @__PURE__ */ new Date() }, { jurisdiction: "HK", assetType: "Securities", ruleName: "SFC\u6301\u724C\u8981\u6C42", description: "\u9999\u6E2F\u8BC1\u5238\u7C7B\u8D44\u4EA7\u4E0A\u94FE\u987B\u7ECFSFC\u6301\u724C\u673A\u6784\u5BA1\u6838", ruleNameI18n: { zh: "SFC\u6301\u724C\u8981\u6C42", en: "SFC Licensing Requirement", ar: "\u0645\u062A\u0637\u0644\u0628\u0627\u062A \u062A\u0631\u062E\u064A\u0635 SFC", ja: "SFC\u30E9\u30A4\u30BB\u30F3\u30B9\u8981\u4EF6", ko: "SFC \uB77C\uC774\uC120\uC2A4 \uC694\uAC74", fr: "Exigence de licence SFC", ru: "\u0422\u0440\u0435\u0431\u043E\u0432\u0430\u043D\u0438\u0435 \u043B\u0438\u0446\u0435\u043D\u0437\u0438\u0438 SFC" }, descriptionI18n: { zh: "\u9999\u6E2F\u8BC1\u5238\u7C7B\u8D44\u4EA7\u4E0A\u94FE\u987B\u7ECFSFC\u6301\u724C\u673A\u6784\u5BA1\u6838", en: "Securities assets on-chain in Hong Kong must be reviewed by SFC-licensed institutions", ar: "\u064A\u062C\u0628 \u0645\u0631\u0627\u062C\u0639\u0629 \u0623\u0635\u0648\u0644 \u0627\u0644\u0623\u0648\u0631\u0627\u0642 \u0627\u0644\u0645\u0627\u0644\u064A\u0629 \u0627\u0644\u0645\u0633\u062C\u0644\u0629 \u0639\u0644\u0649 \u0627\u0644\u0633\u0644\u0633\u0644\u0629 \u0641\u064A \u0647\u0648\u0646\u063A \u0643\u0648\u0646\u063A \u0645\u0646 \u0642\u0628\u0644 \u0645\u0624\u0633\u0633\u0627\u062A \u0645\u0631\u062E\u0635\u0629 \u0645\u0646 SFC", ja: "\u9999\u6E2F\u306E\u8A3C\u5238\u8CC7\u7523\u306E\u30C1\u30A7\u30FC\u30F3\u767B\u9332\u306FSFC\u30E9\u30A4\u30BB\u30F3\u30B9\u6A5F\u95A2\u306E\u5BE9\u67FB\u304C\u5FC5\u8981", ko: "\uD64D\uCF69 \uC99D\uAD8C \uC790\uC0B0 \uC628\uCCB4\uC778 \uB4F1\uB85D \uC2DC SFC \uC778\uAC00 \uAE30\uAD00\uC758 \uC2EC\uC0AC \uD544\uC694", fr: "Les actifs en valeurs mobili\xE8res enregistr\xE9s sur la cha\xEEne \xE0 Hong Kong doivent \xEAtre examin\xE9s par des institutions agr\xE9\xE9es SFC", ru: "\u0426\u0435\u043D\u043D\u044B\u0435 \u0431\u0443\u043C\u0430\u0433\u0438, \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043C\u044B\u0435 \u0432 \u0431\u043B\u043E\u043A\u0447\u0435\u0439\u043D\u0435 \u0432 \u0413\u043E\u043D\u043A\u043E\u043D\u0433\u0435, \u0434\u043E\u043B\u0436\u043D\u044B \u043F\u0440\u043E\u0439\u0442\u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443 \u0443\u0447\u0440\u0435\u0436\u0434\u0435\u043D\u0438\u044F\u043C\u0438 \u0441 \u043B\u0438\u0446\u0435\u043D\u0437\u0438\u0435\u0439 SFC" }, required: true, status: "active", tags: ["HK", "Securities", "License", "SFC"], createdAt: /* @__PURE__ */ new Date() }, { jurisdiction: "US", assetType: "Securities", ruleName: "Reg D\u8C41\u514D\u7533\u62A5", description: "\u7F8E\u56FD\u8BC1\u5238\u7C7B\u8D44\u4EA7\u987B\u6EE1\u8DB3Reg D/S\u8C41\u514D\u6761\u4EF6", ruleNameI18n: { zh: "Reg D\u8C41\u514D\u7533\u62A5", en: "Reg D Exemption Filing", ar: "\u062A\u0642\u062F\u064A\u0645 \u0625\u0639\u0641\u0627\u0621 Reg D", ja: "Reg D\u514D\u9664\u7533\u544A", ko: "Reg D \uBA74\uC81C \uC2E0\uACE0", fr: "D\xE9claration d'exemption Reg D", ru: "\u041F\u043E\u0434\u0430\u0447\u0430 \u0437\u0430\u044F\u0432\u043B\u0435\u043D\u0438\u044F \u043E\u0431 \u043E\u0441\u0432\u043E\u0431\u043E\u0436\u0434\u0435\u043D\u0438\u0438 \u043F\u043E Reg D" }, descriptionI18n: { zh: "\u7F8E\u56FD\u8BC1\u5238\u7C7B\u8D44\u4EA7\u987B\u6EE1\u8DB3Reg D/S\u8C41\u514D\u6761\u4EF6", en: "US securities assets must meet Reg D/S exemption conditions", ar: "\u064A\u062C\u0628 \u0623\u0646 \u062A\u0633\u062A\u0648\u0641\u064A \u0623\u0635\u0648\u0644 \u0627\u0644\u0623\u0648\u0631\u0627\u0642 \u0627\u0644\u0645\u0627\u0644\u064A\u0629 \u0627\u0644\u0623\u0645\u0631\u064A\u0643\u064A\u0629 \u0634\u0631\u0648\u0637 \u0625\u0639\u0641\u0627\u0621 Reg D/S", ja: "\u7C73\u56FD\u8A3C\u5238\u8CC7\u7523\u306FReg D/S\u514D\u9664\u6761\u4EF6\u3092\u6E80\u305F\u3059\u5FC5\u8981\u304C\u3042\u308B", ko: "\uBBF8\uAD6D \uC99D\uAD8C \uC790\uC0B0\uC740 Reg D/S \uBA74\uC81C \uC870\uAC74\uC744 \uCDA9\uC871\uD574\uC57C \uD568", fr: "Les actifs en valeurs mobili\xE8res am\xE9ricains doivent satisfaire aux conditions d'exemption Reg D/S", ru: "\u0426\u0435\u043D\u043D\u044B\u0435 \u0431\u0443\u043C\u0430\u0433\u0438 \u0421\u0428\u0410 \u0434\u043E\u043B\u0436\u043D\u044B \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u043E\u0432\u0430\u0442\u044C \u0443\u0441\u043B\u043E\u0432\u0438\u044F\u043C \u043E\u0441\u0432\u043E\u0431\u043E\u0436\u0434\u0435\u043D\u0438\u044F \u043F\u043E Reg D/S" }, required: true, status: "active", tags: ["US", "Securities", "RegD", "SEC"], createdAt: /* @__PURE__ */ new Date() }, { jurisdiction: "EU", assetType: "ALL", ruleName: "MiCA\u5408\u89C4\u8981\u6C42", description: "\u6B27\u76DF\u5883\u5185\u6240\u6709\u52A0\u5BC6\u8D44\u4EA7\u987B\u7B26\u5408MiCA\u6CD5\u89C4", ruleNameI18n: { zh: "MiCA\u5408\u89C4\u8981\u6C42", en: "MiCA Compliance Requirement", ar: "\u0645\u062A\u0637\u0644\u0628\u0627\u062A \u0627\u0644\u0627\u0645\u062A\u062B\u0627\u0644 MiCA", ja: "MiCA\u30B3\u30F3\u30D7\u30E9\u30A4\u30A2\u30F3\u30B9\u8981\u4EF6", ko: "MiCA \uC900\uC218 \uC694\uAC74", fr: "Exigence de conformit\xE9 MiCA", ru: "\u0422\u0440\u0435\u0431\u043E\u0432\u0430\u043D\u0438\u0435 \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u044F MiCA" }, descriptionI18n: { zh: "\u6B27\u76DF\u5883\u5185\u6240\u6709\u52A0\u5BC6\u8D44\u4EA7\u987B\u7B26\u5408MiCA\u6CD5\u89C4", en: "All crypto assets within the EU must comply with MiCA regulations", ar: "\u064A\u062C\u0628 \u0623\u0646 \u062A\u0645\u062A\u062B\u0644 \u062C\u0645\u064A\u0639 \u0627\u0644\u0623\u0635\u0648\u0644 \u0627\u0644\u0645\u0634\u0641\u0631\u0629 \u062F\u0627\u062E\u0644 \u0627\u0644\u0627\u062A\u062D\u0627\u062F \u0627\u0644\u0623\u0648\u0631\u0648\u0628\u064A \u0644\u0644\u0648\u0627\u0626\u062D MiCA", ja: "EU\u57DF\u5185\u306E\u3059\u3079\u3066\u306E\u6697\u53F7\u8CC7\u7523\u306FMiCA\u898F\u5236\u306B\u6E96\u62E0\u3059\u308B\u5FC5\u8981\u304C\u3042\u308B", ko: "EU \uB0B4 \uBAA8\uB4E0 \uC554\uD638\uD654 \uC790\uC0B0\uC740 MiCA \uADDC\uC815\uC744 \uC900\uC218\uD574\uC57C \uD568", fr: "Tous les crypto-actifs au sein de l'UE doivent se conformer aux r\xE9glementations MiCA", ru: "\u0412\u0441\u0435 \u043A\u0440\u0438\u043F\u0442\u043E\u0430\u043A\u0442\u0438\u0432\u044B \u0432 \u0415\u0421 \u0434\u043E\u043B\u0436\u043D\u044B \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u043E\u0432\u0430\u0442\u044C \u0440\u0435\u0433\u043B\u0430\u043C\u0435\u043D\u0442\u0443 MiCA" }, required: true, status: "active", tags: ["EU", "ALL", "MiCA", "ESMA"], createdAt: /* @__PURE__ */ new Date() }, { jurisdiction: "SG", assetType: "DigitalToken", ruleName: "MAS\u6570\u5B57\u4EE3\u5E01\u670D\u52A1\u724C\u7167", description: "\u65B0\u52A0\u5761\u6570\u5B57\u4EE3\u5E01\u670D\u52A1\u987B\u6301MAS\u724C\u7167", ruleNameI18n: { zh: "MAS\u6570\u5B57\u4EE3\u5E01\u670D\u52A1\u724C\u7167", en: "MAS Digital Token Service License", ar: "\u062A\u0631\u062E\u064A\u0635 \u062E\u062F\u0645\u0629 \u0627\u0644\u0631\u0645\u0632 \u0627\u0644\u0631\u0642\u0645\u064A MAS", ja: "MAS\u30C7\u30B8\u30BF\u30EB\u30C8\u30FC\u30AF\u30F3\u30B5\u30FC\u30D3\u30B9\u30E9\u30A4\u30BB\u30F3\u30B9", ko: "MAS \uB514\uC9C0\uD138 \uD1A0\uD070 \uC11C\uBE44\uC2A4 \uB77C\uC774\uC120\uC2A4", fr: "Licence de service de jetons num\xE9riques MAS", ru: "\u041B\u0438\u0446\u0435\u043D\u0437\u0438\u044F \u043D\u0430 \u0443\u0441\u043B\u0443\u0433\u0438 \u0446\u0438\u0444\u0440\u043E\u0432\u044B\u0445 \u0442\u043E\u043A\u0435\u043D\u043E\u0432 MAS" }, descriptionI18n: { zh: "\u65B0\u52A0\u5761\u6570\u5B57\u4EE3\u5E01\u670D\u52A1\u987B\u6301MAS\u724C\u7167", en: "Digital token services in Singapore must hold a MAS license", ar: "\u064A\u062C\u0628 \u0623\u0646 \u062A\u062D\u0645\u0644 \u062E\u062F\u0645\u0627\u062A \u0627\u0644\u0631\u0645\u0632 \u0627\u0644\u0631\u0642\u0645\u064A \u0641\u064A \u0633\u0646\u063A\u0627\u0641\u0648\u0631\u0629 \u062A\u0631\u062E\u064A\u0635 MAS", ja: "\u30B7\u30F3\u30AC\u30DD\u30FC\u30EB\u306E\u30C7\u30B8\u30BF\u30EB\u30C8\u30FC\u30AF\u30F3\u30B5\u30FC\u30D3\u30B9\u306FMAS\u30E9\u30A4\u30BB\u30F3\u30B9\u304C\u5FC5\u8981", ko: "\uC2F1\uAC00\uD3EC\uB974 \uB514\uC9C0\uD138 \uD1A0\uD070 \uC11C\uBE44\uC2A4\uB294 MAS \uB77C\uC774\uC120\uC2A4 \uBCF4\uC720 \uD544\uC694", fr: "Les services de jetons num\xE9riques \xE0 Singapour doivent d\xE9tenir une licence MAS", ru: "\u0423\u0441\u043B\u0443\u0433\u0438 \u0446\u0438\u0444\u0440\u043E\u0432\u044B\u0445 \u0442\u043E\u043A\u0435\u043D\u043E\u0432 \u0432 \u0421\u0438\u043D\u0433\u0430\u043F\u0443\u0440\u0435 \u0434\u043E\u043B\u0436\u043D\u044B \u0438\u043C\u0435\u0442\u044C \u043B\u0438\u0446\u0435\u043D\u0437\u0438\u044E MAS" }, required: true, status: "active", tags: ["SG", "DigitalToken", "MAS", "License"], createdAt: /* @__PURE__ */ new Date() }, { jurisdiction: "AE", assetType: "RealEstate", ruleName: "DLD\u4EA7\u6743\u8BC1\u4E66\u8981\u6C42", description: "\u8FEA\u62DC\u623F\u5730\u4EA7\u4E0A\u94FE\u987B\u63D0\u4F9BDLD\u9881\u53D1\u7684\u4EA7\u6743\u8BC1\u4E66", ruleNameI18n: { zh: "DLD\u4EA7\u6743\u8BC1\u4E66\u8981\u6C42", en: "DLD Title Deed Requirement", ar: "\u0645\u062A\u0637\u0644\u0628\u0627\u062A \u0633\u0646\u062F \u0627\u0644\u0645\u0644\u0643\u064A\u0629 DLD", ja: "DLD\u6240\u6709\u6A29\u8A3C\u66F8\u8981\u4EF6", ko: "DLD \uC18C\uC720\uAD8C \uC99D\uC11C \uC694\uAC74", fr: "Exigence de titre de propri\xE9t\xE9 DLD", ru: "\u0422\u0440\u0435\u0431\u043E\u0432\u0430\u043D\u0438\u0435 \u043A \u0441\u0432\u0438\u0434\u0435\u0442\u0435\u043B\u044C\u0441\u0442\u0432\u0443 \u043E \u043F\u0440\u0430\u0432\u0435 \u0441\u043E\u0431\u0441\u0442\u0432\u0435\u043D\u043D\u043E\u0441\u0442\u0438 DLD" }, descriptionI18n: { zh: "\u8FEA\u62DC\u623F\u5730\u4EA7\u4E0A\u94FE\u987B\u63D0\u4F9BDLD\u9881\u53D1\u7684\u4EA7\u6743\u8BC1\u4E66", en: "Dubai real estate on-chain must provide a title deed issued by DLD", ar: "\u064A\u062C\u0628 \u0623\u0646 \u062A\u0648\u0641\u0631 \u0627\u0644\u0639\u0642\u0627\u0631\u0627\u062A \u0627\u0644\u0645\u0633\u062C\u0644\u0629 \u0639\u0644\u0649 \u0627\u0644\u0633\u0644\u0633\u0644\u0629 \u0641\u064A \u062F\u0628\u064A \u0633\u0646\u062F \u0645\u0644\u0643\u064A\u0629 \u0635\u0627\u062F\u0631 \u0639\u0646 DLD", ja: "\u30C9\u30D0\u30A4\u306E\u4E0D\u52D5\u7523\u30C1\u30A7\u30FC\u30F3\u767B\u9332\u306B\u306FDLD\u767A\u884C\u306E\u6240\u6709\u6A29\u8A3C\u66F8\u304C\u5FC5\u8981", ko: "\uB450\uBC14\uC774 \uBD80\uB3D9\uC0B0 \uC628\uCCB4\uC778 \uB4F1\uB85D \uC2DC DLD \uBC1C\uAE09 \uC18C\uC720\uAD8C \uC99D\uC11C \uC81C\uCD9C \uD544\uC218", fr: "L'immobilier de Duba\xEF enregistr\xE9 sur la cha\xEEne doit fournir un titre de propri\xE9t\xE9 d\xE9livr\xE9 par DLD", ru: "\u041D\u0435\u0434\u0432\u0438\u0436\u0438\u043C\u043E\u0441\u0442\u044C \u0414\u0443\u0431\u0430\u044F, \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043C\u0430\u044F \u0432 \u0431\u043B\u043E\u043A\u0447\u0435\u0439\u043D\u0435, \u0434\u043E\u043B\u0436\u043D\u0430 \u043F\u0440\u0435\u0434\u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0441\u0432\u0438\u0434\u0435\u0442\u0435\u043B\u044C\u0441\u0442\u0432\u043E \u043E \u043F\u0440\u0430\u0432\u0435 \u0441\u043E\u0431\u0441\u0442\u0432\u0435\u043D\u043D\u043E\u0441\u0442\u0438, \u0432\u044B\u0434\u0430\u043D\u043D\u043E\u0435 DLD" }, required: true, status: "active", tags: ["AE", "RealEstate", "DLD", "Document"], createdAt: /* @__PURE__ */ new Date() } ]); } } var appRouter = router({ system: systemRouter, // ─── NAC原生认证(不使用Manus OAuth)──────────────────────────── nacAuth: router({ login: publicProcedure.input(z2.object({ email: z2.string().email(), password: z2.string().min(1) })).mutation(async ({ input, ctx }) => { try { const result = await loginWithNacCredentials(input.email, input.password); if (!result) throw new TRPCError3({ code: "UNAUTHORIZED", message: "\u90AE\u7BB1\u6216\u5BC6\u7801\u9519\u8BEF" }); const cookieOptions = getSessionCookieOptions(ctx.req); ctx.res.cookie("nac_admin_token", result.token, { ...cookieOptions, maxAge: 24 * 60 * 60 * 1e3 }); await writeAuditLog("LOGIN", result.user.id, result.user.email, { ip: ctx.req.ip }); return { success: true, user: { id: result.user.id, name: result.user.name, email: result.user.email, role: result.user.role, kyc_level: result.user.kyc_level } }; } catch (e) { if (e instanceof TRPCError3) throw e; throw new TRPCError3({ code: "INTERNAL_SERVER_ERROR", message: "\u767B\u5F55\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528" }); } }), logout: publicProcedure.mutation(({ ctx }) => { ctx.res.clearCookie("nac_admin_token"); return { success: true }; }), me: nacAuthProcedure.query(async ({ ctx }) => { const nacUser = ctx.nacUser; return { id: nacUser.id, email: nacUser.email, role: nacUser.role }; }) }), // ─── 全局态势感知仪表盘 ────────────────────────────────────────── dashboard: router({ stats: nacAuthProcedure.query(async () => { const db2 = await getMongoDb(); if (!db2) return { error: "\u6570\u636E\u5E93\u8FDE\u63A5\u5931\u8D25" }; await ensureKnowledgeBaseData(); const [ruleCount, crawlerCount, caseCount, protocolCount, userCount, auditCount] = await Promise.all([ db2.collection(COLLECTIONS.COMPLIANCE_RULES).countDocuments(), db2.collection(COLLECTIONS.CRAWLERS).countDocuments(), db2.collection(COLLECTIONS.APPROVAL_CASES).countDocuments(), db2.collection(COLLECTIONS.PROTOCOL_REGISTRY).countDocuments(), getNacUserCount(), db2.collection(COLLECTIONS.AUDIT_LOGS).countDocuments() ]); const [activeCrawlers, pendingCases, approvedCases, activeProtocols] = await Promise.all([ db2.collection(COLLECTIONS.CRAWLERS).countDocuments({ status: "active" }), db2.collection(COLLECTIONS.APPROVAL_CASES).countDocuments({ status: "pending_review" }), db2.collection(COLLECTIONS.APPROVAL_CASES).countDocuments({ decision: "approved" }), db2.collection(COLLECTIONS.PROTOCOL_REGISTRY).countDocuments({ status: "active" }) ]); const jurisdictionStats = await db2.collection(COLLECTIONS.COMPLIANCE_RULES).aggregate([ { $group: { _id: "$jurisdiction", count: { $sum: 1 } } }, { $sort: { count: -1 } } ]).toArray(); return { knowledgeBase: { totalRules: ruleCount, activeProtocols, totalProtocols: protocolCount }, crawlers: { total: crawlerCount, active: activeCrawlers }, approvals: { total: caseCount, pending: pendingCases, approved: approvedCases, approvalRate: caseCount > 0 ? Math.round(approvedCases / caseCount * 100) : 0 }, users: { total: userCount }, audit: { total: auditCount }, jurisdictionCoverage: jurisdictionStats, systemStatus: { mongodb: "connected", mysql: "connected", timestamp: /* @__PURE__ */ new Date() } }; }), recentActivity: nacAuthProcedure.query(async () => { const db2 = await getMongoDb(); if (!db2) return []; return db2.collection(COLLECTIONS.AUDIT_LOGS).find({}).sort({ timestamp: -1 }).limit(20).toArray(); }) }), // ─── 知识库管理(含多语言支持)────────────────────────────────── knowledgeBase: router({ list: nacAuthProcedure.input(z2.object({ jurisdiction: z2.string().optional(), assetType: z2.string().optional(), status: z2.string().optional(), page: z2.number().default(1), pageSize: z2.number().default(20), lang: z2.enum(["zh", "en", "ar", "ja", "ko", "fr", "ru"]).optional() })).query(async ({ input }) => { const db2 = await getMongoDb(); if (!db2) return { items: [], total: 0 }; const filter = {}; if (input.jurisdiction) filter.jurisdiction = input.jurisdiction; if (input.assetType) filter.assetType = input.assetType; if (input.status) filter.status = input.status; const skip = (input.page - 1) * input.pageSize; const [items, total] = await Promise.all([ db2.collection(COLLECTIONS.COMPLIANCE_RULES).find(filter).sort({ createdAt: -1 }).skip(skip).limit(input.pageSize).toArray(), db2.collection(COLLECTIONS.COMPLIANCE_RULES).countDocuments(filter) ]); const lang = input.lang || "zh"; const localizedItems = items.map((item) => ({ ...item, displayName: item.ruleNameI18n?.[lang] || item.ruleName, displayDescription: item.descriptionI18n?.[lang] || item.description })); return { items: localizedItems, total }; }), create: nacAdminProcedure.input(z2.object({ jurisdiction: z2.string(), assetType: z2.string(), ruleName: z2.string(), description: z2.string(), required: z2.boolean(), tags: z2.array(z2.string()), sourceLang: z2.enum(["zh", "en", "ar", "ja", "ko", "fr", "ru"]).optional(), autoTranslate: z2.boolean().optional() })).mutation(async ({ input, ctx }) => { const db2 = await getMongoDb(); if (!db2) throw new TRPCError3({ code: "INTERNAL_SERVER_ERROR" }); let ruleNameI18n = {}; let descriptionI18n = {}; if (input.autoTranslate !== false) { try { const translations = await generateRuleTranslations( input.ruleName, input.description, input.sourceLang || "zh" ); ruleNameI18n = translations.ruleNameI18n; descriptionI18n = translations.descriptionI18n; } catch (e) { console.error("[KnowledgeBase] Auto-translate failed:", e.message); const lang = input.sourceLang || "zh"; ruleNameI18n[lang] = input.ruleName; descriptionI18n[lang] = input.description; } } else { const lang = input.sourceLang || "zh"; ruleNameI18n[lang] = input.ruleName; descriptionI18n[lang] = input.description; } const result = await db2.collection(COLLECTIONS.COMPLIANCE_RULES).insertOne({ jurisdiction: input.jurisdiction, assetType: input.assetType, ruleName: input.ruleName, description: input.description, ruleNameI18n, descriptionI18n, required: input.required, tags: input.tags, status: "active", createdAt: /* @__PURE__ */ new Date() }); await writeAuditLog("CREATE_RULE", ctx.nacUser.id, ctx.nacUser.email, { ruleId: result.insertedId }); return { id: result.insertedId }; }), update: nacAdminProcedure.input(z2.object({ id: z2.string(), data: z2.object({ ruleName: z2.string().optional(), description: z2.string().optional(), status: z2.enum(["active", "disabled"]).optional(), tags: z2.array(z2.string()).optional() }), autoTranslate: z2.boolean().optional() })).mutation(async ({ input, ctx }) => { const db2 = await getMongoDb(); if (!db2) throw new TRPCError3({ code: "INTERNAL_SERVER_ERROR" }); const updateData = { ...input.data, updatedAt: /* @__PURE__ */ new Date() }; if (input.autoTranslate !== false && (input.data.ruleName || input.data.description)) { const existing = await db2.collection(COLLECTIONS.COMPLIANCE_RULES).findOne({ _id: new ObjectId(input.id) }); if (existing) { try { const translations = await generateRuleTranslations( input.data.ruleName || existing.ruleName, input.data.description || existing.description, "zh", { ruleNameI18n: existing.ruleNameI18n, descriptionI18n: existing.descriptionI18n } ); updateData.ruleNameI18n = translations.ruleNameI18n; updateData.descriptionI18n = translations.descriptionI18n; } catch (e) { console.error("[KnowledgeBase] Auto-translate on update failed:", e.message); } } } await db2.collection(COLLECTIONS.COMPLIANCE_RULES).updateOne( { _id: new ObjectId(input.id) }, { $set: updateData } ); await writeAuditLog("UPDATE_RULE", ctx.nacUser.id, ctx.nacUser.email, { ruleId: input.id }); return { success: true }; }), toggleStatus: nacAdminProcedure.input(z2.object({ id: z2.string(), status: z2.enum(["active", "disabled"]) })).mutation(async ({ input, ctx }) => { const db2 = await getMongoDb(); if (!db2) throw new TRPCError3({ code: "INTERNAL_SERVER_ERROR" }); await db2.collection(COLLECTIONS.COMPLIANCE_RULES).updateOne({ _id: new ObjectId(input.id) }, { $set: { status: input.status, updatedAt: /* @__PURE__ */ new Date() } }); await writeAuditLog("TOGGLE_RULE", ctx.nacUser.id, ctx.nacUser.email, { ruleId: input.id, newStatus: input.status }); return { success: true }; }), delete: nacAdminProcedure.input(z2.object({ id: z2.string() })).mutation(async ({ input, ctx }) => { const db2 = await getMongoDb(); if (!db2) throw new TRPCError3({ code: "INTERNAL_SERVER_ERROR" }); await db2.collection(COLLECTIONS.COMPLIANCE_RULES).deleteOne({ _id: new ObjectId(input.id) }); await writeAuditLog("DELETE_RULE", ctx.nacUser.id, ctx.nacUser.email, { ruleId: input.id }); return { success: true }; }), // ─── AI辅助翻译接口 ────────────────────────────────────────── translateRule: nacAdminProcedure.input(z2.object({ id: z2.string(), targetLang: z2.enum(["zh", "en", "ar", "ja", "ko", "fr", "ru"]).optional() })).mutation(async ({ input, ctx }) => { const db2 = await getMongoDb(); if (!db2) throw new TRPCError3({ code: "INTERNAL_SERVER_ERROR" }); const rule = await db2.collection(COLLECTIONS.COMPLIANCE_RULES).findOne({ _id: new ObjectId(input.id) }); if (!rule) throw new TRPCError3({ code: "NOT_FOUND", message: "\u89C4\u5219\u4E0D\u5B58\u5728" }); const translations = await migrateRuleToMultiLang({ ruleName: rule.ruleName, description: rule.description, ruleNameI18n: rule.ruleNameI18n, descriptionI18n: rule.descriptionI18n }); await db2.collection(COLLECTIONS.COMPLIANCE_RULES).updateOne( { _id: new ObjectId(input.id) }, { $set: { ruleNameI18n: translations.ruleNameI18n, descriptionI18n: translations.descriptionI18n, updatedAt: /* @__PURE__ */ new Date() } } ); await writeAuditLog("TRANSLATE_RULE", ctx.nacUser.id, ctx.nacUser.email, { ruleId: input.id }); return { success: true, translations }; }), // ─── 批量迁移现有规则到多语言格式 ──────────────────────────── migrateAllToMultiLang: nacAdminProcedure.mutation(async ({ ctx }) => { const db2 = await getMongoDb(); if (!db2) throw new TRPCError3({ code: "INTERNAL_SERVER_ERROR" }); const rules = await db2.collection(COLLECTIONS.COMPLIANCE_RULES).find({ $or: [{ ruleNameI18n: { $exists: false } }, { "ruleNameI18n.en": { $exists: false } }] }).toArray(); let migrated = 0; for (const rule of rules) { try { const translations = await migrateRuleToMultiLang({ ruleName: rule.ruleName, description: rule.description, ruleNameI18n: rule.ruleNameI18n, descriptionI18n: rule.descriptionI18n }); await db2.collection(COLLECTIONS.COMPLIANCE_RULES).updateOne( { _id: rule._id }, { $set: { ruleNameI18n: translations.ruleNameI18n, descriptionI18n: translations.descriptionI18n, updatedAt: /* @__PURE__ */ new Date() } } ); migrated++; } catch (e) { console.error(`[Migration] Failed for rule ${rule._id}:`, e.message); } } await writeAuditLog("MIGRATE_MULTILANG", ctx.nacUser.id, ctx.nacUser.email, { migratedCount: migrated }); return { success: true, migratedCount: migrated, totalRules: rules.length }; }), // // ─── 获取支持的语言列表 ────────────────────────────── getSupportedLanguages: nacAuthProcedure.query(() => { return SUPPORTED_LANGUAGES.map((lang) => ({ code: lang, name: { zh: "\u4E2D\u6587\uFF08\u7B80\u4F53\uFF09", en: "English", ar: "\u0627\u0644\u0639\u0631\u0628\u064A\u0629", ja: "\u65E5\u672C\u8A9E", ko: "\uD55C\uAD6D\uC5B4", fr: "Fran\xE7ais", ru: "\u0420\u0443\u0441\u0441\u043A\u0438\u0439" }[lang], isRTL: isRTL(lang) })); }), // ─── AI翻译服务状态检查 ─────────────────────────── aiStatus: nacAuthProcedure.query(() => { const configured = isAiTranslationConfigured(); return { configured, apiUrl: configured ? (process.env.NAC_AI_API_URL || "").replace(/\/+$/, "") : null, model: process.env.NAC_AI_MODEL || "gpt-3.5-turbo", message: configured ? "AI\u7FFB\u8BD1\u670D\u52A1\u5DF2\u914D\u7F6E\uFF0C\u53EF\u4EE5\u4F7F\u7528\u81EA\u52A8\u7FFB\u8BD1\u529F\u80FD" : "AI\u7FFB\u8BD1\u670D\u52A1\u672A\u914D\u7F6E\u3002\u8BF7\u5728\u670D\u52A1\u5668 .env \u4E2D\u8BBE\u7F6E NAC_AI_API_URL \u548C NAC_AI_API_KEY" }; }), // ─── 阿拉伯语RTL专项测试 ──────────────────────────── testArabicRTL: nacAdminProcedure.mutation(async () => { const report = await runArabicRTLTests(); return report; }) }), // ─── 采集器监控与管理 ──────────────────────────────────────────── crawler: router({ list: nacAuthProcedure.query(async () => { const db2 = await getMongoDb(); if (!db2) return []; return db2.collection(COLLECTIONS.CRAWLERS).find({}).sort({ createdAt: -1 }).toArray(); }), logs: nacAuthProcedure.input(z2.object({ crawlerId: z2.string().optional(), limit: z2.number().default(50) })).query(async ({ input }) => { const db2 = await getMongoDb(); if (!db2) return []; const filter = {}; if (input.crawlerId) filter.crawlerId = input.crawlerId; return db2.collection(COLLECTIONS.CRAWLER_LOGS).find(filter).sort({ timestamp: -1 }).limit(input.limit).toArray(); }), trigger: nacAdminProcedure.input(z2.object({ crawlerId: z2.string() })).mutation(async ({ input, ctx }) => { const db2 = await getMongoDb(); if (!db2) throw new TRPCError3({ code: "INTERNAL_SERVER_ERROR" }); const crawler = await db2.collection(COLLECTIONS.CRAWLERS).findOne({ _id: new ObjectId(input.crawlerId) }); if (!crawler) throw new TRPCError3({ code: "NOT_FOUND", message: "\u91C7\u96C6\u5668\u4E0D\u5B58\u5728" }); await db2.collection(COLLECTIONS.CRAWLER_LOGS).insertOne({ crawlerId: input.crawlerId, crawlerName: crawler.name, action: "manual_trigger", status: "triggered", message: "\u7BA1\u7406\u5458\u624B\u52A8\u89E6\u53D1\u91C7\u96C6\u4EFB\u52A1", timestamp: /* @__PURE__ */ new Date() }); await db2.collection(COLLECTIONS.CRAWLERS).updateOne({ _id: new ObjectId(input.crawlerId) }, { $set: { lastRun: /* @__PURE__ */ new Date() } }); await writeAuditLog("TRIGGER_CRAWLER", ctx.nacUser.id, ctx.nacUser.email, { crawlerId: input.crawlerId, crawlerName: crawler.name }); return { success: true, message: `\u91C7\u96C6\u5668 "${crawler.name}" \u5DF2\u89E6\u53D1` }; }), create: nacAdminProcedure.input(z2.object({ name: z2.string(), jurisdiction: z2.string(), type: z2.enum(["internal", "external"]), source: z2.string(), category: z2.string(), frequency: z2.string() })).mutation(async ({ input, ctx }) => { const db2 = await getMongoDb(); if (!db2) throw new TRPCError3({ code: "INTERNAL_SERVER_ERROR" }); const result = await db2.collection(COLLECTIONS.CRAWLERS).insertOne({ ...input, status: "active", lastRun: null, successRate: 0, totalCollected: 0, createdAt: /* @__PURE__ */ new Date() }); await writeAuditLog("CREATE_CRAWLER", ctx.nacUser.id, ctx.nacUser.email, { crawlerName: input.name }); return { id: result.insertedId }; }) }), // ─── AI审批案例审查 ────────────────────────────────────────────── approvalCase: router({ list: nacAuthProcedure.input(z2.object({ status: z2.string().optional(), riskLevel: z2.string().optional(), page: z2.number().default(1), pageSize: z2.number().default(20) })).query(async ({ input }) => { const db2 = await getMongoDb(); if (!db2) return { items: [], total: 0 }; const filter = {}; if (input.status) filter.status = input.status; if (input.riskLevel) filter.riskLevel = input.riskLevel; const skip = (input.page - 1) * input.pageSize; const [items, total] = await Promise.all([ db2.collection(COLLECTIONS.APPROVAL_CASES).find(filter).sort({ createdAt: -1 }).skip(skip).limit(input.pageSize).toArray(), db2.collection(COLLECTIONS.APPROVAL_CASES).countDocuments(filter) ]); return { items, total }; }), review: nacAuthProcedure.input(z2.object({ id: z2.string(), decision: z2.enum(["approved", "rejected"]), comment: z2.string().optional() })).mutation(async ({ input, ctx }) => { const db2 = await getMongoDb(); if (!db2) throw new TRPCError3({ code: "INTERNAL_SERVER_ERROR" }); const nacUser = ctx.nacUser; await db2.collection(COLLECTIONS.APPROVAL_CASES).updateOne( { _id: new ObjectId(input.id) }, { $set: { status: "reviewed", decision: input.decision, reviewComment: input.comment, reviewedBy: nacUser.email, reviewedAt: /* @__PURE__ */ new Date() } } ); await writeAuditLog("REVIEW_CASE", nacUser.id, nacUser.email, { caseId: input.id, decision: input.decision }); return { success: true }; }) }), // ─── 标签与规则引擎治理 ────────────────────────────────────────── tagEngine: router({ listRules: nacAuthProcedure.query(async () => { const db2 = await getMongoDb(); if (!db2) return []; return db2.collection(COLLECTIONS.TAG_RULES).find({}).sort({ createdAt: -1 }).toArray(); }), correctTag: nacAuthProcedure.input(z2.object({ documentId: z2.string(), originalTags: z2.array(z2.string()), correctedTags: z2.array(z2.string()), reason: z2.string() })).mutation(async ({ input, ctx }) => { const db2 = await getMongoDb(); if (!db2) throw new TRPCError3({ code: "INTERNAL_SERVER_ERROR" }); const nacUser = ctx.nacUser; await db2.collection(COLLECTIONS.TAG_RULES).insertOne({ type: "correction", documentId: input.documentId, originalTags: input.originalTags, correctedTags: input.correctedTags, reason: input.reason, correctedBy: nacUser.email, isTrainingData: true, createdAt: /* @__PURE__ */ new Date() }); await writeAuditLog("CORRECT_TAG", nacUser.id, nacUser.email, { documentId: input.documentId }); return { success: true }; }), createRule: nacAdminProcedure.input(z2.object({ keyword: z2.string(), tags: z2.array(z2.string()), dimension: z2.string(), description: z2.string() })).mutation(async ({ input, ctx }) => { const db2 = await getMongoDb(); if (!db2) throw new TRPCError3({ code: "INTERNAL_SERVER_ERROR" }); const result = await db2.collection(COLLECTIONS.TAG_RULES).insertOne({ ...input, type: "rule", status: "active", createdAt: /* @__PURE__ */ new Date() }); await writeAuditLog("CREATE_TAG_RULE", ctx.nacUser.id, ctx.nacUser.email, { keyword: input.keyword }); return { id: result.insertedId }; }) }), // ─── 协议族注册表管理 ──────────────────────────────────────────── protocolRegistry: router({ list: nacAuthProcedure.query(async () => { const db2 = await getMongoDb(); if (!db2) return []; return db2.collection(COLLECTIONS.PROTOCOL_REGISTRY).find({}).sort({ createdAt: -1 }).toArray(); }), register: nacAdminProcedure.input(z2.object({ name: z2.string(), type: z2.string(), version: z2.string(), endpoint: z2.string(), trigger: z2.string(), description: z2.string().optional() })).mutation(async ({ input, ctx }) => { const db2 = await getMongoDb(); if (!db2) throw new TRPCError3({ code: "INTERNAL_SERVER_ERROR" }); const result = await db2.collection(COLLECTIONS.PROTOCOL_REGISTRY).insertOne({ ...input, status: "active", createdAt: /* @__PURE__ */ new Date() }); await writeAuditLog("REGISTER_PROTOCOL", ctx.nacUser.id, ctx.nacUser.email, { protocolName: input.name }); return { id: result.insertedId }; }), toggleStatus: nacAdminProcedure.input(z2.object({ id: z2.string(), status: z2.enum(["active", "disabled", "deprecated"]) })).mutation(async ({ input, ctx }) => { const db2 = await getMongoDb(); if (!db2) throw new TRPCError3({ code: "INTERNAL_SERVER_ERROR" }); await db2.collection(COLLECTIONS.PROTOCOL_REGISTRY).updateOne({ _id: new ObjectId(input.id) }, { $set: { status: input.status, updatedAt: /* @__PURE__ */ new Date() } }); await writeAuditLog("TOGGLE_PROTOCOL", ctx.nacUser.id, ctx.nacUser.email, { protocolId: input.id, newStatus: input.status }); return { success: true }; }), updateVersion: nacAdminProcedure.input(z2.object({ id: z2.string(), version: z2.string(), trigger: z2.string().optional() })).mutation(async ({ input, ctx }) => { const db2 = await getMongoDb(); if (!db2) throw new TRPCError3({ code: "INTERNAL_SERVER_ERROR" }); const update = { version: input.version, updatedAt: /* @__PURE__ */ new Date() }; if (input.trigger) update.trigger = input.trigger; await db2.collection(COLLECTIONS.PROTOCOL_REGISTRY).updateOne({ _id: new ObjectId(input.id) }, { $set: update }); await writeAuditLog("UPDATE_PROTOCOL_VERSION", ctx.nacUser.id, ctx.nacUser.email, { protocolId: input.id, version: input.version }); return { success: true }; }) }), // ─── 权限与审计管理 ────────────────────────────────────────────── rbac: router({ listUsers: nacAdminProcedure.input(z2.object({ page: z2.number().default(1), pageSize: z2.number().default(20) })).query(async ({ input }) => { const offset = (input.page - 1) * input.pageSize; const [users, total] = await Promise.all([listNacUsers(input.pageSize, offset), getNacUserCount()]); return { users, total }; }), auditLogs: nacAdminProcedure.input(z2.object({ action: z2.string().optional(), userId: z2.number().optional(), page: z2.number().default(1), pageSize: z2.number().default(50) })).query(async ({ input }) => { const db2 = await getMongoDb(); if (!db2) return { items: [], total: 0 }; const filter = {}; if (input.action) filter.action = input.action; if (input.userId) filter.userId = input.userId; const skip = (input.page - 1) * input.pageSize; const [items, total] = await Promise.all([ db2.collection(COLLECTIONS.AUDIT_LOGS).find(filter).sort({ timestamp: -1 }).skip(skip).limit(input.pageSize).toArray(), db2.collection(COLLECTIONS.AUDIT_LOGS).countDocuments(filter) ]); return { items, total }; }) }) }); // server/_core/context.ts async function createContext(opts) { let user = null; try { const cookies = opts.req.cookies || {}; const token = cookies["nac_admin_token"] || opts.req.headers["x-nac-token"]; if (token) { const payload = verifyNacToken(token); if (payload) { const nacUser = await getNacUserById(payload.id); if (nacUser) { user = { id: nacUser.id, email: nacUser.email, role: nacUser.role, openId: nacUser.email // 兼容性字段 }; } } } } catch (error) { user = null; } return { req: opts.req, res: opts.res, user }; } // server/_core/index.ts function isPortAvailable(port) { return new Promise((resolve) => { const server = net.createServer(); server.listen(port, () => { server.close(() => resolve(true)); }); server.on("error", () => resolve(false)); }); } async function findAvailablePort(startPort = 3e3) { for (let port = startPort; port < startPort + 20; port++) { if (await isPortAvailable(port)) { return port; } } throw new Error(`No available port found starting from ${startPort}`); } async function startServer() { const app = express3(); const server = createServer(app); app.set("trust proxy", 1); app.use(cookieParser()); app.use(express3.json({ limit: "50mb" })); app.use(express3.urlencoded({ limit: "50mb", extended: true })); app.use( "/api/trpc", createExpressMiddleware({ router: appRouter, createContext }) ); if (process.env.NODE_ENV === "development") { const { setupVite: setupVite2 } = await Promise.resolve().then(() => (init_vite(), vite_exports)); await setupVite2(app, server); } else { const { serveStatic: serveStatic3 } = await Promise.resolve().then(() => (init_static(), static_exports)); serveStatic3(app); } const preferredPort = parseInt(process.env.PORT || "3000"); const port = await findAvailablePort(preferredPort); if (port !== preferredPort) { console.log(`Port ${preferredPort} is busy, using port ${port} instead`); } server.listen(port, () => { console.log(`[NAC Admin] Server running on http://localhost:${port}/`); }); } startServer().catch(console.error);