/** * ๐Ÿ”ฅ ๋ฐ์ดํ„ฐํ”Œ๋กœ์šฐ ์‹คํ–‰ ์ปจํŠธ๋กค๋Ÿฌ * * ๋ฒ„ํŠผ ์ œ์–ด์—์„œ ๊ด€๊ณ„ ์‹คํ–‰ ์‹œ ์‚ฌ์šฉ๋˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ */ import { Request, Response } from "express"; import { AuthenticatedRequest } from "../types/auth"; import { PrismaClient } from "@prisma/client"; import logger from "../utils/logger"; const prisma = new PrismaClient(); /** * ๋ฐ์ดํ„ฐ ์•ก์…˜ ์‹คํ–‰ */ 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 { // TODO: ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ๋ฐ ์‹คํ–‰ ๋กœ์ง ๊ตฌํ˜„ // ํ˜„์žฌ๋Š” ๋กœ๊ทธ๋งŒ ์ถœ๋ ฅํ•˜๊ณ  ์„ฑ๊ณต์œผ๋กœ ์ฒ˜๋ฆฌ logger.info(`์™ธ๋ถ€ DB ์•ก์…˜ ์‹คํ–‰: ${connection.name} (${connection.host}:${connection.port})`); logger.info(`ํ…Œ์ด๋ธ”: ${tableName}, ์•ก์…˜: ${actionType}`, data); // ์ž„์‹œ ์„ฑ๊ณต ์‘๋‹ต return { success: true, message: `์™ธ๋ถ€ DB ์•ก์…˜ ์‹คํ–‰ ์™„๋ฃŒ: ${actionType} on ${tableName}`, connection: connection.name, 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 query = `INSERT INTO ${tableName} (${columns}) VALUES (${placeholders}) RETURNING *`; logger.info(`INSERT ์ฟผ๋ฆฌ ์‹คํ–‰:`, { query, values }); const result = await prisma.$queryRawUnsafe(query, ...values); return { success: true, action: 'insert', tableName, data: result, affectedRows: Array.isArray(result) ? result.length : 1, }; } 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 query = `UPDATE ${tableName} SET ${setClause} WHERE id = $${values.length + 1} RETURNING *`; logger.info(`UPDATE ์ฟผ๋ฆฌ ์‹คํ–‰:`, { query, values: [...values, id] }); const result = await prisma.$queryRawUnsafe(query, ...values, id); return { success: true, action: 'update', tableName, data: result, affectedRows: Array.isArray(result) ? result.length : 1, }; } 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 query = `DELETE FROM ${tableName} WHERE id = $1 RETURNING *`; logger.info(`DELETE ์ฟผ๋ฆฌ ์‹คํ–‰:`, { query, values: [id] }); const result = await prisma.$queryRawUnsafe(query, id); return { success: true, action: 'delete', tableName, data: result, affectedRows: Array.isArray(result) ? result.length : 1, }; } catch (error) { logger.error(`DELETE ์‹คํ–‰ ์˜ค๋ฅ˜:`, error); throw error; } }