Files
LWLT-AI/mock-business-system/server.mjs
2026-07-13 19:57:46 +08:00

151 lines
4.9 KiB
JavaScript

#!/usr/bin/env node
import { createServer } from 'node:http';
import { readFile } from 'node:fs/promises';
import { extname, relative, resolve, sep } from 'node:path';
import { fileURLToPath } from 'node:url';
import {
DEFAULT_BASE_URL,
ExternalAgentParser,
blockedResult
} from './external-agent-client.mjs';
const rootDir = resolve(import.meta.dirname);
const port = Number(process.env.PORT || 8765);
const contentTypes = {
'.css': 'text/css; charset=utf-8',
'.html': 'text/html; charset=utf-8',
'.js': 'text/javascript; charset=utf-8',
'.json': 'application/json; charset=utf-8',
'.md': 'text/markdown; charset=utf-8',
'.mjs': 'text/javascript; charset=utf-8',
'.png': 'image/png'
};
export function createExternalParserFromEnv({ fetchImpl = globalThis.fetch } = {}) {
return new ExternalAgentParser({
baseUrl: process.env.DEERFLOW_BASE_URL || DEFAULT_BASE_URL,
apiKey: process.env.DEERFLOW_OPEN_API_KEY,
fetchImpl,
timeoutMs: parseTimeout(process.env.PARSER_REQUEST_TIMEOUT_MS),
totalTimeoutMs: parseTimeout(process.env.PARSER_TOTAL_TIMEOUT_MS || '180000')
});
}
export function createBusinessServer({ parser = createExternalParserFromEnv() } = {}) {
return createServer(async (req, res) => {
try {
const requestUrl = new URL(req.url || '/', `http://${req.headers.host || '127.0.0.1'}`);
if (req.method === 'GET' && requestUrl.pathname === '/api/status') {
const aiProbe = typeof parser.checkConnection === 'function'
? await parser.checkConnection()
: {
ok: Boolean(process.env.DEERFLOW_OPEN_API_KEY),
configured: Boolean(process.env.DEERFLOW_OPEN_API_KEY),
reachable: false,
error_code: 'status_probe_unavailable'
};
sendJson(res, 200, {
ok: true,
service: 'external_parse_api',
ai_configured: Boolean(aiProbe.configured),
ai_connected: Boolean(aiProbe.ok),
ai_probe: aiProbe
});
return;
}
if (req.method === 'POST' && requestUrl.pathname === '/api/parse') {
const payload = await readRequestJson(req);
const result = await parser.parse({
rawText: payload.raw_text,
taskId: payload.task_id,
receivedAt: payload.received_at
});
sendJson(res, 200, result);
return;
}
if (req.method !== 'GET' && req.method !== 'HEAD') {
sendJson(res, 405, { error: 'method_not_allowed' });
return;
}
await serveStatic(req, res, requestUrl);
} catch (error) {
if (error?.code === 'invalid_json') {
sendJson(res, 400, blockedResult(['请求体不是有效 JSON。'], { error_code: error.code }));
return;
}
sendJson(res, 500, {
error: 'server_error',
message: error.message || String(error)
});
}
});
}
export async function readRequestJson(req) {
const chunks = [];
for await (const chunk of req) chunks.push(chunk);
const body = Buffer.concat(chunks).toString('utf8');
if (!body.trim()) return {};
try {
return JSON.parse(body);
} catch (error) {
const invalidJsonError = new Error('request body must be valid JSON');
invalidJsonError.code = 'invalid_json';
throw invalidJsonError;
}
}
async function serveStatic(req, res, requestUrl) {
const pathname = decodeURIComponent(requestUrl.pathname || '/');
const requestedPath = pathname === '/' ? 'index.html' : pathname.replace(/^\/+/, '');
const filePath = resolve(rootDir, requestedPath);
const relativePath = relative(rootDir, filePath);
if (relativePath.startsWith('..') || relativePath.includes(`..${sep}`)) {
res.writeHead(403);
res.end('Forbidden');
return;
}
try {
const body = await readFile(filePath);
res.writeHead(200, {
'Content-Type': contentTypes[extname(filePath)] || 'application/octet-stream',
'Content-Length': body.byteLength
});
if (req.method === 'HEAD') res.end();
else res.end(body);
} catch (error) {
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('Not found');
}
}
function sendJson(res, status, data) {
const body = `${JSON.stringify(data, null, 2)}\n`;
res.writeHead(status, {
'Content-Type': 'application/json; charset=utf-8',
'Content-Length': Buffer.byteLength(body)
});
res.end(body);
}
function parseTimeout(value) {
const timeout = Number(value);
return Number.isFinite(timeout) && timeout > 0 ? timeout : 120_000;
}
function isMainModule() {
return process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url);
}
if (isMainModule()) {
const server = createBusinessServer();
server.listen(port, '127.0.0.1', () => {
console.log(`Business system listening on http://127.0.0.1:${port}/`);
if (!process.env.DEERFLOW_OPEN_API_KEY) {
console.warn('External parsing service is not configured: set DEERFLOW_OPEN_API_KEY in the server environment.');
}
});
}