테이블 및 컬럼 생성기능 추가
This commit is contained in:
407
backend-node/src/controllers/ddlController.ts
Normal file
407
backend-node/src/controllers/ddlController.ts
Normal file
@@ -0,0 +1,407 @@
|
||||
/**
|
||||
* 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,
|
||||
});
|
||||
|
||||
// DDL 실행 서비스 호출
|
||||
const ddlService = new DDLExecutionService();
|
||||
const result = await ddlService.createTable(
|
||||
tableName,
|
||||
columns,
|
||||
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;
|
||||
}
|
||||
|
||||
if (!column || !column.name || !column.webType) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: {
|
||||
code: "INVALID_INPUT",
|
||||
details: "컬럼명과 웹타입이 필요합니다.",
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("컬럼 추가 요청", {
|
||||
tableName,
|
||||
columnName: column.name,
|
||||
webType: column.webType,
|
||||
userId,
|
||||
ip: req.ip,
|
||||
});
|
||||
|
||||
// DDL 실행 서비스 호출
|
||||
const ddlService = new DDLExecutionService();
|
||||
const result = await ddlService.addColumn(
|
||||
tableName,
|
||||
column,
|
||||
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/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 로그 정리 중 오류가 발생했습니다.",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user