/** * πŸ”₯ λ°μ΄ν„°ν”Œλ‘œμš° μ‹€ν–‰ 컨트둀러 * * λ²„νŠΌ μ œμ–΄μ—μ„œ 관계 μ‹€ν–‰ μ‹œ μ‚¬μš©λ˜λŠ” 컨트둀러 */ import { Request, Response } from "express"; import { AuthenticatedRequest } from "../types/auth"; import { query } from "../database/db"; import logger from "../utils/logger"; /** * 데이터 μ•‘μ…˜ μ‹€ν–‰ */ export async function executeDataAction( req: AuthenticatedRequest, res: Response ): Promise { try { const { tableName, data, actionType, connection } = req.body; const companyCode = req.user?.companyCode || "*"; logger.info(`데이터 μ•‘μ…˜ μ‹€ν–‰ μ‹œμž‘: ${actionType} on ${tableName}`, { tableName, actionType, dataKeys: Object.keys(data), connection: connection?.name, }); // μ—°κ²° 정보에 따라 λ‹€λ₯Έ λ°μ΄ν„°λ² μ΄μŠ€μ— μ €μž₯ let result; if (connection && connection.id !== 0) { // μ™ΈλΆ€ λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° result = await executeExternalDatabaseAction(tableName, data, actionType, connection); } else { // 메인 λ°μ΄ν„°λ² μ΄μŠ€ (ν˜„μž¬ μ‹œμŠ€ν…œ) result = await executeMainDatabaseAction(tableName, data, actionType, companyCode); } logger.info(`데이터 μ•‘μ…˜ μ‹€ν–‰ μ™„λ£Œ: ${actionType} on ${tableName}`, result); res.json({ success: true, message: `데이터 μ•‘μ…˜ μ‹€ν–‰ μ™„λ£Œ: ${actionType}`, data: result, }); } catch (error: any) { logger.error("데이터 μ•‘μ…˜ μ‹€ν–‰ μ‹€νŒ¨:", error); res.status(500).json({ success: false, message: `데이터 μ•‘μ…˜ μ‹€ν–‰ μ‹€νŒ¨: ${error.message}`, errorCode: "DATA_ACTION_EXECUTION_ERROR", }); } } /** * 메인 λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ 데이터 μ•‘μ…˜ μ‹€ν–‰ */ async function executeMainDatabaseAction( tableName: string, data: Record, actionType: string, companyCode: string ): Promise { try { // νšŒμ‚¬ μ½”λ“œ μΆ”κ°€ const dataWithCompany = { ...data, company_code: companyCode, }; switch (actionType.toLowerCase()) { case 'insert': return await executeInsert(tableName, dataWithCompany); case 'update': return await executeUpdate(tableName, dataWithCompany); case 'upsert': return await executeUpsert(tableName, dataWithCompany); case 'delete': return await executeDelete(tableName, dataWithCompany); default: throw new Error(`μ§€μ›ν•˜μ§€ μ•ŠλŠ” μ•‘μ…˜ νƒ€μž…: ${actionType}`); } } catch (error) { logger.error(`메인 DB μ•‘μ…˜ μ‹€ν–‰ 였λ₯˜ (${actionType}):`, error); throw error; } } /** * μ™ΈλΆ€ λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ 데이터 μ•‘μ…˜ μ‹€ν–‰ */ async function executeExternalDatabaseAction( tableName: string, data: Record, actionType: string, connection: any ): Promise { try { logger.info(`μ™ΈλΆ€ DB μ•‘μ…˜ μ‹€ν–‰: ${connection.name} (${connection.host}:${connection.port})`); logger.info(`ν…Œμ΄λΈ”: ${tableName}, μ•‘μ…˜: ${actionType}`, data); // πŸ”₯ μ‹€μ œ μ™ΈλΆ€ DB μ—°κ²° 및 μ‹€ν–‰ 둜직 κ΅¬ν˜„ const { MultiConnectionQueryService } = await import('../services/multiConnectionQueryService'); const queryService = new MultiConnectionQueryService(); let result; switch (actionType.toLowerCase()) { case 'insert': result = await queryService.insertDataToConnection(connection.id, tableName, data); logger.info(`μ™ΈλΆ€ DB INSERT 성곡:`, result); break; case 'update': // TODO: UPDATE 둜직 κ΅¬ν˜„ (쑰건 ν•„μš”) throw new Error('UPDATE μ•‘μ…˜μ€ 아직 μ§€μ›λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 쑰건 섀정이 ν•„μš”ν•©λ‹ˆλ‹€.'); case 'delete': // TODO: DELETE 둜직 κ΅¬ν˜„ (쑰건 ν•„μš”) throw new Error('DELETE μ•‘μ…˜μ€ 아직 μ§€μ›λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 쑰건 섀정이 ν•„μš”ν•©λ‹ˆλ‹€.'); default: throw new Error(`μ§€μ›ν•˜μ§€ μ•ŠλŠ” μ•‘μ…˜ νƒ€μž…: ${actionType}`); } return { success: true, message: `μ™ΈλΆ€ DB μ•‘μ…˜ μ‹€ν–‰ μ™„λ£Œ: ${actionType} on ${tableName}`, connection: connection.name, data: result, affectedRows: 1, }; } catch (error) { logger.error(`μ™ΈλΆ€ DB μ•‘μ…˜ μ‹€ν–‰ 였λ₯˜ (${actionType}):`, error); throw error; } } /** * INSERT μ‹€ν–‰ */ async function executeInsert(tableName: string, data: Record): Promise { try { // 동적 ν…Œμ΄λΈ” 접근을 μœ„ν•œ raw query μ‚¬μš© const columns = Object.keys(data).join(', '); const values = Object.values(data); const placeholders = values.map((_, index) => `$${index + 1}`).join(', '); const insertQuery = `INSERT INTO ${tableName} (${columns}) VALUES (${placeholders}) RETURNING *`; logger.info(`INSERT 쿼리 μ‹€ν–‰:`, { query: insertQuery, values }); const result = await query(insertQuery, values); return { success: true, action: 'insert', tableName, data: result, affectedRows: result.length, }; } catch (error) { logger.error(`INSERT μ‹€ν–‰ 였λ₯˜:`, error); throw error; } } /** * UPDATE μ‹€ν–‰ */ async function executeUpdate(tableName: string, data: Record): Promise { try { // ID λ˜λŠ” κΈ°λ³Έν‚€λ₯Ό κΈ°μ€€μœΌλ‘œ μ—…λ°μ΄νŠΈ const { id, ...updateData } = data; if (!id) { throw new Error('UPDATEλ₯Ό μœ„ν•œ IDκ°€ ν•„μš”ν•©λ‹ˆλ‹€'); } const setClause = Object.keys(updateData) .map((key, index) => `${key} = $${index + 1}`) .join(', '); const values = Object.values(updateData); const updateQuery = `UPDATE ${tableName} SET ${setClause} WHERE id = $${values.length + 1} RETURNING *`; logger.info(`UPDATE 쿼리 μ‹€ν–‰:`, { query: updateQuery, values: [...values, id] }); const result = await query(updateQuery, [...values, id]); return { success: true, action: 'update', tableName, data: result, affectedRows: result.length, }; } catch (error) { logger.error(`UPDATE μ‹€ν–‰ 였λ₯˜:`, error); throw error; } } /** * UPSERT μ‹€ν–‰ */ async function executeUpsert(tableName: string, data: Record): Promise { try { // λ¨Όμ € INSERTλ₯Ό μ‹œλ„ν•˜κ³ , μ‹€νŒ¨ν•˜λ©΄ UPDATE try { return await executeInsert(tableName, data); } catch (insertError) { // INSERT μ‹€νŒ¨ μ‹œ UPDATE μ‹œλ„ logger.info(`INSERT μ‹€νŒ¨, UPDATE μ‹œλ„:`, insertError); return await executeUpdate(tableName, data); } } catch (error) { logger.error(`UPSERT μ‹€ν–‰ 였λ₯˜:`, error); throw error; } } /** * DELETE μ‹€ν–‰ */ async function executeDelete(tableName: string, data: Record): Promise { try { const { id } = data; if (!id) { throw new Error('DELETEλ₯Ό μœ„ν•œ IDκ°€ ν•„μš”ν•©λ‹ˆλ‹€'); } const deleteQuery = `DELETE FROM ${tableName} WHERE id = $1 RETURNING *`; logger.info(`DELETE 쿼리 μ‹€ν–‰:`, { query: deleteQuery, values: [id] }); const result = await query(deleteQuery, [id]); return { success: true, action: 'delete', tableName, data: result, affectedRows: result.length, }; } catch (error) { logger.error(`DELETE μ‹€ν–‰ 였λ₯˜:`, error); throw error; } }