2025-08-21 09:41:46 +09:00
|
|
|
import "dotenv/config";
|
|
|
|
|
import express from "express";
|
|
|
|
|
import cors from "cors";
|
|
|
|
|
import helmet from "helmet";
|
|
|
|
|
import compression from "compression";
|
|
|
|
|
import rateLimit from "express-rate-limit";
|
2025-09-08 13:10:09 +09:00
|
|
|
import path from "path";
|
2025-08-21 09:41:46 +09:00
|
|
|
import config from "./config/environment";
|
|
|
|
|
import { logger } from "./utils/logger";
|
|
|
|
|
import { errorHandler } from "./middleware/errorHandler";
|
2025-12-05 17:46:22 +09:00
|
|
|
import { refreshTokenIfNeeded } from "./middleware/authMiddleware";
|
2025-08-21 09:41:46 +09:00
|
|
|
|
|
|
|
|
// 라우터 임포트
|
|
|
|
|
import authRoutes from "./routes/authRoutes";
|
|
|
|
|
import adminRoutes from "./routes/adminRoutes";
|
2025-08-21 14:47:07 +09:00
|
|
|
import multilangRoutes from "./routes/multilangRoutes";
|
2025-08-25 14:08:08 +09:00
|
|
|
import tableManagementRoutes from "./routes/tableManagementRoutes";
|
2025-09-16 15:13:00 +09:00
|
|
|
import entityJoinRoutes from "./routes/entityJoinRoutes";
|
2025-09-01 11:48:12 +09:00
|
|
|
import screenManagementRoutes from "./routes/screenManagementRoutes";
|
2025-09-02 11:30:19 +09:00
|
|
|
import commonCodeRoutes from "./routes/commonCodeRoutes";
|
2025-09-04 14:23:35 +09:00
|
|
|
import dynamicFormRoutes from "./routes/dynamicFormRoutes";
|
2025-09-05 12:04:13 +09:00
|
|
|
import fileRoutes from "./routes/fileRoutes";
|
2025-09-05 14:52:10 +09:00
|
|
|
import companyManagementRoutes from "./routes/companyManagementRoutes";
|
2025-09-26 01:28:51 +09:00
|
|
|
import dataflowRoutes from "./routes/dataflowRoutes";
|
2025-09-10 15:30:14 +09:00
|
|
|
import dataflowDiagramRoutes from "./routes/dataflowDiagramRoutes";
|
2025-09-09 14:29:04 +09:00
|
|
|
import webTypeStandardRoutes from "./routes/webTypeStandardRoutes";
|
|
|
|
|
import buttonActionStandardRoutes from "./routes/buttonActionStandardRoutes";
|
|
|
|
|
import screenStandardRoutes from "./routes/screenStandardRoutes";
|
2025-09-09 17:42:23 +09:00
|
|
|
import templateStandardRoutes from "./routes/templateStandardRoutes";
|
|
|
|
|
import componentStandardRoutes from "./routes/componentStandardRoutes";
|
2025-09-10 18:36:28 +09:00
|
|
|
import layoutRoutes from "./routes/layoutRoutes";
|
2025-10-01 16:15:53 +09:00
|
|
|
import mailTemplateFileRoutes from "./routes/mailTemplateFileRoutes";
|
|
|
|
|
import mailAccountFileRoutes from "./routes/mailAccountFileRoutes";
|
|
|
|
|
import mailSendSimpleRoutes from "./routes/mailSendSimpleRoutes";
|
2025-10-22 11:33:56 +09:00
|
|
|
import mailSentHistoryRoutes from "./routes/mailSentHistoryRoutes";
|
2025-10-01 17:01:31 +09:00
|
|
|
import mailReceiveBasicRoutes from "./routes/mailReceiveBasicRoutes";
|
2025-09-12 16:47:02 +09:00
|
|
|
import dataRoutes from "./routes/dataRoutes";
|
2025-09-18 12:02:48 +09:00
|
|
|
import testButtonDataflowRoutes from "./routes/testButtonDataflowRoutes";
|
2025-09-19 12:15:14 +09:00
|
|
|
import externalDbConnectionRoutes from "./routes/externalDbConnectionRoutes";
|
2025-10-21 10:59:15 +09:00
|
|
|
import externalRestApiConnectionRoutes from "./routes/externalRestApiConnectionRoutes";
|
2025-09-24 18:23:57 +09:00
|
|
|
import multiConnectionRoutes from "./routes/multiConnectionRoutes";
|
2025-09-26 17:12:03 +09:00
|
|
|
import screenFileRoutes from "./routes/screenFileRoutes";
|
2025-09-26 20:04:07 +09:00
|
|
|
//import dbTypeCategoryRoutes from "./routes/dbTypeCategoryRoutes";
|
2025-09-24 10:46:55 +09:00
|
|
|
import batchRoutes from "./routes/batchRoutes";
|
2025-09-25 11:04:16 +09:00
|
|
|
import batchManagementRoutes from "./routes/batchManagementRoutes";
|
|
|
|
|
import batchExecutionLogRoutes from "./routes/batchExecutionLogRoutes";
|
2025-09-26 17:55:38 +09:00
|
|
|
// import dbTypeCategoryRoutes from "./routes/dbTypeCategoryRoutes"; // 파일이 존재하지 않음
|
2025-09-22 17:00:59 +09:00
|
|
|
import ddlRoutes from "./routes/ddlRoutes";
|
2025-09-21 09:53:05 +09:00
|
|
|
import entityReferenceRoutes from "./routes/entityReferenceRoutes";
|
2025-09-26 17:11:18 +09:00
|
|
|
import externalCallRoutes from "./routes/externalCallRoutes";
|
|
|
|
|
import externalCallConfigRoutes from "./routes/externalCallConfigRoutes";
|
2025-09-29 12:17:10 +09:00
|
|
|
import dataflowExecutionRoutes from "./routes/dataflowExecutionRoutes";
|
2025-10-01 12:06:24 +09:00
|
|
|
import dashboardRoutes from "./routes/dashboardRoutes";
|
2025-10-01 11:34:17 +09:00
|
|
|
import reportRoutes from "./routes/reportRoutes";
|
2025-10-13 19:04:28 +09:00
|
|
|
import openApiProxyRoutes from "./routes/openApiProxyRoutes"; // 날씨/환율 API
|
2025-10-14 16:36:00 +09:00
|
|
|
import deliveryRoutes from "./routes/deliveryRoutes"; // 배송/화물 관리
|
|
|
|
|
import riskAlertRoutes from "./routes/riskAlertRoutes"; // 리스크/알림 관리
|
2025-10-14 17:21:28 +09:00
|
|
|
import todoRoutes from "./routes/todoRoutes"; // To-Do 관리
|
|
|
|
|
import bookingRoutes from "./routes/bookingRoutes"; // 예약 요청 관리
|
2025-10-15 10:29:15 +09:00
|
|
|
import mapDataRoutes from "./routes/mapDataRoutes"; // 지도 데이터 관리
|
2025-11-25 15:06:55 +09:00
|
|
|
import yardLayoutRoutes from "./routes/yardLayoutRoutes"; // 3D 필드
|
2025-10-20 15:53:00 +09:00
|
|
|
//import materialRoutes from "./routes/materialRoutes"; // 자재 관리
|
2025-11-20 10:15:58 +09:00
|
|
|
import digitalTwinRoutes from "./routes/digitalTwinRoutes"; // 디지털 트윈 (야드 관제)
|
2025-10-20 10:55:33 +09:00
|
|
|
import flowRoutes from "./routes/flowRoutes"; // 플로우 관리
|
2025-10-20 17:50:27 +09:00
|
|
|
import flowExternalDbConnectionRoutes from "./routes/flowExternalDbConnectionRoutes"; // 플로우 전용 외부 DB 연결
|
2025-10-20 14:07:08 +09:00
|
|
|
import workHistoryRoutes from "./routes/workHistoryRoutes"; // 작업 이력 관리
|
2025-10-27 11:11:08 +09:00
|
|
|
import tableHistoryRoutes from "./routes/tableHistoryRoutes"; // 테이블 변경 이력 조회
|
2025-10-27 16:40:59 +09:00
|
|
|
import roleRoutes from "./routes/roleRoutes"; // 권한 그룹 관리
|
2025-11-03 16:31:03 +09:00
|
|
|
import departmentRoutes from "./routes/departmentRoutes"; // 부서 관리
|
2025-11-05 15:23:57 +09:00
|
|
|
import tableCategoryValueRoutes from "./routes/tableCategoryValueRoutes"; // 카테고리 값 관리
|
2025-11-04 18:31:26 +09:00
|
|
|
import codeMergeRoutes from "./routes/codeMergeRoutes"; // 코드 병합
|
2025-11-05 18:13:06 +09:00
|
|
|
import numberingRuleRoutes from "./routes/numberingRuleRoutes"; // 채번 규칙 관리
|
2025-11-14 14:43:53 +09:00
|
|
|
import entitySearchRoutes from "./routes/entitySearchRoutes"; // 엔티티 검색
|
2025-11-27 12:08:32 +09:00
|
|
|
import screenEmbeddingRoutes from "./routes/screenEmbeddingRoutes"; // 화면 임베딩 및 데이터 전달
|
2025-12-02 09:53:08 +09:00
|
|
|
import vehicleTripRoutes from "./routes/vehicleTripRoutes"; // 차량 운행 이력 관리
|
2025-12-01 18:41:02 +09:00
|
|
|
import driverRoutes from "./routes/driverRoutes"; // 공차중계 운전자 관리
|
2025-12-08 16:01:59 +09:00
|
|
|
import taxInvoiceRoutes from "./routes/taxInvoiceRoutes"; // 세금계산서 관리
|
2025-12-10 13:53:44 +09:00
|
|
|
import cascadingRelationRoutes from "./routes/cascadingRelationRoutes"; // 연쇄 드롭다운 관계 관리
|
2025-12-10 15:59:04 +09:00
|
|
|
import cascadingAutoFillRoutes from "./routes/cascadingAutoFillRoutes"; // 자동 입력 관리
|
|
|
|
|
import cascadingConditionRoutes from "./routes/cascadingConditionRoutes"; // 조건부 연쇄 관리
|
|
|
|
|
import cascadingMutualExclusionRoutes from "./routes/cascadingMutualExclusionRoutes"; // 상호 배제 관리
|
|
|
|
|
import cascadingHierarchyRoutes from "./routes/cascadingHierarchyRoutes"; // 다단계 계층 관리
|
2025-09-25 11:04:16 +09:00
|
|
|
import { BatchSchedulerService } from "./services/batchSchedulerService";
|
2025-09-24 10:04:25 +09:00
|
|
|
// import collectionRoutes from "./routes/collectionRoutes"; // 임시 주석
|
|
|
|
|
// import batchRoutes from "./routes/batchRoutes"; // 임시 주석
|
2025-08-21 09:41:46 +09:00
|
|
|
// import userRoutes from './routes/userRoutes';
|
|
|
|
|
// import menuRoutes from './routes/menuRoutes';
|
|
|
|
|
|
|
|
|
|
const app = express();
|
|
|
|
|
|
|
|
|
|
// 기본 미들웨어
|
2025-09-29 18:04:56 +09:00
|
|
|
app.use(
|
|
|
|
|
helmet({
|
|
|
|
|
contentSecurityPolicy: {
|
|
|
|
|
directives: {
|
|
|
|
|
...helmet.contentSecurityPolicy.getDefaultDirectives(),
|
|
|
|
|
"frame-ancestors": [
|
|
|
|
|
"'self'",
|
|
|
|
|
"http://localhost:9771",
|
|
|
|
|
"http://localhost:3000",
|
|
|
|
|
], // 프론트엔드 도메인 허용
|
|
|
|
|
},
|
2025-09-29 13:29:03 +09:00
|
|
|
},
|
2025-09-29 18:04:56 +09:00
|
|
|
})
|
|
|
|
|
);
|
2025-08-21 09:41:46 +09:00
|
|
|
app.use(compression());
|
2025-10-13 19:15:52 +09:00
|
|
|
app.use(express.json({ limit: "10mb" }));
|
|
|
|
|
app.use(express.urlencoded({ extended: true, limit: "10mb" }));
|
2025-08-21 09:41:46 +09:00
|
|
|
|
2025-10-01 16:53:35 +09:00
|
|
|
// 정적 파일 서빙 전에 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);
|
|
|
|
|
});
|
|
|
|
|
|
2025-09-08 13:10:09 +09:00
|
|
|
// 정적 파일 서빙 (업로드된 파일들)
|
|
|
|
|
app.use(
|
|
|
|
|
"/uploads",
|
2025-10-01 16:53:35 +09:00
|
|
|
(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"))
|
2025-09-08 13:10:09 +09:00
|
|
|
);
|
|
|
|
|
|
2025-09-04 16:29:57 +09:00
|
|
|
// CORS 설정 - environment.ts에서 이미 올바른 형태로 처리됨
|
2025-08-21 09:41:46 +09:00
|
|
|
app.use(
|
|
|
|
|
cors({
|
2025-09-04 16:29:57 +09:00
|
|
|
origin: config.cors.origin, // 이미 배열 또는 boolean으로 처리됨
|
|
|
|
|
credentials: config.cors.credentials,
|
2025-08-21 09:41:46 +09:00
|
|
|
methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
|
2025-09-04 15:20:26 +09:00
|
|
|
allowedHeaders: [
|
|
|
|
|
"Content-Type",
|
|
|
|
|
"Authorization",
|
|
|
|
|
"X-Requested-With",
|
|
|
|
|
"Accept",
|
|
|
|
|
"Origin",
|
|
|
|
|
"Access-Control-Request-Method",
|
|
|
|
|
"Access-Control-Request-Headers",
|
|
|
|
|
],
|
|
|
|
|
preflightContinue: false,
|
|
|
|
|
optionsSuccessStatus: 200,
|
2025-08-21 09:41:46 +09:00
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Rate Limiting (개발 환경에서는 완화)
|
|
|
|
|
const limiter = rateLimit({
|
|
|
|
|
windowMs: 1 * 60 * 1000, // 1분
|
2025-09-29 18:04:56 +09:00
|
|
|
max: config.nodeEnv === "development" ? 10000 : 10000, // 개발환경에서는 10000으로 증가, 운영환경에서는 100
|
2025-08-21 09:41:46 +09:00
|
|
|
message: {
|
|
|
|
|
error: "너무 많은 요청이 발생했습니다. 잠시 후 다시 시도해주세요.",
|
|
|
|
|
},
|
|
|
|
|
skip: (req) => {
|
2025-09-26 13:52:32 +09:00
|
|
|
// 헬스 체크와 자주 호출되는 API들은 Rate Limiting 완화
|
2025-09-26 01:28:51 +09:00
|
|
|
return (
|
|
|
|
|
req.path === "/health" ||
|
|
|
|
|
req.path.includes("/table-management/") ||
|
2025-09-26 13:52:32 +09:00
|
|
|
req.path.includes("/external-db-connections/") ||
|
|
|
|
|
req.path.includes("/screen-management/") ||
|
|
|
|
|
req.path.includes("/multi-connection/") ||
|
|
|
|
|
req.path.includes("/dataflow-diagrams/")
|
2025-09-26 01:28:51 +09:00
|
|
|
);
|
2025-08-21 09:41:46 +09:00
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
app.use("/api/", limiter);
|
|
|
|
|
|
2025-12-05 17:46:22 +09:00
|
|
|
// 토큰 자동 갱신 미들웨어 (모든 API 요청에 적용)
|
|
|
|
|
// 토큰이 1시간 이내에 만료되는 경우 자동으로 갱신하여 응답 헤더에 포함
|
|
|
|
|
app.use("/api/", refreshTokenIfNeeded);
|
|
|
|
|
|
2025-08-21 09:41:46 +09:00
|
|
|
// 헬스 체크 엔드포인트
|
|
|
|
|
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);
|
2025-08-21 14:47:07 +09:00
|
|
|
app.use("/api/multilang", multilangRoutes);
|
2025-08-25 14:08:08 +09:00
|
|
|
app.use("/api/table-management", tableManagementRoutes);
|
2025-09-16 15:13:00 +09:00
|
|
|
app.use("/api/table-management", entityJoinRoutes); // 🎯 Entity 조인 기능
|
2025-09-01 11:48:12 +09:00
|
|
|
app.use("/api/screen-management", screenManagementRoutes);
|
2025-09-02 11:30:19 +09:00
|
|
|
app.use("/api/common-codes", commonCodeRoutes);
|
2025-09-04 14:23:35 +09:00
|
|
|
app.use("/api/dynamic-form", dynamicFormRoutes);
|
2025-09-05 12:04:13 +09:00
|
|
|
app.use("/api/files", fileRoutes);
|
2025-09-05 14:52:10 +09:00
|
|
|
app.use("/api/company-management", companyManagementRoutes);
|
2025-09-26 01:28:51 +09:00
|
|
|
app.use("/api/dataflow", dataflowRoutes);
|
2025-09-10 15:30:14 +09:00
|
|
|
app.use("/api/dataflow-diagrams", dataflowDiagramRoutes);
|
2025-09-09 14:29:04 +09:00
|
|
|
app.use("/api/admin/web-types", webTypeStandardRoutes);
|
|
|
|
|
app.use("/api/admin/button-actions", buttonActionStandardRoutes);
|
2025-09-09 17:42:23 +09:00
|
|
|
app.use("/api/admin/template-standards", templateStandardRoutes);
|
|
|
|
|
app.use("/api/admin/component-standards", componentStandardRoutes);
|
2025-09-10 18:36:28 +09:00
|
|
|
app.use("/api/layouts", layoutRoutes);
|
2025-10-01 16:15:53 +09:00
|
|
|
app.use("/api/mail/accounts", mailAccountFileRoutes); // 파일 기반 계정
|
|
|
|
|
app.use("/api/mail/templates-file", mailTemplateFileRoutes); // 파일 기반 템플릿
|
|
|
|
|
app.use("/api/mail/send", mailSendSimpleRoutes); // 메일 발송
|
2025-10-22 11:33:56 +09:00
|
|
|
app.use("/api/mail/sent", mailSentHistoryRoutes); // 메일 발송 이력
|
2025-10-01 17:01:31 +09:00
|
|
|
app.use("/api/mail/receive", mailReceiveBasicRoutes); // 메일 수신
|
2025-09-09 14:29:04 +09:00
|
|
|
app.use("/api/screen", screenStandardRoutes);
|
2025-09-12 16:47:02 +09:00
|
|
|
app.use("/api/data", dataRoutes);
|
2025-09-18 12:02:48 +09:00
|
|
|
app.use("/api/test-button-dataflow", testButtonDataflowRoutes);
|
2025-09-19 12:15:14 +09:00
|
|
|
app.use("/api/external-db-connections", externalDbConnectionRoutes);
|
2025-10-21 10:59:15 +09:00
|
|
|
app.use("/api/external-rest-api-connections", externalRestApiConnectionRoutes);
|
2025-09-24 18:23:57 +09:00
|
|
|
app.use("/api/multi-connection", multiConnectionRoutes);
|
2025-09-26 17:12:03 +09:00
|
|
|
app.use("/api/screen-files", screenFileRoutes);
|
2025-09-24 10:46:55 +09:00
|
|
|
app.use("/api/batch-configs", batchRoutes);
|
2025-09-25 11:04:16 +09:00
|
|
|
app.use("/api/batch-management", batchManagementRoutes);
|
|
|
|
|
app.use("/api/batch-execution-logs", batchExecutionLogRoutes);
|
2025-09-26 17:55:38 +09:00
|
|
|
// app.use("/api/db-type-categories", dbTypeCategoryRoutes); // 파일이 존재하지 않음
|
2025-09-22 17:00:59 +09:00
|
|
|
app.use("/api/ddl", ddlRoutes);
|
2025-09-21 09:53:05 +09:00
|
|
|
app.use("/api/entity-reference", entityReferenceRoutes);
|
2025-09-26 17:11:18 +09:00
|
|
|
app.use("/api/external-calls", externalCallRoutes);
|
|
|
|
|
app.use("/api/external-call-configs", externalCallConfigRoutes);
|
2025-09-29 12:17:10 +09:00
|
|
|
app.use("/api/dataflow", dataflowExecutionRoutes);
|
2025-10-01 12:06:24 +09:00
|
|
|
app.use("/api/dashboards", dashboardRoutes);
|
2025-10-01 11:34:17 +09:00
|
|
|
app.use("/api/admin/reports", reportRoutes);
|
2025-10-13 19:04:28 +09:00
|
|
|
app.use("/api/open-api", openApiProxyRoutes); // 날씨/환율 외부 API
|
2025-10-14 16:36:00 +09:00
|
|
|
app.use("/api/delivery", deliveryRoutes); // 배송/화물 관리
|
|
|
|
|
app.use("/api/risk-alerts", riskAlertRoutes); // 리스크/알림 관리
|
2025-10-14 17:21:28 +09:00
|
|
|
app.use("/api/todos", todoRoutes); // To-Do 관리
|
|
|
|
|
app.use("/api/bookings", bookingRoutes); // 예약 요청 관리
|
2025-10-15 10:29:15 +09:00
|
|
|
app.use("/api/map-data", mapDataRoutes); // 지도 데이터 조회
|
2025-11-25 15:06:55 +09:00
|
|
|
app.use("/api/yard-layouts", yardLayoutRoutes); // 3D 필드
|
2025-10-20 15:53:00 +09:00
|
|
|
// app.use("/api/materials", materialRoutes); // 자재 관리 (임시 주석)
|
2025-11-20 10:15:58 +09:00
|
|
|
app.use("/api/digital-twin", digitalTwinRoutes); // 디지털 트윈 (야드 관제)
|
2025-10-20 17:50:27 +09:00
|
|
|
app.use("/api/flow-external-db", flowExternalDbConnectionRoutes); // 플로우 전용 외부 DB 연결
|
|
|
|
|
app.use("/api/flow", flowRoutes); // 플로우 관리 (마지막에 등록하여 다른 라우트와 충돌 방지)
|
2025-10-20 14:07:08 +09:00
|
|
|
app.use("/api/work-history", workHistoryRoutes); // 작업 이력 관리
|
2025-10-27 11:11:08 +09:00
|
|
|
app.use("/api/table-history", tableHistoryRoutes); // 테이블 변경 이력 조회
|
2025-10-27 16:40:59 +09:00
|
|
|
app.use("/api/roles", roleRoutes); // 권한 그룹 관리
|
2025-11-03 16:31:03 +09:00
|
|
|
app.use("/api/departments", departmentRoutes); // 부서 관리
|
2025-11-05 15:23:57 +09:00
|
|
|
app.use("/api/table-categories", tableCategoryValueRoutes); // 카테고리 값 관리
|
2025-11-04 18:31:26 +09:00
|
|
|
app.use("/api/code-merge", codeMergeRoutes); // 코드 병합
|
2025-11-05 18:13:06 +09:00
|
|
|
app.use("/api/numbering-rules", numberingRuleRoutes); // 채번 규칙 관리
|
2025-11-14 14:43:53 +09:00
|
|
|
app.use("/api/entity-search", entitySearchRoutes); // 엔티티 검색
|
2025-12-01 18:41:02 +09:00
|
|
|
app.use("/api/driver", driverRoutes); // 공차중계 운전자 관리
|
2025-12-08 16:01:59 +09:00
|
|
|
app.use("/api/tax-invoice", taxInvoiceRoutes); // 세금계산서 관리
|
2025-12-10 13:53:44 +09:00
|
|
|
app.use("/api/cascading-relations", cascadingRelationRoutes); // 연쇄 드롭다운 관계 관리
|
2025-12-10 15:59:04 +09:00
|
|
|
app.use("/api/cascading-auto-fill", cascadingAutoFillRoutes); // 자동 입력 관리
|
|
|
|
|
app.use("/api/cascading-conditions", cascadingConditionRoutes); // 조건부 연쇄 관리
|
|
|
|
|
app.use("/api/cascading-exclusions", cascadingMutualExclusionRoutes); // 상호 배제 관리
|
|
|
|
|
app.use("/api/cascading-hierarchy", cascadingHierarchyRoutes); // 다단계 계층 관리
|
2025-11-27 12:08:32 +09:00
|
|
|
app.use("/api", screenEmbeddingRoutes); // 화면 임베딩 및 데이터 전달
|
2025-12-02 09:53:08 +09:00
|
|
|
app.use("/api/vehicle", vehicleTripRoutes); // 차량 운행 이력 관리
|
2025-09-24 10:04:25 +09:00
|
|
|
// app.use("/api/collections", collectionRoutes); // 임시 주석
|
|
|
|
|
// app.use("/api/batch", batchRoutes); // 임시 주석
|
2025-08-21 09:41:46 +09:00
|
|
|
// 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;
|
2025-09-04 15:20:26 +09:00
|
|
|
const HOST = config.host;
|
2025-08-21 09:41:46 +09:00
|
|
|
|
2025-09-25 11:04:16 +09:00
|
|
|
app.listen(PORT, HOST, async () => {
|
2025-09-04 15:20:26 +09:00
|
|
|
logger.info(`🚀 Server is running on ${HOST}:${PORT}`);
|
2025-08-21 09:41:46 +09:00
|
|
|
logger.info(`📊 Environment: ${config.nodeEnv}`);
|
2025-09-04 15:20:26 +09:00
|
|
|
logger.info(`🔗 Health check: http://${HOST}:${PORT}/health`);
|
|
|
|
|
logger.info(`🌐 External access: http://39.117.244.52:${PORT}/health`);
|
2025-09-29 18:04:56 +09:00
|
|
|
|
2025-10-27 11:11:08 +09:00
|
|
|
// 데이터베이스 마이그레이션 실행
|
2025-10-16 14:59:07 +09:00
|
|
|
try {
|
2025-10-27 11:11:08 +09:00
|
|
|
const {
|
|
|
|
|
runDashboardMigration,
|
|
|
|
|
runTableHistoryActionMigration,
|
|
|
|
|
runDtgManagementLogMigration,
|
|
|
|
|
} = await import("./database/runMigration");
|
|
|
|
|
|
2025-10-16 14:59:07 +09:00
|
|
|
await runDashboardMigration();
|
2025-10-27 11:11:08 +09:00
|
|
|
await runTableHistoryActionMigration();
|
|
|
|
|
await runDtgManagementLogMigration();
|
2025-10-16 14:59:07 +09:00
|
|
|
} catch (error) {
|
2025-10-27 11:11:08 +09:00
|
|
|
logger.error(`❌ 마이그레이션 실패:`, error);
|
2025-10-16 14:59:07 +09:00
|
|
|
}
|
|
|
|
|
|
2025-09-25 11:04:16 +09:00
|
|
|
// 배치 스케줄러 초기화
|
|
|
|
|
try {
|
2025-12-01 11:34:22 +09:00
|
|
|
await BatchSchedulerService.initializeScheduler();
|
2025-09-25 11:04:16 +09:00
|
|
|
logger.info(`⏰ 배치 스케줄러가 시작되었습니다.`);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`❌ 배치 스케줄러 초기화 실패:`, error);
|
|
|
|
|
}
|
2025-10-14 16:36:00 +09:00
|
|
|
|
|
|
|
|
// 리스크/알림 자동 갱신 시작
|
|
|
|
|
try {
|
2025-10-16 15:39:54 +09:00
|
|
|
const { RiskAlertCacheService } = await import(
|
|
|
|
|
"./services/riskAlertCacheService"
|
|
|
|
|
);
|
2025-10-14 16:36:00 +09:00
|
|
|
const cacheService = RiskAlertCacheService.getInstance();
|
|
|
|
|
cacheService.startAutoRefresh();
|
|
|
|
|
logger.info(`⏰ 리스크/알림 자동 갱신이 시작되었습니다. (10분 간격)`);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`❌ 리스크/알림 자동 갱신 시작 실패:`, error);
|
|
|
|
|
}
|
2025-10-22 16:06:04 +09:00
|
|
|
|
|
|
|
|
// 메일 자동 삭제 (30일 지난 삭제된 메일) - 매일 새벽 2시 실행
|
|
|
|
|
try {
|
|
|
|
|
const cron = await import("node-cron");
|
|
|
|
|
const { mailSentHistoryService } = await import(
|
|
|
|
|
"./services/mailSentHistoryService"
|
|
|
|
|
);
|
2025-10-27 11:11:08 +09:00
|
|
|
|
2025-10-22 16:06:04 +09:00
|
|
|
cron.schedule("0 2 * * *", async () => {
|
|
|
|
|
try {
|
|
|
|
|
logger.info("🗑️ 30일 지난 삭제된 메일 자동 삭제 시작...");
|
2025-10-27 11:11:08 +09:00
|
|
|
const deletedCount =
|
|
|
|
|
await mailSentHistoryService.cleanupOldDeletedMails();
|
2025-10-22 16:06:04 +09:00
|
|
|
logger.info(`✅ 30일 지난 메일 ${deletedCount}개 자동 삭제 완료`);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error("❌ 메일 자동 삭제 실패:", error);
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-10-27 11:11:08 +09:00
|
|
|
|
2025-10-22 16:06:04 +09:00
|
|
|
logger.info(`⏰ 메일 자동 삭제 스케줄러가 시작되었습니다. (매일 새벽 2시)`);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`❌ 메일 자동 삭제 스케줄러 시작 실패:`, error);
|
|
|
|
|
}
|
2025-08-21 09:41:46 +09:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export default app;
|