- Introduced new routes and controllers for managing shipping orders, including listing, saving, and previewing next order numbers. - Added design management routes and controller for handling design requests, projects, tasks, and work logs. - Implemented company code filtering for multi-tenancy support in both shipping order and design request functionalities. - Enhanced the shipping plan routes to include listing and updating plans, improving overall shipping management capabilities. These changes aim to provide comprehensive management features for shipping orders and design processes, facilitating better organization and tracking within the application.
445 lines
21 KiB
TypeScript
445 lines
21 KiB
TypeScript
import "dotenv/config";
|
|
process.env.TZ = "Asia/Seoul";
|
|
import "express-async-errors"; // async 라우트 핸들러의 에러를 Express 에러 핸들러로 자동 전달
|
|
import express from "express";
|
|
import cors from "cors";
|
|
import helmet from "helmet";
|
|
import compression from "compression";
|
|
import rateLimit from "express-rate-limit";
|
|
import path from "path";
|
|
import config from "./config/environment";
|
|
import { logger } from "./utils/logger";
|
|
import { errorHandler } from "./middleware/errorHandler";
|
|
import { refreshTokenIfNeeded } from "./middleware/authMiddleware";
|
|
|
|
// ============================================
|
|
// 프로세스 레벨 예외 처리 (서버 크래시 방지)
|
|
// ============================================
|
|
|
|
// 처리되지 않은 Promise 거부 핸들러
|
|
process.on(
|
|
"unhandledRejection",
|
|
(reason: Error | any, promise: Promise<any>) => {
|
|
logger.error("⚠️ Unhandled Promise Rejection:", {
|
|
reason: reason?.message || reason,
|
|
stack: reason?.stack,
|
|
});
|
|
// 프로세스를 종료하지 않고 로깅만 수행
|
|
// 심각한 에러의 경우 graceful shutdown 고려
|
|
},
|
|
);
|
|
|
|
// 처리되지 않은 예외 핸들러
|
|
process.on("uncaughtException", (error: Error) => {
|
|
logger.error("🔥 Uncaught Exception:", {
|
|
message: error.message,
|
|
stack: error.stack,
|
|
});
|
|
// 예외 발생 후에도 서버를 유지하되, 상태가 불안정할 수 있으므로 주의
|
|
// 심각한 에러의 경우 graceful shutdown 후 재시작 권장
|
|
});
|
|
|
|
// SIGTERM 시그널 처리 (Docker/Kubernetes 환경)
|
|
process.on("SIGTERM", () => {
|
|
logger.info("📴 SIGTERM 시그널 수신, graceful shutdown 시작...");
|
|
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);
|
|
});
|
|
|
|
// 라우터 임포트
|
|
import authRoutes from "./routes/authRoutes";
|
|
import adminRoutes from "./routes/adminRoutes";
|
|
import multilangRoutes from "./routes/multilangRoutes";
|
|
import tableManagementRoutes from "./routes/tableManagementRoutes";
|
|
import entityJoinRoutes from "./routes/entityJoinRoutes";
|
|
import screenManagementRoutes from "./routes/screenManagementRoutes";
|
|
import commonCodeRoutes from "./routes/commonCodeRoutes";
|
|
import dynamicFormRoutes from "./routes/dynamicFormRoutes";
|
|
import fileRoutes from "./routes/fileRoutes";
|
|
import companyManagementRoutes from "./routes/companyManagementRoutes";
|
|
import dataflowRoutes from "./routes/dataflowRoutes";
|
|
import dataflowDiagramRoutes from "./routes/dataflowDiagramRoutes";
|
|
import webTypeStandardRoutes from "./routes/webTypeStandardRoutes";
|
|
import buttonActionStandardRoutes from "./routes/buttonActionStandardRoutes";
|
|
import screenStandardRoutes from "./routes/screenStandardRoutes";
|
|
import templateStandardRoutes from "./routes/templateStandardRoutes";
|
|
import componentStandardRoutes from "./routes/componentStandardRoutes";
|
|
import layoutRoutes from "./routes/layoutRoutes";
|
|
import mailTemplateFileRoutes from "./routes/mailTemplateFileRoutes";
|
|
import mailAccountFileRoutes from "./routes/mailAccountFileRoutes";
|
|
import mailSendSimpleRoutes from "./routes/mailSendSimpleRoutes";
|
|
import mailSentHistoryRoutes from "./routes/mailSentHistoryRoutes";
|
|
import mailReceiveBasicRoutes from "./routes/mailReceiveBasicRoutes";
|
|
import dataRoutes from "./routes/dataRoutes";
|
|
import testButtonDataflowRoutes from "./routes/testButtonDataflowRoutes";
|
|
import externalDbConnectionRoutes from "./routes/externalDbConnectionRoutes";
|
|
import externalRestApiConnectionRoutes from "./routes/externalRestApiConnectionRoutes";
|
|
import multiConnectionRoutes from "./routes/multiConnectionRoutes";
|
|
import screenFileRoutes from "./routes/screenFileRoutes";
|
|
//import dbTypeCategoryRoutes from "./routes/dbTypeCategoryRoutes";
|
|
import batchRoutes from "./routes/batchRoutes";
|
|
import batchManagementRoutes from "./routes/batchManagementRoutes";
|
|
import batchExecutionLogRoutes from "./routes/batchExecutionLogRoutes";
|
|
// import dbTypeCategoryRoutes from "./routes/dbTypeCategoryRoutes"; // 파일이 존재하지 않음
|
|
import ddlRoutes from "./routes/ddlRoutes";
|
|
import entityReferenceRoutes from "./routes/entityReferenceRoutes";
|
|
import externalCallRoutes from "./routes/externalCallRoutes";
|
|
import externalCallConfigRoutes from "./routes/externalCallConfigRoutes";
|
|
import dataflowExecutionRoutes from "./routes/dataflowExecutionRoutes";
|
|
import dashboardRoutes from "./routes/dashboardRoutes";
|
|
import reportRoutes from "./routes/reportRoutes";
|
|
import barcodeLabelRoutes from "./routes/barcodeLabelRoutes";
|
|
import openApiProxyRoutes from "./routes/openApiProxyRoutes"; // 날씨/환율 API
|
|
import deliveryRoutes from "./routes/deliveryRoutes"; // 배송/화물 관리
|
|
import riskAlertRoutes from "./routes/riskAlertRoutes"; // 리스크/알림 관리
|
|
import todoRoutes from "./routes/todoRoutes"; // To-Do 관리
|
|
import bookingRoutes from "./routes/bookingRoutes"; // 예약 요청 관리
|
|
import mapDataRoutes from "./routes/mapDataRoutes"; // 지도 데이터 관리
|
|
import excelMappingRoutes from "./routes/excelMappingRoutes"; // 엑셀 매핑 템플릿
|
|
import yardLayoutRoutes from "./routes/yardLayoutRoutes"; // 3D 필드
|
|
//import materialRoutes from "./routes/materialRoutes"; // 자재 관리
|
|
import digitalTwinRoutes from "./routes/digitalTwinRoutes"; // 디지털 트윈 (야드 관제)
|
|
import flowRoutes from "./routes/flowRoutes"; // 플로우 관리
|
|
import flowExternalDbConnectionRoutes from "./routes/flowExternalDbConnectionRoutes"; // 플로우 전용 외부 DB 연결
|
|
import scheduleRoutes from "./routes/scheduleRoutes"; // 스케줄 자동 생성
|
|
import workHistoryRoutes from "./routes/workHistoryRoutes"; // 작업 이력 관리
|
|
import tableHistoryRoutes from "./routes/tableHistoryRoutes"; // 테이블 변경 이력 조회
|
|
import bomRoutes from "./routes/bomRoutes"; // BOM 이력/버전 관리
|
|
import productionRoutes from "./routes/productionRoutes"; // 생산계획 관리
|
|
import roleRoutes from "./routes/roleRoutes"; // 권한 그룹 관리
|
|
import departmentRoutes from "./routes/departmentRoutes"; // 부서 관리
|
|
import tableCategoryValueRoutes from "./routes/tableCategoryValueRoutes"; // 카테고리 값 관리
|
|
import codeMergeRoutes from "./routes/codeMergeRoutes"; // 코드 병합
|
|
import numberingRuleRoutes from "./routes/numberingRuleRoutes"; // 채번 규칙 관리
|
|
import entitySearchRoutes, {
|
|
entityOptionsRouter,
|
|
} from "./routes/entitySearchRoutes"; // 엔티티 검색 및 옵션
|
|
import screenEmbeddingRoutes from "./routes/screenEmbeddingRoutes"; // 화면 임베딩 및 데이터 전달
|
|
import screenGroupRoutes from "./routes/screenGroupRoutes"; // 화면 그룹 관리
|
|
import popActionRoutes from "./routes/popActionRoutes"; // POP 액션 실행
|
|
import popProductionRoutes from "./routes/popProductionRoutes"; // POP 생산 관리 (공정 생성/타이머)
|
|
import vehicleTripRoutes from "./routes/vehicleTripRoutes"; // 차량 운행 이력 관리
|
|
import approvalRoutes from "./routes/approvalRoutes"; // 결재 시스템
|
|
import driverRoutes from "./routes/driverRoutes"; // 공차중계 운전자 관리
|
|
import taxInvoiceRoutes from "./routes/taxInvoiceRoutes"; // 세금계산서 관리
|
|
import cascadingRelationRoutes from "./routes/cascadingRelationRoutes"; // 연쇄 드롭다운 관계 관리
|
|
import cascadingAutoFillRoutes from "./routes/cascadingAutoFillRoutes"; // 자동 입력 관리
|
|
import cascadingConditionRoutes from "./routes/cascadingConditionRoutes"; // 조건부 연쇄 관리
|
|
import packagingRoutes from "./routes/packagingRoutes"; // 포장/적재정보 관리
|
|
import cascadingMutualExclusionRoutes from "./routes/cascadingMutualExclusionRoutes"; // 상호 배제 관리
|
|
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 auditLogRoutes from "./routes/auditLogRoutes"; // 통합 변경 이력
|
|
import moldRoutes from "./routes/moldRoutes"; // 금형 관리
|
|
import shippingPlanRoutes from "./routes/shippingPlanRoutes"; // 출하계획 관리
|
|
import shippingOrderRoutes from "./routes/shippingOrderRoutes"; // 출하지시 관리
|
|
import designRoutes from "./routes/designRoutes"; // 설계 모듈 (DR/ECR/프로젝트/ECN)
|
|
import { BatchSchedulerService } from "./services/batchSchedulerService";
|
|
// import collectionRoutes from "./routes/collectionRoutes"; // 임시 주석
|
|
// import batchRoutes from "./routes/batchRoutes"; // 임시 주석
|
|
// import userRoutes from './routes/userRoutes';
|
|
// import menuRoutes from './routes/menuRoutes';
|
|
|
|
const app = express();
|
|
|
|
app.set("trust proxy", true);
|
|
|
|
// 기본 미들웨어
|
|
app.use(
|
|
helmet({
|
|
contentSecurityPolicy: {
|
|
directives: {
|
|
...helmet.contentSecurityPolicy.getDefaultDirectives(),
|
|
"frame-ancestors": [
|
|
"'self'",
|
|
"http://localhost:9771",
|
|
"http://localhost:3000",
|
|
], // 프론트엔드 도메인 허용
|
|
},
|
|
},
|
|
}),
|
|
);
|
|
app.use(compression());
|
|
app.use(express.json({ limit: "10mb" }));
|
|
app.use(express.urlencoded({ extended: true, limit: "10mb" }));
|
|
|
|
// 정적 파일 서빙 전에 CORS 미들웨어 추가 (OPTIONS 요청 처리)
|
|
app.options("/uploads/*", (req, res) => {
|
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
res.sendStatus(200);
|
|
});
|
|
|
|
// 정적 파일 서빙 (업로드된 파일들)
|
|
app.use(
|
|
"/uploads",
|
|
(req, res, next) => {
|
|
// 모든 정적 파일 요청에 CORS 헤더 추가
|
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
res.setHeader(
|
|
"Access-Control-Allow-Headers",
|
|
"Content-Type, Authorization",
|
|
);
|
|
res.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
|
|
res.setHeader("Cache-Control", "public, max-age=3600");
|
|
next();
|
|
},
|
|
express.static(path.join(process.cwd(), "uploads")),
|
|
);
|
|
|
|
// CORS 설정 - environment.ts에서 이미 올바른 형태로 처리됨
|
|
app.use(
|
|
cors({
|
|
origin: config.cors.origin, // 이미 배열 또는 boolean으로 처리됨
|
|
credentials: config.cors.credentials,
|
|
methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
|
|
allowedHeaders: [
|
|
"Content-Type",
|
|
"Authorization",
|
|
"X-Requested-With",
|
|
"Accept",
|
|
"Origin",
|
|
"Access-Control-Request-Method",
|
|
"Access-Control-Request-Headers",
|
|
],
|
|
preflightContinue: false,
|
|
optionsSuccessStatus: 200,
|
|
}),
|
|
);
|
|
|
|
// Rate Limiting (개발 환경에서는 완화)
|
|
const limiter = rateLimit({
|
|
windowMs: 1 * 60 * 1000, // 1분
|
|
max: config.nodeEnv === "development" ? 10000 : 10000, // 개발환경에서는 10000으로 증가, 운영환경에서는 100
|
|
message: {
|
|
error: "너무 많은 요청이 발생했습니다. 잠시 후 다시 시도해주세요.",
|
|
},
|
|
skip: (req) => {
|
|
// 헬스 체크와 자주 호출되는 API들은 Rate Limiting 완화
|
|
return (
|
|
req.path === "/health" ||
|
|
req.path.includes("/table-management/") ||
|
|
req.path.includes("/external-db-connections/") ||
|
|
req.path.includes("/screen-management/") ||
|
|
req.path.includes("/multi-connection/") ||
|
|
req.path.includes("/dataflow-diagrams/")
|
|
);
|
|
},
|
|
});
|
|
app.use("/api/", limiter);
|
|
|
|
// 토큰 자동 갱신 미들웨어 (모든 API 요청에 적용)
|
|
// 토큰이 1시간 이내에 만료되는 경우 자동으로 갱신하여 응답 헤더에 포함
|
|
app.use("/api/", refreshTokenIfNeeded);
|
|
|
|
// 헬스 체크 엔드포인트
|
|
app.get("/health", (req, res) => {
|
|
res.status(200).json({
|
|
status: "OK",
|
|
timestamp: new Date().toISOString(),
|
|
uptime: process.uptime(),
|
|
environment: config.nodeEnv,
|
|
});
|
|
});
|
|
|
|
// API 라우터
|
|
app.use("/api/auth", authRoutes);
|
|
app.use("/api/admin", adminRoutes);
|
|
app.use("/api/multilang", multilangRoutes);
|
|
app.use("/api/table-management", tableManagementRoutes);
|
|
app.use("/api/table-management", entityJoinRoutes); // 🎯 Entity 조인 기능
|
|
app.use("/api/screen-management", screenManagementRoutes);
|
|
app.use("/api/screen-groups", screenGroupRoutes); // 화면 그룹 관리
|
|
app.use("/api/pop", popActionRoutes); // POP 액션 실행
|
|
app.use("/api/pop/production", popProductionRoutes); // POP 생산 관리
|
|
app.use("/api/common-codes", commonCodeRoutes);
|
|
app.use("/api/dynamic-form", dynamicFormRoutes);
|
|
app.use("/api/files", fileRoutes);
|
|
app.use("/api/company-management", companyManagementRoutes);
|
|
app.use("/api/dataflow", dataflowRoutes);
|
|
app.use("/api/dataflow-diagrams", dataflowDiagramRoutes);
|
|
app.use("/api/admin/web-types", webTypeStandardRoutes);
|
|
app.use("/api/admin/button-actions", buttonActionStandardRoutes);
|
|
app.use("/api/admin/template-standards", templateStandardRoutes);
|
|
app.use("/api/admin/component-standards", componentStandardRoutes);
|
|
app.use("/api/layouts", layoutRoutes);
|
|
app.use("/api/mail/accounts", mailAccountFileRoutes); // 파일 기반 계정
|
|
app.use("/api/mail/templates-file", mailTemplateFileRoutes); // 파일 기반 템플릿
|
|
app.use("/api/mail/send", mailSendSimpleRoutes); // 메일 발송
|
|
app.use("/api/mail/sent", mailSentHistoryRoutes); // 메일 발송 이력
|
|
app.use("/api/mail/receive", mailReceiveBasicRoutes); // 메일 수신
|
|
app.use("/api/screen", screenStandardRoutes);
|
|
app.use("/api/data", dataRoutes);
|
|
app.use("/api/test-button-dataflow", testButtonDataflowRoutes);
|
|
app.use("/api/external-db-connections", externalDbConnectionRoutes);
|
|
app.use("/api/external-rest-api-connections", externalRestApiConnectionRoutes);
|
|
app.use("/api/multi-connection", multiConnectionRoutes);
|
|
app.use("/api/screen-files", screenFileRoutes);
|
|
app.use("/api/batch-configs", batchRoutes);
|
|
app.use("/api/excel-mapping", excelMappingRoutes); // 엑셀 매핑 템플릿
|
|
app.use("/api/batch-management", batchManagementRoutes);
|
|
app.use("/api/batch-execution-logs", batchExecutionLogRoutes);
|
|
// app.use("/api/db-type-categories", dbTypeCategoryRoutes); // 파일이 존재하지 않음
|
|
app.use("/api/ddl", ddlRoutes);
|
|
app.use("/api/entity-reference", entityReferenceRoutes);
|
|
app.use("/api/external-calls", externalCallRoutes);
|
|
app.use("/api/external-call-configs", externalCallConfigRoutes);
|
|
app.use("/api/dataflow", dataflowExecutionRoutes);
|
|
app.use("/api/dashboards", dashboardRoutes);
|
|
app.use("/api/admin/reports", reportRoutes);
|
|
app.use("/api/admin/barcode-labels", barcodeLabelRoutes);
|
|
app.use("/api/open-api", openApiProxyRoutes); // 날씨/환율 외부 API
|
|
app.use("/api/delivery", deliveryRoutes); // 배송/화물 관리
|
|
app.use("/api/risk-alerts", riskAlertRoutes); // 리스크/알림 관리
|
|
app.use("/api/todos", todoRoutes); // To-Do 관리
|
|
app.use("/api/bookings", bookingRoutes); // 예약 요청 관리
|
|
app.use("/api/map-data", mapDataRoutes); // 지도 데이터 조회
|
|
app.use("/api/yard-layouts", yardLayoutRoutes); // 3D 필드
|
|
// app.use("/api/materials", materialRoutes); // 자재 관리 (임시 주석)
|
|
app.use("/api/digital-twin", digitalTwinRoutes); // 디지털 트윈 (야드 관제)
|
|
app.use("/api/flow-external-db", flowExternalDbConnectionRoutes); // 플로우 전용 외부 DB 연결
|
|
app.use("/api/flow", flowRoutes); // 플로우 관리 (마지막에 등록하여 다른 라우트와 충돌 방지)
|
|
app.use("/api/schedule", scheduleRoutes); // 스케줄 자동 생성
|
|
app.use("/api/work-history", workHistoryRoutes); // 작업 이력 관리
|
|
app.use("/api/table-history", tableHistoryRoutes); // 테이블 변경 이력 조회
|
|
app.use("/api/bom", bomRoutes); // BOM 이력/버전 관리
|
|
app.use("/api/production", productionRoutes); // 생산계획 관리
|
|
app.use("/api/roles", roleRoutes); // 권한 그룹 관리
|
|
app.use("/api/departments", departmentRoutes); // 부서 관리
|
|
app.use("/api/table-categories", tableCategoryValueRoutes); // 카테고리 값 관리
|
|
app.use("/api/code-merge", codeMergeRoutes); // 코드 병합
|
|
app.use("/api/numbering-rules", numberingRuleRoutes); // 채번 규칙 관리
|
|
app.use("/api/entity-search", entitySearchRoutes); // 엔티티 검색
|
|
app.use("/api/entity", entityOptionsRouter); // 엔티티 옵션 (V2Select용)
|
|
app.use("/api/driver", driverRoutes); // 공차중계 운전자 관리
|
|
app.use("/api/tax-invoice", taxInvoiceRoutes); // 세금계산서 관리
|
|
app.use("/api/cascading-relations", cascadingRelationRoutes); // 연쇄 드롭다운 관계 관리
|
|
app.use("/api/cascading-auto-fill", cascadingAutoFillRoutes); // 자동 입력 관리
|
|
app.use("/api/cascading-conditions", cascadingConditionRoutes); // 조건부 연쇄 관리
|
|
app.use("/api/packaging", packagingRoutes); // 포장/적재정보 관리
|
|
app.use("/api/cascading-exclusions", cascadingMutualExclusionRoutes); // 상호 배제 관리
|
|
app.use("/api/cascading-hierarchy", cascadingHierarchyRoutes); // 다단계 계층 관리
|
|
app.use("/api/category-value-cascading", categoryValueCascadingRoutes); // 카테고리 값 연쇄관계
|
|
app.use("/api/category-tree", categoryTreeRoutes); // 카테고리 트리 (테스트)
|
|
app.use("/api/process-work-standard", processWorkStandardRoutes); // 공정 작업기준
|
|
app.use("/api/audit-log", auditLogRoutes); // 통합 변경 이력
|
|
app.use("/api/mold", moldRoutes); // 금형 관리
|
|
app.use("/api/shipping-plan", shippingPlanRoutes); // 출하계획 관리
|
|
app.use("/api/shipping-order", shippingOrderRoutes); // 출하지시 관리
|
|
app.use("/api/design", designRoutes); // 설계 모듈
|
|
app.use("/api", screenEmbeddingRoutes); // 화면 임베딩 및 데이터 전달
|
|
app.use("/api/ai/v1", aiAssistantProxy); // AI 어시스턴트 (동일 서비스 내 프록시 → AI 서비스 포트)
|
|
app.use("/api/vehicle", vehicleTripRoutes); // 차량 운행 이력 관리
|
|
app.use("/api/approval", approvalRoutes); // 결재 시스템
|
|
// app.use("/api/collections", collectionRoutes); // 임시 주석
|
|
// app.use("/api/batch", batchRoutes); // 임시 주석
|
|
// app.use('/api/users', userRoutes);
|
|
// app.use('/api/menus', menuRoutes);
|
|
|
|
// 404 핸들러
|
|
app.use("*", (req, res) => {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: "요청한 리소스를 찾을 수 없습니다.",
|
|
path: req.originalUrl,
|
|
});
|
|
});
|
|
|
|
// 에러 핸들러
|
|
app.use(errorHandler);
|
|
|
|
// 서버 시작
|
|
const PORT = config.port;
|
|
const HOST = config.host;
|
|
|
|
app.listen(PORT, HOST, async () => {
|
|
logger.info(`🚀 Server is running on ${HOST}:${PORT}`);
|
|
logger.info(`📊 Environment: ${config.nodeEnv}`);
|
|
logger.info(`🔗 Health check: http://${HOST}:${PORT}/health`);
|
|
logger.info(`🌐 External access: http://39.117.244.52:${PORT}/health`);
|
|
|
|
// 데이터베이스 마이그레이션 실행
|
|
try {
|
|
const {
|
|
runDashboardMigration,
|
|
runTableHistoryActionMigration,
|
|
runDtgManagementLogMigration,
|
|
runApprovalSystemMigration,
|
|
} = await import("./database/runMigration");
|
|
|
|
await runDashboardMigration();
|
|
await runTableHistoryActionMigration();
|
|
await runDtgManagementLogMigration();
|
|
await runApprovalSystemMigration();
|
|
} catch (error) {
|
|
logger.error(`❌ 마이그레이션 실패:`, error);
|
|
}
|
|
|
|
// 배치 스케줄러 초기화
|
|
try {
|
|
await BatchSchedulerService.initializeScheduler();
|
|
logger.info(`⏰ 배치 스케줄러가 시작되었습니다.`);
|
|
} catch (error) {
|
|
logger.error(`❌ 배치 스케줄러 초기화 실패:`, error);
|
|
}
|
|
|
|
// 리스크/알림 자동 갱신 시작
|
|
try {
|
|
const { RiskAlertCacheService } = await import(
|
|
"./services/riskAlertCacheService"
|
|
);
|
|
const cacheService = RiskAlertCacheService.getInstance();
|
|
cacheService.startAutoRefresh();
|
|
logger.info(`⏰ 리스크/알림 자동 갱신이 시작되었습니다. (10분 간격)`);
|
|
} catch (error) {
|
|
logger.error(`❌ 리스크/알림 자동 갱신 시작 실패:`, error);
|
|
}
|
|
|
|
// 메일 자동 삭제 (30일 지난 삭제된 메일) - 매일 새벽 2시 실행
|
|
try {
|
|
const cron = await import("node-cron");
|
|
const { mailSentHistoryService } = await import(
|
|
"./services/mailSentHistoryService"
|
|
);
|
|
|
|
cron.schedule("0 2 * * *", async () => {
|
|
try {
|
|
logger.info("🗑️ 30일 지난 삭제된 메일 자동 삭제 시작...");
|
|
const deletedCount =
|
|
await mailSentHistoryService.cleanupOldDeletedMails();
|
|
logger.info(`✅ 30일 지난 메일 ${deletedCount}개 자동 삭제 완료`);
|
|
} catch (error) {
|
|
logger.error("❌ 메일 자동 삭제 실패:", error);
|
|
}
|
|
});
|
|
|
|
logger.info(`⏰ 메일 자동 삭제 스케줄러가 시작되었습니다. (매일 새벽 2시)`);
|
|
} catch (error) {
|
|
logger.error(`❌ 메일 자동 삭제 스케줄러 시작 실패:`, error);
|
|
}
|
|
|
|
// AI 어시스턴트 서비스 함께 기동 (한 번에 킬 가능)
|
|
try {
|
|
const { startAiAssistant } = await import("./utils/startAiAssistant");
|
|
startAiAssistant();
|
|
} catch (error) {
|
|
logger.warn("⚠️ AI 어시스턴트 기동 스킵:", error);
|
|
}
|
|
});
|
|
|
|
export default app;
|