Merge branch 'dev' of http://39.117.244.52:3000/kjs/ERP-node into dataflowMng

This commit is contained in:
hyeonsu
2025-09-15 20:07:44 +09:00
252 changed files with 33302 additions and 2046 deletions

View File

@@ -25,6 +25,12 @@ router.get(
componentStandardController.getStatistics.bind(componentStandardController)
);
// 컴포넌트 코드 중복 체크
router.get(
"/check-duplicate/:component_code",
componentStandardController.checkDuplicate.bind(componentStandardController)
);
// 컴포넌트 상세 조회
router.get(
"/:component_code",

View File

@@ -0,0 +1,130 @@
import express from "express";
import { dataService } from "../services/dataService";
import { authenticateToken } from "../middleware/authMiddleware";
import { AuthenticatedRequest } from "../types/auth";
const router = express.Router();
/**
* 동적 테이블 데이터 조회 API
* GET /api/data/{tableName}
*/
router.get(
"/:tableName",
authenticateToken,
async (req: AuthenticatedRequest, res) => {
try {
const { tableName } = req.params;
const { limit = "10", offset = "0", orderBy, ...filters } = req.query;
// 입력값 검증
if (!tableName || typeof tableName !== "string") {
return res.status(400).json({
success: false,
message: "테이블명이 필요합니다.",
error: "INVALID_TABLE_NAME",
});
}
// SQL 인젝션 방지를 위한 테이블명 검증
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tableName)) {
return res.status(400).json({
success: false,
message: "유효하지 않은 테이블명입니다.",
error: "INVALID_TABLE_NAME",
});
}
console.log(`📊 데이터 조회 요청: ${tableName}`, {
limit: parseInt(limit as string),
offset: parseInt(offset as string),
orderBy: orderBy as string,
filters,
user: req.user?.userId,
});
// 데이터 조회
const result = await dataService.getTableData({
tableName,
limit: parseInt(limit as string),
offset: parseInt(offset as string),
orderBy: orderBy as string,
filters: filters as Record<string, string>,
userCompany: req.user?.companyCode,
});
if (!result.success) {
return res.status(400).json(result);
}
console.log(
`✅ 데이터 조회 성공: ${tableName}, ${result.data?.length || 0}개 항목`
);
return res.json(result.data);
} catch (error) {
console.error("데이터 조회 오류:", error);
return res.status(500).json({
success: false,
message: "데이터 조회 중 오류가 발생했습니다.",
error: error instanceof Error ? error.message : "Unknown error",
});
}
}
);
/**
* 테이블 컬럼 정보 조회 API
* GET /api/data/{tableName}/columns
*/
router.get(
"/:tableName/columns",
authenticateToken,
async (req: AuthenticatedRequest, res) => {
try {
const { tableName } = req.params;
// 입력값 검증
if (!tableName || typeof tableName !== "string") {
return res.status(400).json({
success: false,
message: "테이블명이 필요합니다.",
error: "INVALID_TABLE_NAME",
});
}
// SQL 인젝션 방지를 위한 테이블명 검증
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tableName)) {
return res.status(400).json({
success: false,
message: "유효하지 않은 테이블명입니다.",
error: "INVALID_TABLE_NAME",
});
}
console.log(`📋 컬럼 정보 조회: ${tableName}`);
// 컬럼 정보 조회
const result = await dataService.getTableColumns(tableName);
if (!result.success) {
return res.status(400).json(result);
}
console.log(
`✅ 컬럼 정보 조회 성공: ${tableName}, ${result.data?.length || 0}개 컬럼`
);
return res.json(result);
} catch (error) {
console.error("컬럼 정보 조회 오류:", error);
return res.status(500).json({
success: false,
message: "컬럼 정보 조회 중 오류가 발생했습니다.",
error: error instanceof Error ? error.message : "Unknown error",
});
}
}
);
export default router;

View File

@@ -0,0 +1,73 @@
import { Router } from "express";
import { layoutController } from "../controllers/layoutController";
import { authenticateToken } from "../middleware/authMiddleware";
const router = Router();
// 모든 레이아웃 라우트에 인증 미들웨어 적용
router.use(authenticateToken);
/**
* @route GET /api/layouts
* @desc 레이아웃 목록 조회
* @access Private
* @params page, size, category, layoutType, searchTerm, includePublic
*/
router.get("/", layoutController.getLayouts.bind(layoutController));
/**
* @route GET /api/layouts/counts-by-category
* @desc 카테고리별 레이아웃 개수 조회
* @access Private
*/
router.get(
"/counts-by-category",
layoutController.getLayoutCountsByCategory.bind(layoutController)
);
/**
* @route GET /api/layouts/:id
* @desc 레이아웃 상세 조회
* @access Private
* @params id (layoutCode)
*/
router.get("/:id", layoutController.getLayoutById.bind(layoutController));
/**
* @route POST /api/layouts
* @desc 레이아웃 생성
* @access Private
* @body CreateLayoutRequest
*/
router.post("/", layoutController.createLayout.bind(layoutController));
/**
* @route PUT /api/layouts/:id
* @desc 레이아웃 수정
* @access Private
* @params id (layoutCode)
* @body Partial<CreateLayoutRequest>
*/
router.put("/:id", layoutController.updateLayout.bind(layoutController));
/**
* @route DELETE /api/layouts/:id
* @desc 레이아웃 삭제
* @access Private
* @params id (layoutCode)
*/
router.delete("/:id", layoutController.deleteLayout.bind(layoutController));
/**
* @route POST /api/layouts/:id/duplicate
* @desc 레이아웃 복제
* @access Private
* @params id (layoutCode)
* @body { newName: string }
*/
router.post(
"/:id/duplicate",
layoutController.duplicateLayout.bind(layoutController)
);
export default router;