import { Request, Response } from "express"; import { Client } from "pg"; import { logger } from "../utils/logger"; import { AuthenticatedRequest } from "../types/auth"; import { ApiResponse } from "../types/common"; import { TableManagementService } from "../services/tableManagementService"; import { TableInfo, ColumnTypeInfo, ColumnSettings, TableListResponse, ColumnListResponse, ColumnSettingsResponse, } from "../types/tableManagement"; import { query } from "../database/db"; // πŸ†• query ν•¨μˆ˜ import /** * ν…Œμ΄λΈ” λͺ©λ‘ 쑰회 */ export async function getTableList( req: AuthenticatedRequest, res: Response ): Promise { try { logger.info("=== ν…Œμ΄λΈ” λͺ©λ‘ 쑰회 μ‹œμž‘ ==="); const tableManagementService = new TableManagementService(); const tableList = await tableManagementService.getTableList(); logger.info(`ν…Œμ΄λΈ” λͺ©λ‘ 쑰회 κ²°κ³Ό: ${tableList.length}개`); const response: ApiResponse = { success: true, message: "ν…Œμ΄λΈ” λͺ©λ‘μ„ μ„±κ³΅μ μœΌλ‘œ μ‘°νšŒν–ˆμŠ΅λ‹ˆλ‹€.", data: tableList, }; res.status(200).json(response); } catch (error) { logger.error("ν…Œμ΄λΈ” λͺ©λ‘ 쑰회 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ” λͺ©λ‘ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "TABLE_LIST_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } /** * ν…Œμ΄λΈ” 컬럼 정보 쑰회 */ export async function getColumnList( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName } = req.params; const { page = 1, size = 50 } = req.query; // πŸ”₯ νšŒμ‚¬ μ½”λ“œ μΆ”μΆœ (JWTμ—μ„œ λ˜λŠ” DBμ—μ„œ 쑰회) let companyCode = req.user?.companyCode; if (!companyCode && req.user?.userId) { // JWT에 μ—†μœΌλ©΄ DBμ—μ„œ 쑰회 const { query } = require("../database/db"); const userResult = await query( `SELECT company_code FROM user_info WHERE user_id = $1`, [req.user.userId] ); companyCode = userResult[0]?.company_code; logger.info( `DBμ—μ„œ νšŒμ‚¬ μ½”λ“œ 쑰회 (컬럼 λͺ©λ‘): ${req.user.userId} β†’ ${companyCode}` ); } logger.info( `=== 컬럼 정보 쑰회 μ‹œμž‘: ${tableName} (page: ${page}, size: ${size}), company: ${companyCode} ===` ); if (!tableName) { const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ”λͺ…이 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_TABLE_NAME", details: "ν…Œμ΄λΈ”λͺ… νŒŒλΌλ―Έν„°κ°€ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } const tableManagementService = new TableManagementService(); const result = await tableManagementService.getColumnList( tableName, parseInt(page as string), parseInt(size as string), companyCode // πŸ”₯ νšŒμ‚¬ μ½”λ“œ 전달 ); logger.info( `컬럼 정보 쑰회 κ²°κ³Ό: ${tableName}, ${result.columns.length}/${result.total}개 (${result.page}/${result.totalPages} νŽ˜μ΄μ§€)` ); const response: ApiResponse = { success: true, message: "컬럼 λͺ©λ‘μ„ μ„±κ³΅μ μœΌλ‘œ μ‘°νšŒν–ˆμŠ΅λ‹ˆλ‹€.", data: result, }; res.status(200).json(response); } catch (error) { logger.error("컬럼 정보 쑰회 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "컬럼 λͺ©λ‘ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "COLUMN_LIST_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } /** * κ°œλ³„ 컬럼 μ„€μ • μ—…λ°μ΄νŠΈ */ export async function updateColumnSettings( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName, columnName } = req.params; const settings: ColumnSettings = req.body; // πŸ”₯ νšŒμ‚¬ μ½”λ“œ μΆ”μΆœ (JWTμ—μ„œ λ˜λŠ” DBμ—μ„œ 쑰회) let companyCode = req.user?.companyCode; if (!companyCode && req.user?.userId) { // JWT에 μ—†μœΌλ©΄ DBμ—μ„œ 쑰회 const { query } = require("../database/db"); const userResult = await query( `SELECT company_code FROM user_info WHERE user_id = $1`, [req.user.userId] ); companyCode = userResult[0]?.company_code; logger.info(`DBμ—μ„œ νšŒμ‚¬ μ½”λ“œ 쑰회: ${req.user.userId} β†’ ${companyCode}`); } logger.info( `=== 컬럼 μ„€μ • μ—…λ°μ΄νŠΈ μ‹œμž‘: ${tableName}.${columnName}, company: ${companyCode} ===` ); if (!tableName || !columnName) { const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ”λͺ…κ³Ό 컬럼λͺ…이 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_PARAMETERS", details: "ν…Œμ΄λΈ”λͺ… λ˜λŠ” 컬럼λͺ… νŒŒλΌλ―Έν„°κ°€ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } if (!settings) { const response: ApiResponse = { success: false, message: "컬럼 μ„€μ • 정보가 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_SETTINGS", details: "μš”μ²­ 본문에 컬럼 μ„€μ • 정보가 λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } if (!companyCode) { logger.error(`νšŒμ‚¬ μ½”λ“œ λˆ„λ½: ${tableName}.${columnName}`, { user: req.user, hasUser: !!req.user, userId: req.user?.userId, companyCodeFromJWT: req.user?.companyCode, }); const response: ApiResponse = { success: false, message: "νšŒμ‚¬ μ½”λ“œλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.", error: { code: "MISSING_COMPANY_CODE", details: "μ‚¬μš©μž μ •λ³΄μ—μ„œ νšŒμ‚¬ μ½”λ“œλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€. κ΄€λ¦¬μžμ—κ²Œ λ¬Έμ˜ν•˜μ„Έμš”.", }, }; res.status(400).json(response); return; } const tableManagementService = new TableManagementService(); await tableManagementService.updateColumnSettings( tableName, columnName, settings, companyCode // πŸ”₯ νšŒμ‚¬ μ½”λ“œ 전달 ); logger.info( `컬럼 μ„€μ • μ—…λ°μ΄νŠΈ μ™„λ£Œ: ${tableName}.${columnName}, company: ${companyCode}` ); const response: ApiResponse = { success: true, message: "컬럼 섀정을 μ„±κ³΅μ μœΌλ‘œ μ €μž₯ν–ˆμŠ΅λ‹ˆλ‹€.", }; res.status(200).json(response); } catch (error) { logger.error("컬럼 μ„€μ • μ—…λ°μ΄νŠΈ 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "컬럼 μ„€μ • μ €μž₯ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "COLUMN_SETTINGS_UPDATE_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } /** * 전체 컬럼 μ„€μ • 일괄 μ—…λ°μ΄νŠΈ */ export async function updateAllColumnSettings( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName } = req.params; const columnSettings: ColumnSettings[] = req.body; // πŸ”₯ νšŒμ‚¬ μ½”λ“œ μΆ”μΆœ (JWTμ—μ„œ λ˜λŠ” DBμ—μ„œ 쑰회) let companyCode = req.user?.companyCode; if (!companyCode && req.user?.userId) { // JWT에 μ—†μœΌλ©΄ DBμ—μ„œ 쑰회 const { query } = require("../database/db"); const userResult = await query( `SELECT company_code FROM user_info WHERE user_id = $1`, [req.user.userId] ); companyCode = userResult[0]?.company_code; logger.info(`DBμ—μ„œ νšŒμ‚¬ μ½”λ“œ 쑰회: ${req.user.userId} β†’ ${companyCode}`); } // πŸ” 디버깅: μ‚¬μš©μž 정보 좜λ ₯ logger.info(`[DEBUG] req.user:`, JSON.stringify(req.user, null, 2)); logger.info(`[DEBUG] req.user?.companyCode: ${req.user?.companyCode}`); logger.info(`[DEBUG] req.user?.userId: ${req.user?.userId}`); logger.info(`[DEBUG] companyCode μ΅œμ’…κ°’: ${companyCode}`); logger.info( `=== 전체 컬럼 μ„€μ • 일괄 μ—…λ°μ΄νŠΈ μ‹œμž‘: ${tableName}, company: ${companyCode} ===` ); if (!tableName) { const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ”λͺ…이 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_TABLE_NAME", details: "ν…Œμ΄λΈ”λͺ… νŒŒλΌλ―Έν„°κ°€ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } if (!Array.isArray(columnSettings) || columnSettings.length === 0) { const response: ApiResponse = { success: false, message: "컬럼 μ„€μ • λͺ©λ‘μ΄ ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_COLUMN_SETTINGS", details: "μš”μ²­ 본문에 컬럼 μ„€μ • λͺ©λ‘μ΄ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } if (!companyCode) { logger.error(`νšŒμ‚¬ μ½”λ“œ λˆ„λ½ (일괄 μ—…λ°μ΄νŠΈ): ${tableName}`, { user: req.user, hasUser: !!req.user, userId: req.user?.userId, companyCodeFromJWT: req.user?.companyCode, settingsCount: columnSettings.length, }); const response: ApiResponse = { success: false, message: "νšŒμ‚¬ μ½”λ“œλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.", error: { code: "MISSING_COMPANY_CODE", details: "μ‚¬μš©μž μ •λ³΄μ—μ„œ νšŒμ‚¬ μ½”λ“œλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€. κ΄€λ¦¬μžμ—κ²Œ λ¬Έμ˜ν•˜μ„Έμš”.", }, }; res.status(400).json(response); return; } const tableManagementService = new TableManagementService(); await tableManagementService.updateAllColumnSettings( tableName, columnSettings, companyCode // πŸ”₯ νšŒμ‚¬ μ½”λ“œ 전달 ); logger.info( `전체 컬럼 μ„€μ • 일괄 μ—…λ°μ΄νŠΈ μ™„λ£Œ: ${tableName}, ${columnSettings.length}개, company: ${companyCode}` ); const response: ApiResponse = { success: true, message: "λͺ¨λ“  컬럼 섀정을 μ„±κ³΅μ μœΌλ‘œ μ €μž₯ν–ˆμŠ΅λ‹ˆλ‹€.", }; res.status(200).json(response); } catch (error) { logger.error("전체 컬럼 μ„€μ • 일괄 μ—…λ°μ΄νŠΈ 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "컬럼 μ„€μ • μ €μž₯ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "ALL_COLUMN_SETTINGS_UPDATE_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } /** * ν…Œμ΄λΈ” 라벨 정보 쑰회 */ export async function getTableLabels( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName } = req.params; logger.info(`=== ν…Œμ΄λΈ” 라벨 정보 쑰회 μ‹œμž‘: ${tableName} ===`); if (!tableName) { const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ”λͺ…이 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_TABLE_NAME", details: "ν…Œμ΄λΈ”λͺ… νŒŒλΌλ―Έν„°κ°€ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } const tableManagementService = new TableManagementService(); const tableLabels = await tableManagementService.getTableLabels(tableName); if (!tableLabels) { // 라벨이 μ—†μœΌλ©΄ 빈 객체λ₯Ό μ„±κ³΅μœΌλ‘œ λ°˜ν™˜ (404 μ—λŸ¬ λŒ€μ‹ ) const response: ApiResponse<{}> = { success: true, message: "ν…Œμ΄λΈ” 라벨 정보λ₯Ό μ‘°νšŒν–ˆμŠ΅λ‹ˆλ‹€.", data: {}, }; res.status(200).json(response); return; } logger.info(`ν…Œμ΄λΈ” 라벨 정보 쑰회 μ™„λ£Œ: ${tableName}`); const response: ApiResponse = { success: true, message: "ν…Œμ΄λΈ” 라벨 정보λ₯Ό μ„±κ³΅μ μœΌλ‘œ μ‘°νšŒν–ˆμŠ΅λ‹ˆλ‹€.", data: tableLabels, }; res.status(200).json(response); } catch (error) { logger.error("ν…Œμ΄λΈ” 라벨 정보 쑰회 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ” 라벨 정보 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "TABLE_LABELS_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } /** * 컬럼 라벨 정보 쑰회 */ export async function getColumnLabels( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName, columnName } = req.params; logger.info(`=== 컬럼 라벨 정보 쑰회 μ‹œμž‘: ${tableName}.${columnName} ===`); if (!tableName || !columnName) { const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ”λͺ…κ³Ό 컬럼λͺ…이 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_PARAMETERS", details: "ν…Œμ΄λΈ”λͺ… λ˜λŠ” 컬럼λͺ… νŒŒλΌλ―Έν„°κ°€ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } const tableManagementService = new TableManagementService(); const columnLabels = await tableManagementService.getColumnLabels( tableName, columnName ); if (!columnLabels) { // 라벨이 μ—†μœΌλ©΄ 빈 객체λ₯Ό μ„±κ³΅μœΌλ‘œ λ°˜ν™˜ (404 μ—λŸ¬ λŒ€μ‹ ) const response: ApiResponse<{}> = { success: true, message: "컬럼 라벨 정보λ₯Ό μ‘°νšŒν–ˆμŠ΅λ‹ˆλ‹€.", data: {}, }; res.status(200).json(response); return; } logger.info(`컬럼 라벨 정보 쑰회 μ™„λ£Œ: ${tableName}.${columnName}`); const response: ApiResponse = { success: true, message: "컬럼 라벨 정보λ₯Ό μ„±κ³΅μ μœΌλ‘œ μ‘°νšŒν–ˆμŠ΅λ‹ˆλ‹€.", data: columnLabels, }; res.status(200).json(response); } catch (error) { logger.error("컬럼 라벨 정보 쑰회 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "컬럼 라벨 정보 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "COLUMN_LABELS_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } /** * ν…Œμ΄λΈ” 라벨 μ„€μ • */ export async function updateTableLabel( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName } = req.params; const { displayName, description } = req.body; logger.info(`=== ν…Œμ΄λΈ” 라벨 μ„€μ • μ‹œμž‘: ${tableName} ===`); logger.info(`ν‘œμ‹œλͺ…: ${displayName}, μ„€λͺ…: ${description}`); if (!tableName) { const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ”λͺ…이 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_TABLE_NAME", details: "ν…Œμ΄λΈ”λͺ… νŒŒλΌλ―Έν„°κ°€ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } const tableManagementService = new TableManagementService(); await tableManagementService.updateTableLabel( tableName, displayName, description ); logger.info(`ν…Œμ΄λΈ” 라벨 μ„€μ • μ™„λ£Œ: ${tableName}`); const response: ApiResponse = { success: true, message: "ν…Œμ΄λΈ” 라벨이 μ„±κ³΅μ μœΌλ‘œ μ„€μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", data: null, }; res.status(200).json(response); } catch (error) { logger.error("ν…Œμ΄λΈ” 라벨 μ„€μ • 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ” 라벨 μ„€μ • 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "TABLE_LABEL_UPDATE_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } /** * 컬럼 μž…λ ₯ νƒ€μž… μ„€μ • */ export async function updateColumnInputType( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName, columnName } = req.params; const { inputType, detailSettings } = req.body; // πŸ”₯ νšŒμ‚¬ μ½”λ“œ μΆ”μΆœ (JWTμ—μ„œ λ˜λŠ” DBμ—μ„œ 쑰회) let companyCode = req.user?.companyCode; if (!companyCode && req.user?.userId) { // JWT에 μ—†μœΌλ©΄ DBμ—μ„œ 쑰회 const { query } = require("../database/db"); const userResult = await query( `SELECT company_code FROM user_info WHERE user_id = $1`, [req.user.userId] ); companyCode = userResult[0]?.company_code; logger.info(`DBμ—μ„œ νšŒμ‚¬ μ½”λ“œ 쑰회: ${req.user.userId} β†’ ${companyCode}`); } logger.info( `=== 컬럼 μž…λ ₯ νƒ€μž… μ„€μ • μ‹œμž‘: ${tableName}.${columnName} = ${inputType}, company: ${companyCode} ===` ); if (!tableName || !columnName || !inputType) { const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ”λͺ…, 컬럼λͺ…, μž…λ ₯ νƒ€μž…μ΄ λͺ¨λ‘ ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_PARAMETERS", details: "ν•„μˆ˜ νŒŒλΌλ―Έν„°κ°€ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } if (!companyCode) { logger.error(`νšŒμ‚¬ μ½”λ“œ λˆ„λ½ (μž…λ ₯ νƒ€μž…): ${tableName}.${columnName}`, { user: req.user, hasUser: !!req.user, userId: req.user?.userId, companyCodeFromJWT: req.user?.companyCode, inputType, }); const response: ApiResponse = { success: false, message: "νšŒμ‚¬ μ½”λ“œλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.", error: { code: "MISSING_COMPANY_CODE", details: "μ‚¬μš©μž μ •λ³΄μ—μ„œ νšŒμ‚¬ μ½”λ“œλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€. κ΄€λ¦¬μžμ—κ²Œ λ¬Έμ˜ν•˜μ„Έμš”.", }, }; res.status(400).json(response); return; } const tableManagementService = new TableManagementService(); await tableManagementService.updateColumnInputType( tableName, columnName, inputType, companyCode, detailSettings ); logger.info( `컬럼 μž…λ ₯ νƒ€μž… μ„€μ • μ™„λ£Œ: ${tableName}.${columnName} = ${inputType}, company: ${companyCode}` ); const response: ApiResponse = { success: true, message: "컬럼 μž…λ ₯ νƒ€μž…μ΄ μ„±κ³΅μ μœΌλ‘œ μ„€μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", data: null, }; res.status(200).json(response); } catch (error) { logger.error("컬럼 μž…λ ₯ νƒ€μž… μ„€μ • 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "컬럼 μž…λ ₯ νƒ€μž… μ„€μ • 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "INPUT_TYPE_UPDATE_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } /** * 단일 λ ˆμ½”λ“œ 쑰회 (μžλ™ μž…λ ₯용) */ export async function getTableRecord( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName } = req.params; const { filterColumn, filterValue, displayColumn } = req.body; logger.info(`=== 단일 λ ˆμ½”λ“œ 쑰회 μ‹œμž‘: ${tableName} ===`); logger.info(`ν•„ν„°: ${filterColumn} = ${filterValue}`); logger.info(`ν‘œμ‹œ 컬럼: ${displayColumn}`); if (!tableName || !filterColumn || !filterValue || !displayColumn) { const response: ApiResponse = { success: false, message: "ν•„μˆ˜ νŒŒλΌλ―Έν„°κ°€ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", error: { code: "MISSING_PARAMETERS", details: "tableName, filterColumn, filterValue, displayColumn이 ν•„μš”ν•©λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } const tableManagementService = new TableManagementService(); // 단일 λ ˆμ½”λ“œ 쑰회 (WHERE filterColumn = filterValue) const result = await tableManagementService.getTableData(tableName, { page: 1, size: 1, search: { [filterColumn]: filterValue, }, }); if (!result.data || result.data.length === 0) { const response: ApiResponse = { success: false, message: "데이터λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.", error: { code: "NOT_FOUND", details: `${filterColumn} = ${filterValue}에 ν•΄λ‹Ήν•˜λŠ” 데이터가 μ—†μŠ΅λ‹ˆλ‹€.`, }, }; res.status(404).json(response); return; } const record = result.data[0]; const displayValue = record[displayColumn]; logger.info(`λ ˆμ½”λ“œ 쑰회 μ™„λ£Œ: ${displayColumn} = ${displayValue}`); const response: ApiResponse<{ value: any; record: any }> = { success: true, message: "λ ˆμ½”λ“œλ₯Ό μ„±κ³΅μ μœΌλ‘œ μ‘°νšŒν–ˆμŠ΅λ‹ˆλ‹€.", data: { value: displayValue, record: record, }, }; res.status(200).json(response); } catch (error) { logger.error("λ ˆμ½”λ“œ 쑰회 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "λ ˆμ½”λ“œ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "RECORD_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } /** * ν…Œμ΄λΈ” 데이터 쑰회 (νŽ˜μ΄μ§• + 검색 + 필터링) */ export async function getTableData( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName } = req.params; const { page = 1, size = 10, search = {}, sortBy, sortOrder = "asc", autoFilter, // πŸ†• μžλ™ ν•„ν„° μ„€μ • μΆ”κ°€ (μ»΄ν¬λ„ŒνŠΈμ—μ„œ 직접 전달) } = req.body; logger.info(`=== ν…Œμ΄λΈ” 데이터 쑰회 μ‹œμž‘: ${tableName} ===`); logger.info(`νŽ˜μ΄μ§•: page=${page}, size=${size}`); logger.info(`검색 쑰건:`, search); logger.info(`μ •λ ¬: ${sortBy} ${sortOrder}`); logger.info(`μžλ™ ν•„ν„°:`, autoFilter); // πŸ†• if (!tableName) { const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ”λͺ…이 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_TABLE_NAME", details: "ν…Œμ΄λΈ”λͺ… νŒŒλΌλ―Έν„°κ°€ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } const tableManagementService = new TableManagementService(); // πŸ†• ν˜„μž¬ μ‚¬μš©μž ν•„ν„° 적용 let enhancedSearch = { ...search }; if (autoFilter?.enabled && req.user) { const filterColumn = autoFilter.filterColumn || "company_code"; const userField = autoFilter.userField || "companyCode"; const userValue = (req.user as any)[userField]; if (userValue) { enhancedSearch[filterColumn] = userValue; logger.info("πŸ” ν˜„μž¬ μ‚¬μš©μž ν•„ν„° 적용:", { filterColumn, userField, userValue, tableName, }); } else { logger.warn("⚠️ μ‚¬μš©μž 정보 ν•„λ“œ κ°’ μ—†μŒ:", { userField, user: req.user, }); } } // 데이터 쑰회 const result = await tableManagementService.getTableData(tableName, { page: parseInt(page), size: parseInt(size), search: enhancedSearch, // πŸ†• ν•„ν„°κ°€ 적용된 search μ‚¬μš© sortBy, sortOrder, }); logger.info( `ν…Œμ΄λΈ” 데이터 쑰회 μ™„λ£Œ: ${tableName}, 총 ${result.total}건, νŽ˜μ΄μ§€ ${result.page}/${result.totalPages}` ); const response: ApiResponse = { success: true, message: "ν…Œμ΄λΈ” 데이터λ₯Ό μ„±κ³΅μ μœΌλ‘œ μ‘°νšŒν–ˆμŠ΅λ‹ˆλ‹€.", data: result, }; res.status(200).json(response); } catch (error) { logger.error("ν…Œμ΄λΈ” 데이터 쑰회 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ” 데이터 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "TABLE_DATA_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } /** * ν…Œμ΄λΈ” 데이터 μΆ”κ°€ */ export async function addTableData( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName } = req.params; const data = req.body; logger.info(`=== ν…Œμ΄λΈ” 데이터 μΆ”κ°€ μ‹œμž‘: ${tableName} ===`); logger.info(`μΆ”κ°€ν•  데이터:`, data); if (!tableName) { const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ”λͺ…이 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_TABLE_NAME", details: "ν…Œμ΄λΈ”λͺ… νŒŒλΌλ―Έν„°κ°€ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } if (!data || Object.keys(data).length === 0) { const response: ApiResponse = { success: false, message: "μΆ”κ°€ν•  데이터가 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_DATA", details: "μš”μ²­ 본문에 데이터가 μ—†μŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } const tableManagementService = new TableManagementService(); // 데이터 μΆ”κ°€ await tableManagementService.addTableData(tableName, data); logger.info(`ν…Œμ΄λΈ” 데이터 μΆ”κ°€ μ™„λ£Œ: ${tableName}`); const response: ApiResponse = { success: true, message: "ν…Œμ΄λΈ” 데이터λ₯Ό μ„±κ³΅μ μœΌλ‘œ μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€.", }; res.status(201).json(response); } catch (error) { logger.error("ν…Œμ΄λΈ” 데이터 μΆ”κ°€ 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ” 데이터 μΆ”κ°€ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "TABLE_ADD_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } /** * ν…Œμ΄λΈ” 데이터 μˆ˜μ • */ export async function editTableData( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName } = req.params; const { originalData, updatedData } = req.body; logger.info(`=== ν…Œμ΄λΈ” 데이터 μˆ˜μ • μ‹œμž‘: ${tableName} ===`); logger.info(`원본 데이터:`, originalData); logger.info(`μˆ˜μ •ν•  데이터:`, updatedData); if (!tableName) { const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ”λͺ…이 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "INVALID_TABLE_NAME", details: "ν…Œμ΄λΈ”λͺ…이 λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } if (!originalData || !updatedData) { const response: ApiResponse = { success: false, message: "원본 데이터와 μˆ˜μ •ν•  데이터가 λͺ¨λ‘ ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "INVALID_DATA", details: "originalData와 updatedDataκ°€ λͺ¨λ‘ μ œκ³΅λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } if (Object.keys(updatedData).length === 0) { const response: ApiResponse = { success: false, message: "μˆ˜μ •ν•  데이터가 μ—†μŠ΅λ‹ˆλ‹€.", error: { code: "INVALID_DATA", details: "μˆ˜μ •ν•  데이터가 λΉ„μ–΄μžˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } const tableManagementService = new TableManagementService(); // 데이터 μˆ˜μ • await tableManagementService.editTableData( tableName, originalData, updatedData ); logger.info(`ν…Œμ΄λΈ” 데이터 μˆ˜μ • μ™„λ£Œ: ${tableName}`); const response: ApiResponse = { success: true, message: "ν…Œμ΄λΈ” 데이터λ₯Ό μ„±κ³΅μ μœΌλ‘œ μˆ˜μ •ν–ˆμŠ΅λ‹ˆλ‹€.", }; res.status(200).json(response); } catch (error) { logger.error("ν…Œμ΄λΈ” 데이터 μˆ˜μ • 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ” 데이터 μˆ˜μ • 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "TABLE_EDIT_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } /** * ν…Œμ΄λΈ” μŠ€ν‚€λ§ˆ 정보 쑰회 (컬럼 쑴재 μ—¬λΆ€ κ²€μ¦μš©) */ export async function getTableSchema( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName } = req.params; logger.info(`=== ν…Œμ΄λΈ” μŠ€ν‚€λ§ˆ 정보 쑰회 μ‹œμž‘: ${tableName} ===`); if (!tableName) { const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ”λͺ…이 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_TABLE_NAME", details: "ν…Œμ΄λΈ”λͺ… νŒŒλΌλ―Έν„°κ°€ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } const tableManagementService = new TableManagementService(); const schema = await tableManagementService.getTableSchema(tableName); logger.info( `ν…Œμ΄λΈ” μŠ€ν‚€λ§ˆ 정보 쑰회 μ™„λ£Œ: ${tableName}, ${schema.length}개 컬럼` ); const response: ApiResponse = { success: true, message: "ν…Œμ΄λΈ” μŠ€ν‚€λ§ˆ 정보λ₯Ό μ„±κ³΅μ μœΌλ‘œ μ‘°νšŒν–ˆμŠ΅λ‹ˆλ‹€.", data: schema, }; res.status(200).json(response); } catch (error) { logger.error("ν…Œμ΄λΈ” μŠ€ν‚€λ§ˆ 정보 쑰회 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ” μŠ€ν‚€λ§ˆ 정보 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "TABLE_SCHEMA_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } /** * ν…Œμ΄λΈ” 쑴재 μ—¬λΆ€ 확인 */ export async function checkTableExists( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName } = req.params; logger.info(`=== ν…Œμ΄λΈ” 쑴재 μ—¬λΆ€ 확인 μ‹œμž‘: ${tableName} ===`); if (!tableName) { const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ”λͺ…이 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_TABLE_NAME", details: "ν…Œμ΄λΈ”λͺ… νŒŒλΌλ―Έν„°κ°€ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } const tableManagementService = new TableManagementService(); const exists = await tableManagementService.checkTableExists(tableName); logger.info(`ν…Œμ΄λΈ” 쑴재 μ—¬λΆ€ 확인 μ™„λ£Œ: ${tableName} = ${exists}`); const response: ApiResponse<{ exists: boolean }> = { success: true, message: "ν…Œμ΄λΈ” 쑴재 μ—¬λΆ€λ₯Ό ν™•μΈν–ˆμŠ΅λ‹ˆλ‹€.", data: { exists }, }; res.status(200).json(response); } catch (error) { logger.error("ν…Œμ΄λΈ” 쑴재 μ—¬λΆ€ 확인 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ” 쑴재 μ—¬λΆ€ 확인 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "TABLE_EXISTS_CHECK_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } /** * 컬럼 μ›Ήνƒ€μž… 정보 쑰회 (화면관리 μ—°λ™μš©) */ export async function getColumnWebTypes( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName } = req.params; // πŸ”₯ νšŒμ‚¬ μ½”λ“œ μΆ”μΆœ (JWTμ—μ„œ λ˜λŠ” DBμ—μ„œ 쑰회) let companyCode = req.user?.companyCode; if (!companyCode && req.user?.userId) { // JWT에 μ—†μœΌλ©΄ DBμ—μ„œ 쑰회 const { query } = require("../database/db"); const userResult = await query( `SELECT company_code FROM user_info WHERE user_id = $1`, [req.user.userId] ); companyCode = userResult[0]?.company_code; logger.info( `DBμ—μ„œ νšŒμ‚¬ μ½”λ“œ 쑰회 (쑰회): ${req.user.userId} β†’ ${companyCode}` ); } logger.info( `=== 컬럼 μ›Ήνƒ€μž… 정보 쑰회 μ‹œμž‘: ${tableName}, company: ${companyCode} ===` ); if (!tableName) { const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ”λͺ…이 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_TABLE_NAME", details: "ν…Œμ΄λΈ”λͺ… νŒŒλΌλ―Έν„°κ°€ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } if (!companyCode) { logger.error(`νšŒμ‚¬ μ½”λ“œ λˆ„λ½ (쑰회): ${tableName}`, { user: req.user, hasUser: !!req.user, userId: req.user?.userId, companyCodeFromJWT: req.user?.companyCode, }); const response: ApiResponse = { success: false, message: "νšŒμ‚¬ μ½”λ“œλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.", error: { code: "MISSING_COMPANY_CODE", details: "μ‚¬μš©μž μ •λ³΄μ—μ„œ νšŒμ‚¬ μ½”λ“œλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€. κ΄€λ¦¬μžμ—κ²Œ λ¬Έμ˜ν•˜μ„Έμš”.", }, }; res.status(400).json(response); return; } const tableManagementService = new TableManagementService(); const inputTypes = await tableManagementService.getColumnInputTypes( tableName, companyCode ); logger.info( `컬럼 μž…λ ₯νƒ€μž… 정보 쑰회 μ™„λ£Œ: ${tableName}, company: ${companyCode}, ${inputTypes.length}개 컬럼` ); const response: ApiResponse = { success: true, message: "컬럼 μž…λ ₯νƒ€μž… 정보λ₯Ό μ„±κ³΅μ μœΌλ‘œ μ‘°νšŒν–ˆμŠ΅λ‹ˆλ‹€.", data: inputTypes, }; res.status(200).json(response); } catch (error) { logger.error("컬럼 μ›Ήνƒ€μž… 정보 쑰회 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "컬럼 μ›Ήνƒ€μž… 정보 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "COLUMN_WEB_TYPES_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } /** * λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° μƒνƒœ 확인 */ export async function checkDatabaseConnection( req: AuthenticatedRequest, res: Response ): Promise { try { logger.info("=== λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° μƒνƒœ 확인 μ‹œμž‘ ==="); const tableManagementService = new TableManagementService(); const connectionStatus = await tableManagementService.checkDatabaseConnection(); logger.info( `λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° μƒνƒœ: ${connectionStatus.connected ? "연결됨" : "μ—°κ²° μ•ˆλ¨"}` ); const response: ApiResponse<{ connected: boolean; message: string }> = { success: true, message: "λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° μƒνƒœλ₯Ό ν™•μΈν–ˆμŠ΅λ‹ˆλ‹€.", data: connectionStatus, }; res.status(200).json(response); } catch (error) { logger.error("λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° μƒνƒœ 확인 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° μƒνƒœ 확인 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "DATABASE_CONNECTION_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } /** * ν…Œμ΄λΈ” 데이터 μ‚­μ œ */ export async function deleteTableData( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName } = req.params; const data = req.body; logger.info(`=== ν…Œμ΄λΈ” 데이터 μ‚­μ œ μ‹œμž‘: ${tableName} ===`); logger.info(`μ‚­μ œν•  데이터:`, data); if (!tableName) { const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ”λͺ…이 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_TABLE_NAME", details: "ν…Œμ΄λΈ”λͺ… νŒŒλΌλ―Έν„°κ°€ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } if (!data || (Array.isArray(data) && data.length === 0)) { const response: ApiResponse = { success: false, message: "μ‚­μ œν•  데이터가 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_DATA", details: "μš”μ²­ 본문에 μ‚­μ œν•  데이터가 μ—†μŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } const tableManagementService = new TableManagementService(); // 데이터 μ‚­μ œ const deletedCount = await tableManagementService.deleteTableData( tableName, data ); logger.info( `ν…Œμ΄λΈ” 데이터 μ‚­μ œ μ™„λ£Œ: ${tableName}, ${deletedCount}건 μ‚­μ œ` ); const response: ApiResponse<{ deletedCount: number }> = { success: true, message: `ν…Œμ΄λΈ” 데이터λ₯Ό μ„±κ³΅μ μœΌλ‘œ μ‚­μ œν–ˆμŠ΅λ‹ˆλ‹€. (${deletedCount}건)`, data: { deletedCount }, }; res.status(200).json(response); } catch (error) { logger.error("ν…Œμ΄λΈ” 데이터 μ‚­μ œ 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ” 데이터 μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "TABLE_DELETE_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } /** * 컬럼 μ›Ή νƒ€μž… μ„€μ • (λ ˆκ±°μ‹œ 지원) * @deprecated updateColumnInputType μ‚¬μš© ꢌμž₯ */ export async function updateColumnWebType( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName, columnName } = req.params; const { webType, detailSettings, inputType } = req.body; logger.warn( `λ ˆκ±°μ‹œ API μ‚¬μš©: updateColumnWebType β†’ updateColumnInputType μ‚¬μš© ꢌμž₯` ); // webType을 inputType으둜 λ³€ν™˜ const convertedInputType = inputType || webType || "text"; // μƒˆλ‘œμš΄ λ©”μ„œλ“œ 호좜 req.body = { inputType: convertedInputType, detailSettings }; await updateColumnInputType(req, res); } catch (error) { logger.error("λ ˆκ±°μ‹œ 컬럼 μ›Ή νƒ€μž… μ„€μ • 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "컬럼 μ›Ή νƒ€μž… μ„€μ • 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "WEB_TYPE_UPDATE_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } // ======================================== // 🎯 ν…Œμ΄λΈ” 둜그 μ‹œμŠ€ν…œ API // ======================================== /** * 둜그 ν…Œμ΄λΈ” 생성 */ export async function createLogTable( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName } = req.params; const { pkColumn } = req.body; const userId = req.user?.userId; logger.info(`=== 둜그 ν…Œμ΄λΈ” 생성 μ‹œμž‘: ${tableName} ===`); if (!tableName) { const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ”λͺ…이 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_TABLE_NAME", details: "ν…Œμ΄λΈ”λͺ… νŒŒλΌλ―Έν„°κ°€ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } if (!pkColumn || !pkColumn.columnName || !pkColumn.dataType) { const response: ApiResponse = { success: false, message: "PK 컬럼 정보가 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_PK_COLUMN", details: "PK 컬럼λͺ…κ³Ό 데이터 νƒ€μž…μ΄ ν•„μš”ν•©λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } const tableManagementService = new TableManagementService(); await tableManagementService.createLogTable(tableName, pkColumn, userId); logger.info(`둜그 ν…Œμ΄λΈ” 생성 μ™„λ£Œ: ${tableName}_log`); const response: ApiResponse = { success: true, message: "둜그 ν…Œμ΄λΈ”μ΄ μ„±κ³΅μ μœΌλ‘œ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }; res.status(200).json(response); } catch (error) { logger.error("둜그 ν…Œμ΄λΈ” 생성 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "둜그 ν…Œμ΄λΈ” 생성 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "LOG_TABLE_CREATE_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } /** * 둜그 μ„€μ • 쑰회 */ export async function getLogConfig( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName } = req.params; logger.info(`=== 둜그 μ„€μ • 쑰회: ${tableName} ===`); if (!tableName) { const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ”λͺ…이 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_TABLE_NAME", details: "ν…Œμ΄λΈ”λͺ… νŒŒλΌλ―Έν„°κ°€ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } const tableManagementService = new TableManagementService(); const logConfig = await tableManagementService.getLogConfig(tableName); const response: ApiResponse = { success: true, message: "둜그 섀정을 μ‘°νšŒν–ˆμŠ΅λ‹ˆλ‹€.", data: logConfig, }; res.status(200).json(response); } catch (error) { logger.error("둜그 μ„€μ • 쑰회 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "둜그 μ„€μ • 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "LOG_CONFIG_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } /** * 둜그 데이터 쑰회 */ export async function getLogData( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName } = req.params; const { page = 1, size = 20, operationType, startDate, endDate, changedBy, originalId, } = req.query; logger.info(`=== 둜그 데이터 쑰회: ${tableName} ===`); if (!tableName) { const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ”λͺ…이 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_TABLE_NAME", details: "ν…Œμ΄λΈ”λͺ… νŒŒλΌλ―Έν„°κ°€ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } const tableManagementService = new TableManagementService(); const result = await tableManagementService.getLogData(tableName, { page: parseInt(page as string), size: parseInt(size as string), operationType: operationType as string, startDate: startDate as string, endDate: endDate as string, changedBy: changedBy as string, originalId: originalId as string, }); logger.info(`둜그 데이터 쑰회 μ™„λ£Œ: ${tableName}_log, ${result.total}건`); const response: ApiResponse = { success: true, message: "둜그 데이터λ₯Ό μ‘°νšŒν–ˆμŠ΅λ‹ˆλ‹€.", data: result, }; res.status(200).json(response); } catch (error) { logger.error("둜그 데이터 쑰회 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "둜그 데이터 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "LOG_DATA_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } } /** * 둜그 ν…Œμ΄λΈ” ν™œμ„±ν™”/λΉ„ν™œμ„±ν™” */ export async function toggleLogTable( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName } = req.params; const { isActive } = req.body; logger.info( `=== 둜그 ν…Œμ΄λΈ” ν† κΈ€: ${tableName}, isActive: ${isActive} ===` ); if (!tableName) { const response: ApiResponse = { success: false, message: "ν…Œμ΄λΈ”λͺ…이 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_TABLE_NAME", details: "ν…Œμ΄λΈ”λͺ… νŒŒλΌλ―Έν„°κ°€ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } if (isActive === undefined || isActive === null) { const response: ApiResponse = { success: false, message: "isActive 값이 ν•„μš”ν•©λ‹ˆλ‹€.", error: { code: "MISSING_IS_ACTIVE", details: "isActive νŒŒλΌλ―Έν„°κ°€ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", }, }; res.status(400).json(response); return; } const tableManagementService = new TableManagementService(); await tableManagementService.toggleLogTable( tableName, isActive === "Y" || isActive === true ); logger.info(`둜그 ν…Œμ΄λΈ” ν† κΈ€ μ™„λ£Œ: ${tableName}, isActive: ${isActive}`); const response: ApiResponse = { success: true, message: `둜그 κΈ°λŠ₯이 ${isActive ? "ν™œμ„±ν™”" : "λΉ„ν™œμ„±ν™”"}λ˜μ—ˆμŠ΅λ‹ˆλ‹€.`, }; res.status(200).json(response); } catch (error) { logger.error("둜그 ν…Œμ΄λΈ” ν† κΈ€ 쀑 였λ₯˜ λ°œμƒ:", error); const response: ApiResponse = { success: false, message: "둜그 ν…Œμ΄λΈ” ν† κΈ€ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: { code: "LOG_TOGGLE_ERROR", details: error instanceof Error ? error.message : "Unknown error", }, }; res.status(500).json(response); } }