// server/_core/index.ts import "dotenv/config"; import express2 from "express"; import { createServer } from "http"; import net from "net"; import { createExpressMiddleware } from "@trpc/server/adapters/express"; // shared/const.ts var COOKIE_NAME = "app_session_id"; var ONE_YEAR_MS = 1e3 * 60 * 60 * 24 * 365; var AXIOS_TIMEOUT_MS = 3e4; var UNAUTHED_ERR_MSG = "Please login (10001)"; var NOT_ADMIN_ERR_MSG = "You do not have required permission (10002)"; // server/db.ts import { eq } from "drizzle-orm"; import { drizzle } from "drizzle-orm/mysql2"; // drizzle/schema.ts import { int, mysqlEnum, mysqlTable, text, timestamp, varchar } from "drizzle-orm/mysql-core"; var users = mysqlTable("users", { /** * Surrogate primary key. Auto-incremented numeric value managed by the database. * Use this for relations between tables. */ id: int("id").autoincrement().primaryKey(), /** NAC_AI OAuth identifier (openId) returned from the OAuth callback. Unique per user. */ openId: varchar("openId", { length: 64 }).notNull().unique(), name: text("name"), email: varchar("email", { length: 320 }), loginMethod: varchar("loginMethod", { length: 64 }), role: mysqlEnum("role", ["user", "admin"]).default("user").notNull(), createdAt: timestamp("createdAt").defaultNow().notNull(), updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(), lastSignedIn: timestamp("lastSignedIn").defaultNow().notNull() }); // server/_core/env.ts var ENV = { appId: process.env.VITE_APP_ID ?? "", cookieSecret: process.env.JWT_SECRET ?? "", databaseUrl: process.env.DATABASE_URL ?? "", oAuthServerUrl: process.env.OAUTH_SERVER_URL ?? "", ownerOpenId: process.env.OWNER_OPEN_ID ?? "", isProduction: process.env.NODE_ENV === "production", forgeApiUrl: process.env.BUILT_IN_FORGE_API_URL ?? "", forgeApiKey: process.env.BUILT_IN_FORGE_API_KEY ?? "" // 注意:NAC数据库密钥请通过 server/secrets.ts 中的函数访问 // 例如:getNacMysqlUrl(), getNacMongoUrl(), getNacJwtSecret() }; // server/db.ts var _db = null; async function getDb() { if (!_db && process.env.DATABASE_URL) { try { _db = drizzle(process.env.DATABASE_URL); } catch (error) { console.warn("[Database] Failed to connect:", error); _db = null; } } return _db; } async function upsertUser(user) { if (!user.openId) { throw new Error("User openId is required for upsert"); } const db2 = await getDb(); if (!db2) { console.warn("[Database] Cannot upsert user: database not available"); return; } try { const values = { openId: user.openId }; const updateSet = {}; const textFields = ["name", "email", "loginMethod"]; const assignNullable = (field) => { const value = user[field]; if (value === void 0) return; const normalized = value ?? null; values[field] = normalized; updateSet[field] = normalized; }; textFields.forEach(assignNullable); if (user.lastSignedIn !== void 0) { values.lastSignedIn = user.lastSignedIn; updateSet.lastSignedIn = user.lastSignedIn; } if (user.role !== void 0) { values.role = user.role; updateSet.role = user.role; } else if (user.openId === ENV.ownerOpenId) { values.role = "admin"; updateSet.role = "admin"; } if (!values.lastSignedIn) { values.lastSignedIn = /* @__PURE__ */ new Date(); } if (Object.keys(updateSet).length === 0) { updateSet.lastSignedIn = /* @__PURE__ */ new Date(); } await db2.insert(users).values(values).onDuplicateKeyUpdate({ set: updateSet }); } catch (error) { console.error("[Database] Failed to upsert user:", error); throw error; } } async function getUserByOpenId(openId) { const db2 = await getDb(); if (!db2) { console.warn("[Database] Cannot get user: database not available"); return void 0; } const result = await db2.select().from(users).where(eq(users.openId, openId)).limit(1); return result.length > 0 ? result[0] : void 0; } // 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) }; } // shared/_core/errors.ts var HttpError = class extends Error { constructor(statusCode, message) { super(message); this.statusCode = statusCode; this.name = "HttpError"; } }; var ForbiddenError = (msg) => new HttpError(403, msg); // server/_core/sdk.ts import axios from "axios"; import { parse as parseCookieHeader } from "cookie"; import { SignJWT, jwtVerify } from "jose"; var isNonEmptyString = (value) => typeof value === "string" && value.length > 0; var EXCHANGE_TOKEN_PATH = `/webdev.v1.WebDevAuthPublicService/ExchangeToken`; var GET_USER_INFO_PATH = `/webdev.v1.WebDevAuthPublicService/GetUserInfo`; var GET_USER_INFO_WITH_JWT_PATH = `/webdev.v1.WebDevAuthPublicService/GetUserInfoWithJwt`; var OAuthService = class { constructor(client2) { this.client = client2; console.log("[OAuth] Initialized with baseURL:", ENV.oAuthServerUrl); if (!ENV.oAuthServerUrl) { console.error( "[OAuth] ERROR: OAUTH_SERVER_URL is not configured! Set OAUTH_SERVER_URL environment variable." ); } } decodeState(state) { const redirectUri = atob(state); return redirectUri; } async getTokenByCode(code, state) { const payload = { clientId: ENV.appId, grantType: "authorization_code", code, redirectUri: this.decodeState(state) }; const { data } = await this.client.post( EXCHANGE_TOKEN_PATH, payload ); return data; } async getUserInfoByToken(token) { const { data } = await this.client.post( GET_USER_INFO_PATH, { accessToken: token.accessToken } ); return data; } }; var createOAuthHttpClient = () => axios.create({ baseURL: ENV.oAuthServerUrl, timeout: AXIOS_TIMEOUT_MS }); var SDKServer = class { client; oauthService; constructor(client2 = createOAuthHttpClient()) { this.client = client2; this.oauthService = new OAuthService(this.client); } deriveLoginMethod(platforms, fallback) { if (fallback && fallback.length > 0) return fallback; if (!Array.isArray(platforms) || platforms.length === 0) return null; const set = new Set( platforms.filter((p) => typeof p === "string") ); if (set.has("REGISTERED_PLATFORM_EMAIL")) return "email"; if (set.has("REGISTERED_PLATFORM_GOOGLE")) return "google"; if (set.has("REGISTERED_PLATFORM_APPLE")) return "apple"; if (set.has("REGISTERED_PLATFORM_MICROSOFT") || set.has("REGISTERED_PLATFORM_AZURE")) return "microsoft"; if (set.has("REGISTERED_PLATFORM_GITHUB")) return "github"; const first = Array.from(set)[0]; return first ? first.toLowerCase() : null; } /** * Exchange OAuth authorization code for access token * @example * const tokenResponse = await sdk.exchangeCodeForToken(code, state); */ async exchangeCodeForToken(code, state) { return this.oauthService.getTokenByCode(code, state); } /** * Get user information using access token * @example * const userInfo = await sdk.getUserInfo(tokenResponse.accessToken); */ async getUserInfo(accessToken) { const data = await this.oauthService.getUserInfoByToken({ accessToken }); const loginMethod = this.deriveLoginMethod( data?.platforms, data?.platform ?? data.platform ?? null ); return { ...data, platform: loginMethod, loginMethod }; } parseCookies(cookieHeader) { if (!cookieHeader) { return /* @__PURE__ */ new Map(); } const parsed = parseCookieHeader(cookieHeader); return new Map(Object.entries(parsed)); } getSessionSecret() { const secret = ENV.cookieSecret; return new TextEncoder().encode(secret); } /** * Create a session token for a NAC_AI user openId * @example * const sessionToken = await sdk.createSessionToken(userInfo.openId); */ async createSessionToken(openId, options = {}) { return this.signSession( { openId, appId: ENV.appId, name: options.name || "" }, options ); } async signSession(payload, options = {}) { const issuedAt = Date.now(); const expiresInMs = options.expiresInMs ?? ONE_YEAR_MS; const expirationSeconds = Math.floor((issuedAt + expiresInMs) / 1e3); const secretKey = this.getSessionSecret(); return new SignJWT({ openId: payload.openId, appId: payload.appId, name: payload.name }).setProtectedHeader({ alg: "HS256", typ: "JWT" }).setExpirationTime(expirationSeconds).sign(secretKey); } async verifySession(cookieValue) { if (!cookieValue) { console.warn("[Auth] Missing session cookie"); return null; } try { const secretKey = this.getSessionSecret(); const { payload } = await jwtVerify(cookieValue, secretKey, { algorithms: ["HS256"] }); const { openId, appId, name } = payload; if (!isNonEmptyString(openId) || !isNonEmptyString(appId) || !isNonEmptyString(name)) { console.warn("[Auth] Session payload missing required fields"); return null; } return { openId, appId, name }; } catch (error) { console.warn("[Auth] Session verification failed", String(error)); return null; } } async getUserInfoWithJwt(jwtToken) { const payload = { jwtToken, projectId: ENV.appId }; const { data } = await this.client.post( GET_USER_INFO_WITH_JWT_PATH, payload ); const loginMethod = this.deriveLoginMethod( data?.platforms, data?.platform ?? data.platform ?? null ); return { ...data, platform: loginMethod, loginMethod }; } async authenticateRequest(req) { const cookies = this.parseCookies(req.headers.cookie); const sessionCookie = cookies.get(COOKIE_NAME); const session = await this.verifySession(sessionCookie); if (!session) { throw ForbiddenError("Invalid session cookie"); } const sessionUserId = session.openId; const signedInAt = /* @__PURE__ */ new Date(); let user = await getUserByOpenId(sessionUserId); if (!user) { try { const userInfo = await this.getUserInfoWithJwt(sessionCookie ?? ""); await upsertUser({ openId: userInfo.openId, name: userInfo.name || null, email: userInfo.email ?? null, loginMethod: userInfo.loginMethod ?? userInfo.platform ?? null, lastSignedIn: signedInAt }); user = await getUserByOpenId(userInfo.openId); } catch (error) { console.error("[Auth] Failed to sync user from OAuth:", error); throw ForbiddenError("Failed to sync user info"); } } if (!user) { throw ForbiddenError("User not found"); } await upsertUser({ openId: user.openId, lastSignedIn: signedInAt }); return user; } }; var sdk = new SDKServer(); // server/_core/oauth.ts function getQueryParam(req, key) { const value = req.query[key]; return typeof value === "string" ? value : void 0; } function registerOAuthRoutes(app) { app.get("/api/oauth/callback", async (req, res) => { const code = getQueryParam(req, "code"); const state = getQueryParam(req, "state"); if (!code || !state) { res.status(400).json({ error: "code and state are required" }); return; } try { const tokenResponse = await sdk.exchangeCodeForToken(code, state); const userInfo = await sdk.getUserInfo(tokenResponse.accessToken); if (!userInfo.openId) { res.status(400).json({ error: "openId missing from user info" }); return; } await upsertUser({ openId: userInfo.openId, name: userInfo.name || null, email: userInfo.email ?? null, loginMethod: userInfo.loginMethod ?? userInfo.platform ?? null, lastSignedIn: /* @__PURE__ */ new Date() }); const sessionToken = await sdk.createSessionToken(userInfo.openId, { name: userInfo.name || "", expiresInMs: ONE_YEAR_MS }); const cookieOptions = getSessionCookieOptions(req); res.cookie(COOKIE_NAME, sessionToken, { ...cookieOptions, maxAge: ONE_YEAR_MS }); res.redirect(302, "/"); } catch (error) { console.error("[OAuth] Callback failed", error); res.status(500).json({ error: "OAuth callback failed" }); } }); } // server/routers.ts import { z as z2 } from "zod"; import { TRPCError as TRPCError3 } from "@trpc/server"; // 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 isNonEmptyString2 = (value) => typeof value === "string" && value.trim().length > 0; var buildEndpointUrl = (baseUrl) => { const normalizedBase = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`; return new URL( "webdevtoken.v1.WebDevService/SendNotification", normalizedBase ).toString(); }; var validatePayload = (input) => { if (!isNonEmptyString2(input.title)) { throw new TRPCError2({ code: "BAD_REQUEST", message: "Notification title is required." }); } if (!isNonEmptyString2(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); if (!ENV.forgeApiUrl) { throw new TRPCError2({ code: "INTERNAL_SERVER_ERROR", message: "Notification service URL is not configured." }); } if (!ENV.forgeApiKey) { throw new TRPCError2({ code: "INTERNAL_SERVER_ERROR", message: "Notification service API key is not configured." }); } const endpoint = buildEndpointUrl(ENV.forgeApiUrl); try { const response = await fetch(endpoint, { method: "POST", headers: { accept: "application/json", authorization: `Bearer ${ENV.forgeApiKey}`, "content-type": "application/json", "connect-protocol-version": "1" }, body: JSON.stringify({ title, content }) }); if (!response.ok) { const detail = await response.text().catch(() => ""); console.warn( `[Notification] Failed to notify owner (${response.status} ${response.statusText})${detail ? `: ${detail}` : ""}` ); return false; } return true; } catch (error) { console.warn("[Notification] Error calling notification service:", error); return false; } } // 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 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"; 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", 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", 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", 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", 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", 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", required: true, status: "active", tags: ["AE", "RealEstate", "DLD", "Document"], createdAt: /* @__PURE__ */ new Date() } ]); } } var appRouter = router({ system: systemRouter, // ─── NAC原生认证(不使用NAC_AI 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) })).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) ]); return { items, 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()) })).mutation(async ({ input, ctx }) => { const db2 = await getMongoDb(); if (!db2) throw new TRPCError3({ code: "INTERNAL_SERVER_ERROR" }); const result = await db2.collection(COLLECTIONS.COMPLIANCE_RULES).insertOne({ ...input, status: "active", createdAt: /* @__PURE__ */ new Date() }); await writeAuditLog("CREATE_RULE", ctx.nacUser.id, ctx.nacUser.email, { ruleId: result.insertedId, ruleName: input.ruleName }); 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() }) })).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: { ...input.data, updatedAt: /* @__PURE__ */ new Date() } }); 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 }; }) }), // ─── 采集器监控与管理 ──────────────────────────────────────────── 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 [users2, total] = await Promise.all([listNacUsers(input.pageSize, offset), getNacUserCount()]); return { users: users2, 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 { user = await sdk.authenticateRequest(opts.req); } catch (error) { user = null; } return { req: opts.req, res: opts.res, user }; } // server/_core/vite.ts import express from "express"; import fs2 from "fs"; import { nanoid } from "nanoid"; import path2 from "path"; import { createServer as createViteServer } from "vite"; // vite.config.ts import { jsxLocPlugin } from "@builder.io/vite-plugin-jsx-loc"; import tailwindcss from "@tailwindcss/vite"; import react from "@vitejs/plugin-react"; import fs from "node:fs"; import path from "node:path"; import { defineConfig } from "vite"; import { vitePluginNAC_AIRuntime } from "vite-plugin-NAC_AI-runtime"; var PROJECT_ROOT = import.meta.dirname; var LOG_DIR = path.join(PROJECT_ROOT, ".NAC_AI-logs"); var MAX_LOG_SIZE_BYTES = 1 * 1024 * 1024; var TRIM_TARGET_BYTES = Math.floor(MAX_LOG_SIZE_BYTES * 0.6); function ensureLogDir() { if (!fs.existsSync(LOG_DIR)) { fs.mkdirSync(LOG_DIR, { recursive: true }); } } function trimLogFile(logPath, maxSize) { try { if (!fs.existsSync(logPath) || fs.statSync(logPath).size <= maxSize) { return; } const lines = fs.readFileSync(logPath, "utf-8").split("\n"); const keptLines = []; let keptBytes = 0; const targetSize = TRIM_TARGET_BYTES; for (let i = lines.length - 1; i >= 0; i--) { const lineBytes = Buffer.byteLength(`${lines[i]} `, "utf-8"); if (keptBytes + lineBytes > targetSize) break; keptLines.unshift(lines[i]); keptBytes += lineBytes; } fs.writeFileSync(logPath, keptLines.join("\n"), "utf-8"); } catch { } } function writeToLogFile(source, entries) { if (entries.length === 0) return; ensureLogDir(); const logPath = path.join(LOG_DIR, `${source}.log`); const lines = entries.map((entry) => { const ts = (/* @__PURE__ */ new Date()).toISOString(); return `[${ts}] ${JSON.stringify(entry)}`; }); fs.appendFileSync(logPath, `${lines.join("\n")} `, "utf-8"); trimLogFile(logPath, MAX_LOG_SIZE_BYTES); } function vitePluginNAC_AIDebugCollector() { return { name: "NAC_AI-debug-collector", transformIndexHtml(html) { if (process.env.NODE_ENV === "production") { return html; } return { html, tags: [ { tag: "script", attrs: { defer: true }, injectTo: "head" } ] }; }, configureServer(server) { server.middlewares.use("/__NAC_AI__/logs", (req, res, next) => { if (req.method !== "POST") { return next(); } const handlePayload = (payload) => { if (payload.consoleLogs?.length > 0) { writeToLogFile("browserConsole", payload.consoleLogs); } if (payload.networkRequests?.length > 0) { writeToLogFile("networkRequests", payload.networkRequests); } if (payload.sessionEvents?.length > 0) { writeToLogFile("sessionReplay", payload.sessionEvents); } res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify({ success: true })); }; const reqBody = req.body; if (reqBody && typeof reqBody === "object") { try { handlePayload(reqBody); } catch (e) { res.writeHead(400, { "Content-Type": "application/json" }); res.end(JSON.stringify({ success: false, error: String(e) })); } return; } let body = ""; req.on("data", (chunk) => { body += chunk.toString(); }); req.on("end", () => { try { const payload = JSON.parse(body); handlePayload(payload); } catch (e) { res.writeHead(400, { "Content-Type": "application/json" }); res.end(JSON.stringify({ success: false, error: String(e) })); } }); }); } }; } var plugins = [react(), tailwindcss(), jsxLocPlugin(), vitePluginNAC_AIRuntime(), vitePluginNAC_AIDebugCollector()]; var vite_config_default = defineConfig({ plugins, resolve: { alias: { "@": path.resolve(import.meta.dirname, "client", "src"), "@shared": path.resolve(import.meta.dirname, "shared"), "@assets": path.resolve(import.meta.dirname, "attached_assets") } }, envDir: path.resolve(import.meta.dirname), root: path.resolve(import.meta.dirname, "client"), publicDir: path.resolve(import.meta.dirname, "client", "public"), build: { outDir: path.resolve(import.meta.dirname, "dist/public"), emptyOutDir: true }, server: { host: true, allowedHosts: [ "localhost", "127.0.0.1" ], fs: { strict: true, deny: ["**/.*"] } } }); // server/_core/vite.ts async function setupVite(app, server) { const serverOptions = { middlewareMode: true, hmr: { server }, allowedHosts: true }; const vite = await createViteServer({ ...vite_config_default, configFile: false, server: serverOptions, appType: "custom" }); app.use(vite.middlewares); app.use("*", async (req, res, next) => { const url = req.originalUrl; try { const clientTemplate = path2.resolve( import.meta.dirname, "../..", "client", "index.html" ); let template = await fs2.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 distPath = process.env.NODE_ENV === "development" ? path2.resolve(import.meta.dirname, "../..", "dist", "public") : path2.resolve(import.meta.dirname, "public"); if (!fs2.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(path2.resolve(distPath, "index.html")); }); } // 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 = express2(); const server = createServer(app); app.use(express2.json({ limit: "50mb" })); app.use(express2.urlencoded({ limit: "50mb", extended: true })); registerOAuthRoutes(app); app.use( "/api/trpc", createExpressMiddleware({ router: appRouter, createContext }) ); if (process.env.NODE_ENV === "development") { await setupVite(app, server); } else { serveStatic(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(`Server running on http://localhost:${port}/`); }); } startServer().catch(console.error);