ERP-node/backend-node/src/controllers/ddlController.ts

493 lines
12 KiB
TypeScript
Raw Normal View History

/**
* DDL
* / API
*/
import { Response } from "express";
import { AuthenticatedRequest } from "../middleware/superAdminMiddleware";
import { DDLExecutionService } from "../services/ddlExecutionService";
import { DDLAuditLogger } from "../services/ddlAuditLogger";
import { CreateTableRequest, AddColumnRequest } from "../types/ddl";
import { logger } from "../utils/logger";
export class DDLController {
/**
* POST /api/ddl/tables -
*/
static async createTable(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const { tableName, columns, description }: CreateTableRequest = req.body;
const userId = req.user!.userId;
const userCompanyCode = req.user!.companyCode;
// 입력값 기본 검증
if (!tableName || !columns || columns.length === 0) {
res.status(400).json({
success: false,
error: {
code: "INVALID_INPUT",
details: "테이블명과 최소 1개의 컬럼이 필요합니다.",
},
});
return;
}
logger.info("테이블 생성 요청", {
tableName,
userId,
columnCount: columns.length,
ip: req.ip,
});
2025-09-23 10:40:21 +09:00
// inputType을 webType으로 변환 (레거시 호환성)
const processedColumns = columns.map((col) => ({
...col,
webType: (col.inputType || col.webType || "text") as any,
}));
// DDL 실행 서비스 호출
const ddlService = new DDLExecutionService();
const result = await ddlService.createTable(
tableName,
2025-09-23 10:40:21 +09:00
processedColumns,
userCompanyCode,
userId,
description
);
if (result.success) {
res.status(200).json({
success: true,
message: result.message,
data: {
tableName,
columnCount: columns.length,
executedQuery: result.executedQuery,
},
});
} else {
res.status(400).json({
success: false,
message: result.message,
error: result.error,
});
}
} catch (error) {
logger.error("테이블 생성 컨트롤러 오류:", {
error: (error as Error).message,
stack: (error as Error).stack,
userId: req.user?.userId,
body: req.body,
});
res.status(500).json({
success: false,
error: {
code: "INTERNAL_SERVER_ERROR",
details: "테이블 생성 중 서버 오류가 발생했습니다.",
},
});
}
}
/**
* POST /api/ddl/tables/:tableName/columns -
*/
static async addColumn(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const { tableName } = req.params;
const { column }: AddColumnRequest = req.body;
const userId = req.user!.userId;
const userCompanyCode = req.user!.companyCode;
// 입력값 기본 검증
if (!tableName) {
res.status(400).json({
success: false,
error: {
code: "INVALID_INPUT",
details: "테이블명이 필요합니다.",
},
});
return;
}
2025-09-23 10:40:21 +09:00
if (!column || !column.name || (!column.inputType && !column.webType)) {
res.status(400).json({
success: false,
error: {
code: "INVALID_INPUT",
2025-09-23 10:40:21 +09:00
details: "컬럼명과 입력타입이 필요합니다.",
},
});
return;
}
logger.info("컬럼 추가 요청", {
tableName,
columnName: column.name,
webType: column.webType,
userId,
ip: req.ip,
});
2025-09-23 10:40:21 +09:00
// inputType을 webType으로 변환 (레거시 호환성)
const processedColumn = {
...column,
webType: (column.inputType || column.webType || "text") as any,
};
// DDL 실행 서비스 호출
const ddlService = new DDLExecutionService();
const result = await ddlService.addColumn(
tableName,
2025-09-23 10:40:21 +09:00
processedColumn,
userCompanyCode,
userId
);
if (result.success) {
res.status(200).json({
success: true,
message: result.message,
data: {
tableName,
columnName: column.name,
webType: column.webType,
executedQuery: result.executedQuery,
},
});
} else {
res.status(400).json({
success: false,
message: result.message,
error: result.error,
});
}
} catch (error) {
logger.error("컬럼 추가 컨트롤러 오류:", {
error: (error as Error).message,
stack: (error as Error).stack,
userId: req.user?.userId,
tableName: req.params.tableName,
body: req.body,
});
res.status(500).json({
success: false,
error: {
code: "INTERNAL_SERVER_ERROR",
details: "컬럼 추가 중 서버 오류가 발생했습니다.",
},
});
}
}
/**
* GET /api/ddl/logs - DDL
*/
static async getDDLLogs(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const { limit, userId, ddlType } = req.query;
const logs = await DDLAuditLogger.getRecentDDLLogs(
limit ? parseInt(limit as string) : 50,
userId as string,
ddlType as string
);
res.json({
success: true,
data: {
logs,
total: logs.length,
},
});
} catch (error) {
logger.error("DDL 로그 조회 오류:", error);
res.status(500).json({
success: false,
error: {
code: "LOG_RETRIEVAL_FAILED",
details: "DDL 로그 조회 중 오류가 발생했습니다.",
},
});
}
}
/**
* GET /api/ddl/statistics - DDL
*/
static async getDDLStatistics(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const { fromDate, toDate } = req.query;
const statistics = await DDLAuditLogger.getDDLStatistics(
fromDate ? new Date(fromDate as string) : undefined,
toDate ? new Date(toDate as string) : undefined
);
res.json({
success: true,
data: statistics,
});
} catch (error) {
logger.error("DDL 통계 조회 오류:", error);
res.status(500).json({
success: false,
error: {
code: "STATISTICS_RETRIEVAL_FAILED",
details: "DDL 통계 조회 중 오류가 발생했습니다.",
},
});
}
}
/**
* GET /api/ddl/tables/:tableName/info -
*/
static async getTableInfo(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const { tableName } = req.params;
const ddlService = new DDLExecutionService();
const tableInfo = await ddlService.getCreatedTableInfo(tableName);
if (!tableInfo) {
res.status(404).json({
success: false,
error: {
code: "TABLE_NOT_FOUND",
details: `테이블 '${tableName}'을 찾을 수 없습니다.`,
},
});
return;
}
res.json({
success: true,
data: tableInfo,
});
} catch (error) {
logger.error("테이블 정보 조회 오류:", error);
res.status(500).json({
success: false,
error: {
code: "TABLE_INFO_RETRIEVAL_FAILED",
details: "테이블 정보 조회 중 오류가 발생했습니다.",
},
});
}
}
/**
* GET /api/ddl/tables/:tableName/history - DDL
*/
static async getTableDDLHistory(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const { tableName } = req.params;
const history = await DDLAuditLogger.getTableDDLHistory(tableName);
res.json({
success: true,
data: {
tableName,
history,
total: history.length,
},
});
} catch (error) {
logger.error("테이블 DDL 히스토리 조회 오류:", error);
res.status(500).json({
success: false,
error: {
code: "HISTORY_RETRIEVAL_FAILED",
details: "테이블 DDL 히스토리 조회 중 오류가 발생했습니다.",
},
});
}
}
/**
* POST /api/ddl/validate/table -
*/
static async validateTableCreation(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const { tableName, columns }: CreateTableRequest = req.body;
if (!tableName || !columns) {
res.status(400).json({
success: false,
error: {
code: "INVALID_INPUT",
details: "테이블명과 컬럼 정보가 필요합니다.",
},
});
return;
}
// 검증만 수행 (실제 생성하지 않음)
const { DDLSafetyValidator } = await import(
"../services/ddlSafetyValidator"
);
const validationReport = DDLSafetyValidator.generateValidationReport(
tableName,
columns
);
res.json({
success: true,
data: {
isValid: validationReport.validationResult.isValid,
errors: validationReport.validationResult.errors,
warnings: validationReport.validationResult.warnings,
summary: validationReport.summary,
},
});
} catch (error) {
logger.error("테이블 생성 검증 오류:", error);
res.status(500).json({
success: false,
error: {
code: "VALIDATION_ERROR",
details: "테이블 생성 검증 중 오류가 발생했습니다.",
},
});
}
}
/**
* DELETE /api/ddl/tables/:tableName - ( )
*/
static async dropTable(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const { tableName } = req.params;
const userId = req.user!.userId;
const userCompanyCode = req.user!.companyCode;
// 입력값 기본 검증
if (!tableName) {
res.status(400).json({
success: false,
error: {
code: "INVALID_INPUT",
details: "테이블명이 필요합니다.",
},
});
return;
}
logger.info("테이블 삭제 요청", {
tableName,
userId,
userCompanyCode,
ip: req.ip,
});
// DDL 실행 서비스 호출
const ddlService = new DDLExecutionService();
const result = await ddlService.dropTable(
tableName,
userCompanyCode,
userId
);
if (result.success) {
res.status(200).json({
success: true,
message: result.message,
data: {
tableName,
executedQuery: result.executedQuery,
},
});
} else {
res.status(400).json({
success: false,
message: result.message,
error: result.error,
});
}
} catch (error) {
logger.error("테이블 삭제 컨트롤러 오류:", {
error: (error as Error).message,
stack: (error as Error).stack,
userId: req.user?.userId,
tableName: req.params.tableName,
});
res.status(500).json({
success: false,
error: {
code: "INTERNAL_SERVER_ERROR",
details: "테이블 삭제 중 서버 오류가 발생했습니다.",
},
});
}
}
/**
* DELETE /api/ddl/logs/cleanup - DDL
*/
static async cleanupOldLogs(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const { retentionDays } = req.query;
const days = retentionDays ? parseInt(retentionDays as string) : 90;
const deletedCount = await DDLAuditLogger.cleanupOldLogs(days);
res.json({
success: true,
message: `${deletedCount}개의 오래된 DDL 로그가 삭제되었습니다.`,
data: {
deletedCount,
retentionDays: days,
},
});
} catch (error) {
logger.error("DDL 로그 정리 오류:", error);
res.status(500).json({
success: false,
error: {
code: "LOG_CLEANUP_FAILED",
details: "DDL 로그 정리 중 오류가 발생했습니다.",
},
});
}
}
}