플로우 로그기능 수정

This commit is contained in:
kjs
2025-10-21 14:21:29 +09:00
parent 93675100da
commit 74ebb565e6
10 changed files with 154 additions and 56 deletions

View File

@@ -31,13 +31,16 @@ export class FlowController {
*/
createFlowDefinition = async (req: Request, res: Response): Promise<void> => {
try {
const { name, description, tableName } = req.body;
const { name, description, tableName, dbSourceType, dbConnectionId } =
req.body;
const userId = (req as any).user?.userId || "system";
console.log("🔍 createFlowDefinition called with:", {
name,
description,
tableName,
dbSourceType,
dbConnectionId,
});
if (!name) {
@@ -62,7 +65,7 @@ export class FlowController {
}
const flowDef = await this.flowDefinitionService.create(
{ name, description, tableName },
{ name, description, tableName, dbSourceType, dbConnectionId },
userId
);

View File

@@ -4,6 +4,7 @@
import { Router } from "express";
import { FlowController } from "../controllers/flowController";
import { authenticateToken } from "../middleware/authMiddleware";
const router = Router();
const flowController = new FlowController();
@@ -32,8 +33,8 @@ router.get("/:flowId/step/:stepId/list", flowController.getStepDataList);
router.get("/:flowId/steps/counts", flowController.getAllStepCounts);
// ==================== 데이터 이동 ====================
router.post("/move", flowController.moveData);
router.post("/move-batch", flowController.moveBatchData);
router.post("/move", authenticateToken, flowController.moveData);
router.post("/move-batch", authenticateToken, flowController.moveBatchData);
// ==================== 오딧 로그 ====================
router.get("/audit/:flowId/:recordId", flowController.getAuditLogs);

View File

@@ -7,7 +7,7 @@
import { Pool as PgPool } from "pg";
import * as mysql from "mysql2/promise";
import db from "../database/db";
import { CredentialEncryption } from "../utils/credentialEncryption";
import { PasswordEncryption } from "../utils/passwordEncryption";
import {
getConnectionTestQuery,
getPlaceholder,
@@ -31,24 +31,13 @@ interface ExternalDbConnection {
// 외부 DB 연결 풀 캐시 (타입별로 다른 풀 객체)
const connectionPools = new Map<number, any>();
// 비밀번호 복호화 유틸
const credentialEncryption = new CredentialEncryption(
process.env.ENCRYPTION_SECRET_KEY || "default-secret-key-change-in-production"
);
/**
* 외부 DB 연결 정보 조회
*/
async function getExternalConnection(
connectionId: number
): Promise<ExternalDbConnection | null> {
const query = `
SELECT
id, connection_name, db_type, host, port,
database_name, username, encrypted_password, is_active
FROM external_db_connections
WHERE id = $1 AND is_active = true
`;
const query = `SELECT * FROM external_db_connections WHERE id = $1 AND is_active = 'Y'`;
const result = await db.query(query, [connectionId]);
@@ -58,13 +47,14 @@ async function getExternalConnection(
const row = result[0];
// 비밀번호 복호화
// 비밀번호 복호화 (암호화된 비밀번호는 password 컬럼에 저장됨)
let decryptedPassword = "";
try {
decryptedPassword = credentialEncryption.decrypt(row.encrypted_password);
decryptedPassword = PasswordEncryption.decrypt(row.password);
} catch (error) {
console.error(`비밀번호 복호화 실패 (ID: ${connectionId}):`, error);
throw new Error("외부 DB 비밀번호 복호화에 실패했습니다");
// 복호화 실패 시 원본 비밀번호 사용 (fallback)
decryptedPassword = row.password;
}
return {

View File

@@ -161,6 +161,28 @@ export class FlowDataMoveService {
}
// 5. 감사 로그 기록
let dbConnectionName = null;
if (
flowDefinition.dbSourceType === "external" &&
flowDefinition.dbConnectionId
) {
// 외부 DB인 경우 연결 이름 조회
try {
const connResult = await client.query(
`SELECT connection_name FROM external_db_connections WHERE id = $1`,
[flowDefinition.dbConnectionId]
);
if (connResult.rows && connResult.rows.length > 0) {
dbConnectionName = connResult.rows[0].connection_name;
}
} catch (error) {
console.warn("외부 DB 연결 이름 조회 실패:", error);
}
} else {
// 내부 DB인 경우
dbConnectionName = "내부 데이터베이스";
}
await this.logDataMove(client, {
flowId,
fromStepId,
@@ -173,6 +195,11 @@ export class FlowDataMoveService {
statusFrom: fromStep.statusValue,
statusTo: toStep.statusValue,
userId,
dbConnectionId:
flowDefinition.dbSourceType === "external"
? flowDefinition.dbConnectionId
: null,
dbConnectionName,
});
return {
@@ -361,8 +388,9 @@ export class FlowDataMoveService {
move_type, source_table, target_table,
source_data_id, target_data_id,
status_from, status_to,
changed_by, note
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
changed_by, note,
db_connection_id, db_connection_name
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
`;
await client.query(query, [
@@ -378,6 +406,8 @@ export class FlowDataMoveService {
params.statusTo,
params.userId,
params.note || null,
params.dbConnectionId || null,
params.dbConnectionName || null,
]);
}
@@ -452,6 +482,8 @@ export class FlowDataMoveService {
targetDataId: row.target_data_id,
statusFrom: row.status_from,
statusTo: row.status_to,
dbConnectionId: row.db_connection_id,
dbConnectionName: row.db_connection_name,
}));
}
@@ -496,6 +528,8 @@ export class FlowDataMoveService {
targetDataId: row.target_data_id,
statusFrom: row.status_from,
statusTo: row.status_to,
dbConnectionId: row.db_connection_id,
dbConnectionName: row.db_connection_name,
}));
}
@@ -718,7 +752,21 @@ export class FlowDataMoveService {
// 3. 외부 연동 처리는 생략 (외부 DB 자체가 외부이므로)
// 4. 감사 로그 기록 (내부 DB에)
// 4. 외부 DB 연결 이름 조회
let dbConnectionName = null;
try {
const connResult = await db.query(
`SELECT connection_name FROM external_db_connections WHERE id = $1`,
[dbConnectionId]
);
if (connResult.length > 0) {
dbConnectionName = connResult[0].connection_name;
}
} catch (error) {
console.warn("외부 DB 연결 이름 조회 실패:", error);
}
// 5. 감사 로그 기록 (내부 DB에)
// 외부 DB는 내부 DB 트랜잭션 외부이므로 직접 쿼리 실행
const auditQuery = `
INSERT INTO flow_audit_log (
@@ -726,8 +774,9 @@ export class FlowDataMoveService {
move_type, source_table, target_table,
source_data_id, target_data_id,
status_from, status_to,
changed_by, note
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
changed_by, note,
db_connection_id, db_connection_name
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
`;
await db.query(auditQuery, [
@@ -743,6 +792,8 @@ export class FlowDataMoveService {
toStep.statusValue || null, // statusTo
userId,
`외부 DB (${dbType}) 데이터 이동`,
dbConnectionId,
dbConnectionName,
]);
return {

View File

@@ -182,6 +182,9 @@ export interface FlowAuditLog {
targetDataId?: string;
statusFrom?: string;
statusTo?: string;
// 외부 DB 연결 정보
dbConnectionId?: number;
dbConnectionName?: string;
// 조인 필드
fromStepName?: string;
toStepName?: string;