로그시스템 개선
This commit is contained in:
@@ -62,6 +62,7 @@ import yardLayoutRoutes from "./routes/yardLayoutRoutes"; // 야드 관리 3D
|
||||
import flowRoutes from "./routes/flowRoutes"; // 플로우 관리
|
||||
import flowExternalDbConnectionRoutes from "./routes/flowExternalDbConnectionRoutes"; // 플로우 전용 외부 DB 연결
|
||||
import workHistoryRoutes from "./routes/workHistoryRoutes"; // 작업 이력 관리
|
||||
import tableHistoryRoutes from "./routes/tableHistoryRoutes"; // 테이블 변경 이력 조회
|
||||
import { BatchSchedulerService } from "./services/batchSchedulerService";
|
||||
// import collectionRoutes from "./routes/collectionRoutes"; // 임시 주석
|
||||
// import batchRoutes from "./routes/batchRoutes"; // 임시 주석
|
||||
@@ -218,6 +219,7 @@ app.use("/api/yard-layouts", yardLayoutRoutes); // 야드 관리 3D
|
||||
app.use("/api/flow-external-db", flowExternalDbConnectionRoutes); // 플로우 전용 외부 DB 연결
|
||||
app.use("/api/flow", flowRoutes); // 플로우 관리 (마지막에 등록하여 다른 라우트와 충돌 방지)
|
||||
app.use("/api/work-history", workHistoryRoutes); // 작업 이력 관리
|
||||
app.use("/api/table-history", tableHistoryRoutes); // 테이블 변경 이력 조회
|
||||
// app.use("/api/collections", collectionRoutes); // 임시 주석
|
||||
// app.use("/api/batch", batchRoutes); // 임시 주석
|
||||
// app.use('/api/users', userRoutes);
|
||||
@@ -245,12 +247,19 @@ app.listen(PORT, HOST, async () => {
|
||||
logger.info(`🔗 Health check: http://${HOST}:${PORT}/health`);
|
||||
logger.info(`🌐 External access: http://39.117.244.52:${PORT}/health`);
|
||||
|
||||
// 대시보드 마이그레이션 실행
|
||||
// 데이터베이스 마이그레이션 실행
|
||||
try {
|
||||
const { runDashboardMigration } = await import("./database/runMigration");
|
||||
const {
|
||||
runDashboardMigration,
|
||||
runTableHistoryActionMigration,
|
||||
runDtgManagementLogMigration,
|
||||
} = await import("./database/runMigration");
|
||||
|
||||
await runDashboardMigration();
|
||||
await runTableHistoryActionMigration();
|
||||
await runDtgManagementLogMigration();
|
||||
} catch (error) {
|
||||
logger.error(`❌ 대시보드 마이그레이션 실패:`, error);
|
||||
logger.error(`❌ 마이그레이션 실패:`, error);
|
||||
}
|
||||
|
||||
// 배치 스케줄러 초기화
|
||||
@@ -279,17 +288,18 @@ app.listen(PORT, HOST, async () => {
|
||||
const { mailSentHistoryService } = await import(
|
||||
"./services/mailSentHistoryService"
|
||||
);
|
||||
|
||||
|
||||
cron.schedule("0 2 * * *", async () => {
|
||||
try {
|
||||
logger.info("🗑️ 30일 지난 삭제된 메일 자동 삭제 시작...");
|
||||
const deletedCount = await mailSentHistoryService.cleanupOldDeletedMails();
|
||||
const deletedCount =
|
||||
await mailSentHistoryService.cleanupOldDeletedMails();
|
||||
logger.info(`✅ 30일 지난 메일 ${deletedCount}개 자동 삭제 완료`);
|
||||
} catch (error) {
|
||||
logger.error("❌ 메일 자동 삭제 실패:", error);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
logger.info(`⏰ 메일 자동 삭제 스케줄러가 시작되었습니다. (매일 새벽 2시)`);
|
||||
} catch (error) {
|
||||
logger.error(`❌ 메일 자동 삭제 스케줄러 시작 실패:`, error);
|
||||
|
||||
406
backend-node/src/controllers/tableHistoryController.ts
Normal file
406
backend-node/src/controllers/tableHistoryController.ts
Normal file
@@ -0,0 +1,406 @@
|
||||
/**
|
||||
* 테이블 이력 조회 컨트롤러
|
||||
* 테이블 타입 관리의 {테이블명}_log 테이블과 연동
|
||||
*/
|
||||
|
||||
import { Request, Response } from "express";
|
||||
import { query } from "../database/db";
|
||||
import { logger } from "../utils/logger";
|
||||
|
||||
export class TableHistoryController {
|
||||
/**
|
||||
* 특정 레코드의 변경 이력 조회
|
||||
*/
|
||||
static async getRecordHistory(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tableName, recordId } = req.params;
|
||||
const { limit = 50, offset = 0, operationType, changedBy, startDate, endDate } = req.query;
|
||||
|
||||
logger.info(`📜 테이블 이력 조회 요청:`, {
|
||||
tableName,
|
||||
recordId,
|
||||
limit,
|
||||
offset,
|
||||
});
|
||||
|
||||
// 로그 테이블명 생성
|
||||
const logTableName = `${tableName}_log`;
|
||||
|
||||
// 동적 WHERE 조건 생성
|
||||
const whereConditions: string[] = [`original_id = $1`];
|
||||
const queryParams: any[] = [recordId];
|
||||
let paramIndex = 2;
|
||||
|
||||
// 작업 유형 필터
|
||||
if (operationType) {
|
||||
whereConditions.push(`operation_type = $${paramIndex}`);
|
||||
queryParams.push(operationType);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
// 변경자 필터
|
||||
if (changedBy) {
|
||||
whereConditions.push(`changed_by ILIKE $${paramIndex}`);
|
||||
queryParams.push(`%${changedBy}%`);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
// 날짜 범위 필터
|
||||
if (startDate) {
|
||||
whereConditions.push(`changed_at >= $${paramIndex}`);
|
||||
queryParams.push(startDate);
|
||||
paramIndex++;
|
||||
}
|
||||
if (endDate) {
|
||||
whereConditions.push(`changed_at <= $${paramIndex}`);
|
||||
queryParams.push(endDate);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
// LIMIT과 OFFSET 파라미터 추가
|
||||
queryParams.push(limit);
|
||||
const limitParam = `$${paramIndex}`;
|
||||
paramIndex++;
|
||||
|
||||
queryParams.push(offset);
|
||||
const offsetParam = `$${paramIndex}`;
|
||||
|
||||
const whereClause = whereConditions.join(" AND ");
|
||||
|
||||
// 이력 조회 쿼리
|
||||
const historyQuery = `
|
||||
SELECT
|
||||
log_id,
|
||||
operation_type,
|
||||
original_id,
|
||||
changed_column,
|
||||
old_value,
|
||||
new_value,
|
||||
changed_by,
|
||||
changed_at,
|
||||
ip_address,
|
||||
user_agent,
|
||||
full_row_before,
|
||||
full_row_after
|
||||
FROM ${logTableName}
|
||||
WHERE ${whereClause}
|
||||
ORDER BY changed_at DESC
|
||||
LIMIT ${limitParam} OFFSET ${offsetParam}
|
||||
`;
|
||||
|
||||
// 전체 카운트 쿼리
|
||||
const countQuery = `
|
||||
SELECT COUNT(*) as total
|
||||
FROM ${logTableName}
|
||||
WHERE ${whereClause}
|
||||
`;
|
||||
|
||||
const [historyRecords, countResult] = await Promise.all([
|
||||
query<any>(historyQuery, queryParams),
|
||||
query<any>(countQuery, queryParams.slice(0, -2)), // LIMIT, OFFSET 제외
|
||||
]);
|
||||
|
||||
const total = parseInt(countResult[0]?.total || "0", 10);
|
||||
|
||||
logger.info(`✅ 이력 조회 완료: ${historyRecords.length}건 / 전체 ${total}건`);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
records: historyRecords,
|
||||
pagination: {
|
||||
total,
|
||||
limit: parseInt(limit as string, 10),
|
||||
offset: parseInt(offset as string, 10),
|
||||
hasMore: parseInt(offset as string, 10) + historyRecords.length < total,
|
||||
},
|
||||
},
|
||||
message: "이력 조회 성공",
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error(`❌ 테이블 이력 조회 실패:`, error);
|
||||
|
||||
// 테이블이 존재하지 않는 경우
|
||||
if (error.code === "42P01") {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
message: "이력 테이블이 존재하지 않습니다. 테이블 타입 관리에서 이력 관리를 활성화해주세요.",
|
||||
errorCode: "TABLE_NOT_FOUND",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "이력 조회 중 오류가 발생했습니다.",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 전체 테이블 이력 조회 (레코드 ID 없이)
|
||||
*/
|
||||
static async getAllTableHistory(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tableName } = req.params;
|
||||
const { limit = 50, offset = 0, operationType, changedBy, startDate, endDate } = req.query;
|
||||
|
||||
logger.info(`📜 전체 테이블 이력 조회 요청:`, {
|
||||
tableName,
|
||||
limit,
|
||||
offset,
|
||||
});
|
||||
|
||||
// 로그 테이블명 생성
|
||||
const logTableName = `${tableName}_log`;
|
||||
|
||||
// 동적 WHERE 조건 생성
|
||||
const whereConditions: string[] = [];
|
||||
const queryParams: any[] = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
// 작업 유형 필터
|
||||
if (operationType) {
|
||||
whereConditions.push(`operation_type = $${paramIndex}`);
|
||||
queryParams.push(operationType);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
// 변경자 필터
|
||||
if (changedBy) {
|
||||
whereConditions.push(`changed_by ILIKE $${paramIndex}`);
|
||||
queryParams.push(`%${changedBy}%`);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
// 날짜 범위 필터
|
||||
if (startDate) {
|
||||
whereConditions.push(`changed_at >= $${paramIndex}`);
|
||||
queryParams.push(startDate);
|
||||
paramIndex++;
|
||||
}
|
||||
if (endDate) {
|
||||
whereConditions.push(`changed_at <= $${paramIndex}`);
|
||||
queryParams.push(endDate);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
// LIMIT과 OFFSET 파라미터 추가
|
||||
queryParams.push(limit);
|
||||
const limitParam = `$${paramIndex}`;
|
||||
paramIndex++;
|
||||
|
||||
queryParams.push(offset);
|
||||
const offsetParam = `$${paramIndex}`;
|
||||
|
||||
const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(" AND ")}` : "";
|
||||
|
||||
// 이력 조회 쿼리
|
||||
const historyQuery = `
|
||||
SELECT
|
||||
log_id,
|
||||
operation_type,
|
||||
original_id,
|
||||
changed_column,
|
||||
old_value,
|
||||
new_value,
|
||||
changed_by,
|
||||
changed_at,
|
||||
ip_address,
|
||||
user_agent,
|
||||
full_row_before,
|
||||
full_row_after
|
||||
FROM ${logTableName}
|
||||
${whereClause}
|
||||
ORDER BY changed_at DESC
|
||||
LIMIT ${limitParam} OFFSET ${offsetParam}
|
||||
`;
|
||||
|
||||
// 전체 카운트 쿼리
|
||||
const countQuery = `
|
||||
SELECT COUNT(*) as total
|
||||
FROM ${logTableName}
|
||||
${whereClause}
|
||||
`;
|
||||
|
||||
const [historyRecords, countResult] = await Promise.all([
|
||||
query<any>(historyQuery, queryParams),
|
||||
query<any>(countQuery, queryParams.slice(0, -2)), // LIMIT, OFFSET 제외
|
||||
]);
|
||||
|
||||
const total = parseInt(countResult[0]?.total || "0", 10);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
records: historyRecords,
|
||||
pagination: {
|
||||
total,
|
||||
limit: Number(limit),
|
||||
offset: Number(offset),
|
||||
hasMore: Number(offset) + Number(limit) < total,
|
||||
},
|
||||
},
|
||||
message: "전체 테이블 이력 조회 성공",
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error(`❌ 전체 테이블 이력 조회 실패:`, error);
|
||||
|
||||
if (error.code === "42P01") {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
message: "이력 테이블이 존재하지 않습니다.",
|
||||
errorCode: "TABLE_NOT_FOUND",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "이력 조회 중 오류가 발생했습니다.",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 테이블 전체 이력 요약 조회
|
||||
*/
|
||||
static async getTableHistorySummary(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tableName } = req.params;
|
||||
const logTableName = `${tableName}_log`;
|
||||
|
||||
const summaryQuery = `
|
||||
SELECT
|
||||
operation_type,
|
||||
COUNT(*) as count,
|
||||
COUNT(DISTINCT original_id) as affected_records,
|
||||
COUNT(DISTINCT changed_by) as unique_users,
|
||||
MIN(changed_at) as first_change,
|
||||
MAX(changed_at) as last_change
|
||||
FROM ${logTableName}
|
||||
GROUP BY operation_type
|
||||
`;
|
||||
|
||||
const summary = await query<any>(summaryQuery);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: summary,
|
||||
message: "이력 요약 조회 성공",
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error(`❌ 테이블 이력 요약 조회 실패:`, error);
|
||||
|
||||
if (error.code === "42P01") {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
message: "이력 테이블이 존재하지 않습니다.",
|
||||
errorCode: "TABLE_NOT_FOUND",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "이력 요약 조회 중 오류가 발생했습니다.",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 레코드의 변경 타임라인 조회 (그룹화)
|
||||
*/
|
||||
static async getRecordTimeline(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tableName, recordId } = req.params;
|
||||
const logTableName = `${tableName}_log`;
|
||||
|
||||
// 변경 이벤트별로 그룹화 (동일 시간대 변경을 하나의 이벤트로)
|
||||
const timelineQuery = `
|
||||
WITH grouped_changes AS (
|
||||
SELECT
|
||||
changed_at,
|
||||
changed_by,
|
||||
operation_type,
|
||||
ip_address,
|
||||
json_agg(
|
||||
json_build_object(
|
||||
'column', changed_column,
|
||||
'oldValue', old_value,
|
||||
'newValue', new_value
|
||||
) ORDER BY changed_column
|
||||
) as changes,
|
||||
full_row_before,
|
||||
full_row_after
|
||||
FROM ${logTableName}
|
||||
WHERE original_id = $1
|
||||
GROUP BY changed_at, changed_by, operation_type, ip_address, full_row_before, full_row_after
|
||||
ORDER BY changed_at DESC
|
||||
LIMIT 100
|
||||
)
|
||||
SELECT * FROM grouped_changes
|
||||
`;
|
||||
|
||||
const timeline = await query<any>(timelineQuery, [recordId]);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: timeline,
|
||||
message: "타임라인 조회 성공",
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error(`❌ 레코드 타임라인 조회 실패:`, error);
|
||||
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "타임라인 조회 중 오류가 발생했습니다.",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 이력 테이블 존재 여부 확인
|
||||
*/
|
||||
static async checkHistoryTableExists(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tableName } = req.params;
|
||||
const logTableName = `${tableName}_log`;
|
||||
|
||||
const checkQuery = `
|
||||
SELECT EXISTS (
|
||||
SELECT FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = $1
|
||||
) as exists
|
||||
`;
|
||||
|
||||
const result = await query<any>(checkQuery, [logTableName]);
|
||||
const exists = result[0]?.exists || false;
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
tableName,
|
||||
logTableName,
|
||||
exists,
|
||||
historyEnabled: exists,
|
||||
},
|
||||
message: exists ? "이력 테이블이 존재합니다." : "이력 테이블이 존재하지 않습니다.",
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error(`❌ 이력 테이블 존재 여부 확인 실패:`, error);
|
||||
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "이력 테이블 확인 중 오류가 발생했습니다.",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { PostgreSQLService } from './PostgreSQLService';
|
||||
import { PostgreSQLService } from "./PostgreSQLService";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
/**
|
||||
* 데이터베이스 마이그레이션 실행
|
||||
@@ -6,21 +8,21 @@ import { PostgreSQLService } from './PostgreSQLService';
|
||||
*/
|
||||
export async function runDashboardMigration() {
|
||||
try {
|
||||
console.log('🔄 대시보드 마이그레이션 시작...');
|
||||
console.log("🔄 대시보드 마이그레이션 시작...");
|
||||
|
||||
// custom_title 컬럼 추가
|
||||
await PostgreSQLService.query(`
|
||||
ALTER TABLE dashboard_elements
|
||||
ADD COLUMN IF NOT EXISTS custom_title VARCHAR(255)
|
||||
`);
|
||||
console.log('✅ custom_title 컬럼 추가 완료');
|
||||
console.log("✅ custom_title 컬럼 추가 완료");
|
||||
|
||||
// show_header 컬럼 추가
|
||||
await PostgreSQLService.query(`
|
||||
ALTER TABLE dashboard_elements
|
||||
ADD COLUMN IF NOT EXISTS show_header BOOLEAN DEFAULT true
|
||||
`);
|
||||
console.log('✅ show_header 컬럼 추가 완료');
|
||||
console.log("✅ show_header 컬럼 추가 완료");
|
||||
|
||||
// 기존 데이터 업데이트
|
||||
await PostgreSQLService.query(`
|
||||
@@ -28,15 +30,83 @@ export async function runDashboardMigration() {
|
||||
SET show_header = true
|
||||
WHERE show_header IS NULL
|
||||
`);
|
||||
console.log('✅ 기존 데이터 업데이트 완료');
|
||||
console.log("✅ 기존 데이터 업데이트 완료");
|
||||
|
||||
console.log('✅ 대시보드 마이그레이션 완료!');
|
||||
console.log("✅ 대시보드 마이그레이션 완료!");
|
||||
} catch (error) {
|
||||
console.error('❌ 대시보드 마이그레이션 실패:', error);
|
||||
console.error("❌ 대시보드 마이그레이션 실패:", error);
|
||||
// 이미 컬럼이 있는 경우는 무시
|
||||
if (error instanceof Error && error.message.includes('already exists')) {
|
||||
console.log('ℹ️ 컬럼이 이미 존재합니다.');
|
||||
if (error instanceof Error && error.message.includes("already exists")) {
|
||||
console.log("ℹ️ 컬럼이 이미 존재합니다.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 테이블 이력 보기 버튼 액션 마이그레이션
|
||||
*/
|
||||
export async function runTableHistoryActionMigration() {
|
||||
try {
|
||||
console.log("🔄 테이블 이력 보기 액션 마이그레이션 시작...");
|
||||
|
||||
// SQL 파일 읽기
|
||||
const sqlFilePath = path.join(
|
||||
__dirname,
|
||||
"../../db/migrations/024_add_table_history_view_action.sql"
|
||||
);
|
||||
|
||||
if (!fs.existsSync(sqlFilePath)) {
|
||||
console.log("⚠️ 마이그레이션 파일이 없습니다:", sqlFilePath);
|
||||
return;
|
||||
}
|
||||
|
||||
const sqlContent = fs.readFileSync(sqlFilePath, "utf8");
|
||||
|
||||
// SQL 실행
|
||||
await PostgreSQLService.query(sqlContent);
|
||||
|
||||
console.log("✅ 테이블 이력 보기 액션 마이그레이션 완료!");
|
||||
} catch (error) {
|
||||
console.error("❌ 테이블 이력 보기 액션 마이그레이션 실패:", error);
|
||||
// 이미 액션이 있는 경우는 무시
|
||||
if (
|
||||
error instanceof Error &&
|
||||
error.message.includes("duplicate key value")
|
||||
) {
|
||||
console.log("ℹ️ 액션이 이미 존재합니다.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DTG Management 테이블 이력 시스템 마이그레이션
|
||||
*/
|
||||
export async function runDtgManagementLogMigration() {
|
||||
try {
|
||||
console.log("🔄 DTG Management 이력 테이블 마이그레이션 시작...");
|
||||
|
||||
// SQL 파일 읽기
|
||||
const sqlFilePath = path.join(
|
||||
__dirname,
|
||||
"../../db/migrations/025_create_dtg_management_log.sql"
|
||||
);
|
||||
|
||||
if (!fs.existsSync(sqlFilePath)) {
|
||||
console.log("⚠️ 마이그레이션 파일이 없습니다:", sqlFilePath);
|
||||
return;
|
||||
}
|
||||
|
||||
const sqlContent = fs.readFileSync(sqlFilePath, "utf8");
|
||||
|
||||
// SQL 실행
|
||||
await PostgreSQLService.query(sqlContent);
|
||||
|
||||
console.log("✅ DTG Management 이력 테이블 마이그레이션 완료!");
|
||||
} catch (error) {
|
||||
console.error("❌ DTG Management 이력 테이블 마이그레이션 실패:", error);
|
||||
// 이미 테이블이 있는 경우는 무시
|
||||
if (error instanceof Error && error.message.includes("already exists")) {
|
||||
console.log("ℹ️ 이력 테이블이 이미 존재합니다.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
35
backend-node/src/routes/tableHistoryRoutes.ts
Normal file
35
backend-node/src/routes/tableHistoryRoutes.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 테이블 이력 조회 라우트
|
||||
*/
|
||||
|
||||
import { Router } from "express";
|
||||
import { authenticateToken } from "../middleware/authMiddleware";
|
||||
import { TableHistoryController } from "../controllers/tableHistoryController";
|
||||
|
||||
const router = Router();
|
||||
|
||||
// 모든 라우트에 인증 미들웨어 적용
|
||||
router.use(authenticateToken);
|
||||
|
||||
// 이력 테이블 존재 여부 확인
|
||||
router.get("/:tableName/check", TableHistoryController.checkHistoryTableExists);
|
||||
|
||||
// 테이블 전체 이력 요약
|
||||
router.get(
|
||||
"/:tableName/summary",
|
||||
TableHistoryController.getTableHistorySummary
|
||||
);
|
||||
|
||||
// 전체 테이블 이력 조회 (레코드 ID 없이)
|
||||
router.get("/:tableName/all", TableHistoryController.getAllTableHistory);
|
||||
|
||||
// 특정 레코드의 타임라인
|
||||
router.get(
|
||||
"/:tableName/:recordId/timeline",
|
||||
TableHistoryController.getRecordTimeline
|
||||
);
|
||||
|
||||
// 특정 레코드의 변경 이력 (상세)
|
||||
router.get("/:tableName/:recordId", TableHistoryController.getRecordHistory);
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user