바코드 업데이트 및 AI LLm 적용용
This commit is contained in:
@@ -38,13 +38,16 @@ process.on("uncaughtException", (error: Error) => {
|
||||
// SIGTERM 시그널 처리 (Docker/Kubernetes 환경)
|
||||
process.on("SIGTERM", () => {
|
||||
logger.info("📴 SIGTERM 시그널 수신, graceful shutdown 시작...");
|
||||
// 여기서 연결 풀 정리 등 cleanup 로직 추가 가능
|
||||
const { stopAiAssistant } = require("./utils/startAiAssistant");
|
||||
stopAiAssistant();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
// SIGINT 시그널 처리 (Ctrl+C)
|
||||
process.on("SIGINT", () => {
|
||||
logger.info("📴 SIGINT 시그널 수신, graceful shutdown 시작...");
|
||||
const { stopAiAssistant } = require("./utils/startAiAssistant");
|
||||
stopAiAssistant();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
@@ -127,6 +130,7 @@ import cascadingHierarchyRoutes from "./routes/cascadingHierarchyRoutes"; // 다
|
||||
import categoryValueCascadingRoutes from "./routes/categoryValueCascadingRoutes"; // 카테고리 값 연쇄관계
|
||||
import categoryTreeRoutes from "./routes/categoryTreeRoutes"; // 카테고리 트리 (테스트)
|
||||
import processWorkStandardRoutes from "./routes/processWorkStandardRoutes"; // 공정 작업기준
|
||||
import aiAssistantProxy from "./routes/aiAssistantProxy"; // AI 어시스턴트 API 프록시 (같은 포트로 서비스)
|
||||
import { BatchSchedulerService } from "./services/batchSchedulerService";
|
||||
// import collectionRoutes from "./routes/collectionRoutes"; // 임시 주석
|
||||
// import batchRoutes from "./routes/batchRoutes"; // 임시 주석
|
||||
@@ -313,6 +317,7 @@ app.use("/api/category-value-cascading", categoryValueCascadingRoutes); // 카
|
||||
app.use("/api/category-tree", categoryTreeRoutes); // 카테고리 트리 (테스트)
|
||||
app.use("/api/process-work-standard", processWorkStandardRoutes); // 공정 작업기준
|
||||
app.use("/api", screenEmbeddingRoutes); // 화면 임베딩 및 데이터 전달
|
||||
app.use("/api/ai/v1", aiAssistantProxy); // AI 어시스턴트 (동일 서비스 내 프록시 → AI 서비스 포트)
|
||||
app.use("/api/vehicle", vehicleTripRoutes); // 차량 운행 이력 관리
|
||||
// app.use("/api/collections", collectionRoutes); // 임시 주석
|
||||
// app.use("/api/batch", batchRoutes); // 임시 주석
|
||||
@@ -398,6 +403,14 @@ app.listen(PORT, HOST, async () => {
|
||||
} catch (error) {
|
||||
logger.error(`❌ 메일 자동 삭제 스케줄러 시작 실패:`, error);
|
||||
}
|
||||
|
||||
// AI 어시스턴트 서비스 함께 기동 (한 번에 킬 가능)
|
||||
try {
|
||||
const { startAiAssistant } = await import("./utils/startAiAssistant");
|
||||
startAiAssistant();
|
||||
} catch (error) {
|
||||
logger.warn("⚠️ AI 어시스턴트 기동 스킵:", error);
|
||||
}
|
||||
});
|
||||
|
||||
export default app;
|
||||
|
||||
31
backend-node/src/routes/aiAssistantProxy.ts
Normal file
31
backend-node/src/routes/aiAssistantProxy.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* AI 어시스턴트 API 프록시
|
||||
* - /api/ai/v1/* 요청을 AI 서비스(기본 3100 포트)로 전달
|
||||
* - VEXPLOR와 같은 서비스로 쓰려면: 프론트(9771) → 백엔드(8080) → 여기서 3100으로 프록시
|
||||
*/
|
||||
import { createProxyMiddleware } from "http-proxy-middleware";
|
||||
import type { RequestHandler } from "express";
|
||||
|
||||
const AI_SERVICE_URL =
|
||||
process.env.AI_ASSISTANT_SERVICE_URL || "http://127.0.0.1:3100";
|
||||
|
||||
const aiAssistantProxy: RequestHandler = createProxyMiddleware({
|
||||
target: AI_SERVICE_URL,
|
||||
changeOrigin: true,
|
||||
pathRewrite: { "^/api/ai/v1": "/api/v1" },
|
||||
// 대상 서비스 미기동 시 502 등 에러 처리 (v3 타입에 없을 수 있음)
|
||||
onError: (_err, _req, res) => {
|
||||
if (!res.headersSent) {
|
||||
res.status(502).json({
|
||||
success: false,
|
||||
error: {
|
||||
code: "AI_SERVICE_UNAVAILABLE",
|
||||
message:
|
||||
"AI 어시스턴트 서비스를 사용할 수 없습니다. AI 서비스(기본 3100 포트)를 기동한 뒤 다시 시도하세요.",
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
} as Parameters<typeof createProxyMiddleware>[0]);
|
||||
|
||||
export default aiAssistantProxy;
|
||||
65
backend-node/src/utils/startAiAssistant.ts
Normal file
65
backend-node/src/utils/startAiAssistant.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* AI 어시스턴트 서비스를 자식 프로세스로 기동
|
||||
* - backend-node 서버 기동 시 함께 띄우고, 종료 시 함께 종료 (한 번에 킬)
|
||||
*/
|
||||
import path from "path";
|
||||
import { spawn, ChildProcess } from "child_process";
|
||||
import { logger } from "./logger";
|
||||
|
||||
const AI_PORT = process.env.AI_ASSISTANT_SERVICE_PORT || "3100";
|
||||
|
||||
let aiAssistantProcess: ChildProcess | null = null;
|
||||
|
||||
/** ERP-node/ai-assistant 경로 (backend-node 기준 상대) */
|
||||
function getAiAssistantDir(): string {
|
||||
return path.resolve(process.cwd(), "..", "ai-assistant");
|
||||
}
|
||||
|
||||
/**
|
||||
* AI 어시스턴트 서비스 기동 (있으면 띄움, 실패해도 backend는 계속 동작)
|
||||
*/
|
||||
export function startAiAssistant(): void {
|
||||
const aiDir = getAiAssistantDir();
|
||||
const appPath = path.join(aiDir, "src", "app.js");
|
||||
|
||||
try {
|
||||
const fs = require("fs");
|
||||
if (!fs.existsSync(appPath)) {
|
||||
logger.info(`⏭️ AI 어시스턴트 스킵 (경로 없음: ${appPath})`);
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
aiAssistantProcess = spawn("node", ["src/app.js"], {
|
||||
cwd: aiDir,
|
||||
stdio: "inherit",
|
||||
env: { ...process.env, PORT: AI_PORT },
|
||||
shell: true, // Windows에서 node 경로 인식
|
||||
});
|
||||
|
||||
aiAssistantProcess.on("error", (err) => {
|
||||
logger.warn(`⚠️ AI 어시스턴트 프로세스 에러: ${err.message}`);
|
||||
});
|
||||
|
||||
aiAssistantProcess.on("exit", (code, signal) => {
|
||||
aiAssistantProcess = null;
|
||||
if (code != null && code !== 0) {
|
||||
logger.warn(`⚠️ AI 어시스턴트 종료 (code=${code}, signal=${signal})`);
|
||||
}
|
||||
});
|
||||
|
||||
logger.info(`🤖 AI 어시스턴트 서비스 기동 (포트 ${AI_PORT}, cwd: ${aiDir})`);
|
||||
}
|
||||
|
||||
/**
|
||||
* AI 어시스턴트 프로세스 종료 (SIGTERM/SIGINT 시 호출)
|
||||
*/
|
||||
export function stopAiAssistant(): void {
|
||||
if (aiAssistantProcess && aiAssistantProcess.kill) {
|
||||
aiAssistantProcess.kill("SIGTERM");
|
||||
aiAssistantProcess = null;
|
||||
logger.info("🤖 AI 어시스턴트 프로세스 종료");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user