외부 호출 중간 저장
This commit is contained in:
@@ -29,6 +29,7 @@ import componentStandardRoutes from "./routes/componentStandardRoutes";
|
||||
import layoutRoutes from "./routes/layoutRoutes";
|
||||
import dataRoutes from "./routes/dataRoutes";
|
||||
import externalCallRoutes from "./routes/externalCallRoutes";
|
||||
import externalCallConfigRoutes from "./routes/externalCallConfigRoutes";
|
||||
// import userRoutes from './routes/userRoutes';
|
||||
// import menuRoutes from './routes/menuRoutes';
|
||||
|
||||
@@ -121,6 +122,7 @@ app.use("/api/layouts", layoutRoutes);
|
||||
app.use("/api/screen", screenStandardRoutes);
|
||||
app.use("/api/data", dataRoutes);
|
||||
app.use("/api/external-calls", externalCallRoutes);
|
||||
app.use("/api/external-call-configs", externalCallConfigRoutes);
|
||||
// app.use('/api/users', userRoutes);
|
||||
// app.use('/api/menus', menuRoutes);
|
||||
|
||||
|
||||
252
backend-node/src/routes/externalCallConfigRoutes.ts
Normal file
252
backend-node/src/routes/externalCallConfigRoutes.ts
Normal file
@@ -0,0 +1,252 @@
|
||||
import express, { Request, Response } from "express";
|
||||
import externalCallConfigService, {
|
||||
ExternalCallConfigFilter,
|
||||
} from "../services/externalCallConfigService";
|
||||
import { logger } from "../utils/logger";
|
||||
import { authenticateToken } from "../middleware/authMiddleware";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// 모든 라우트에 인증 미들웨어 적용
|
||||
router.use(authenticateToken);
|
||||
|
||||
/**
|
||||
* 외부 호출 설정 목록 조회
|
||||
* GET /api/external-call-configs
|
||||
*/
|
||||
router.get("/", async (req: Request, res: Response) => {
|
||||
try {
|
||||
const filter: ExternalCallConfigFilter = {
|
||||
company_code: req.query.company_code as string,
|
||||
call_type: req.query.call_type as string,
|
||||
api_type: req.query.api_type as string,
|
||||
is_active: (req.query.is_active as string) || "Y",
|
||||
search: req.query.search as string,
|
||||
};
|
||||
|
||||
const configs = await externalCallConfigService.getConfigs(filter);
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
data: configs,
|
||||
message: `외부 호출 설정 ${configs.length}개 조회 완료`,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("외부 호출 설정 목록 조회 API 오류:", error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message:
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "외부 호출 설정 목록 조회 실패",
|
||||
errorCode: "EXTERNAL_CALL_CONFIG_LIST_ERROR",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 외부 호출 설정 단일 조회
|
||||
* GET /api/external-call-configs/:id
|
||||
*/
|
||||
router.get("/:id", async (req: Request, res: Response) => {
|
||||
try {
|
||||
const id = parseInt(req.params.id);
|
||||
if (isNaN(id)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "유효하지 않은 설정 ID입니다.",
|
||||
errorCode: "INVALID_CONFIG_ID",
|
||||
});
|
||||
}
|
||||
|
||||
const config = await externalCallConfigService.getConfigById(id);
|
||||
if (!config) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "외부 호출 설정을 찾을 수 없습니다.",
|
||||
errorCode: "CONFIG_NOT_FOUND",
|
||||
});
|
||||
}
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
data: config,
|
||||
message: "외부 호출 설정 조회 완료",
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("외부 호출 설정 조회 API 오류:", error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message:
|
||||
error instanceof Error ? error.message : "외부 호출 설정 조회 실패",
|
||||
errorCode: "EXTERNAL_CALL_CONFIG_GET_ERROR",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 외부 호출 설정 생성
|
||||
* POST /api/external-call-configs
|
||||
*/
|
||||
router.post("/", async (req: Request, res: Response) => {
|
||||
try {
|
||||
const {
|
||||
config_name,
|
||||
call_type,
|
||||
api_type,
|
||||
config_data,
|
||||
description,
|
||||
company_code,
|
||||
} = req.body;
|
||||
|
||||
// 필수 필드 검증
|
||||
if (!config_name || !call_type || !config_data) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message:
|
||||
"필수 필드가 누락되었습니다. (config_name, call_type, config_data)",
|
||||
errorCode: "MISSING_REQUIRED_FIELDS",
|
||||
});
|
||||
}
|
||||
|
||||
// 사용자 정보 가져오기
|
||||
const userInfo = (req as any).user;
|
||||
const userId = userInfo?.userId || "SYSTEM";
|
||||
|
||||
const newConfig = await externalCallConfigService.createConfig({
|
||||
config_name,
|
||||
call_type,
|
||||
api_type,
|
||||
config_data,
|
||||
description,
|
||||
company_code: company_code || "*",
|
||||
created_by: userId,
|
||||
updated_by: userId,
|
||||
});
|
||||
|
||||
return res.status(201).json({
|
||||
success: true,
|
||||
data: newConfig,
|
||||
message: "외부 호출 설정이 성공적으로 생성되었습니다.",
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("외부 호출 설정 생성 API 오류:", error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message:
|
||||
error instanceof Error ? error.message : "외부 호출 설정 생성 실패",
|
||||
errorCode: "EXTERNAL_CALL_CONFIG_CREATE_ERROR",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 외부 호출 설정 수정
|
||||
* PUT /api/external-call-configs/:id
|
||||
*/
|
||||
router.put("/:id", async (req: Request, res: Response) => {
|
||||
try {
|
||||
const id = parseInt(req.params.id);
|
||||
if (isNaN(id)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "유효하지 않은 설정 ID입니다.",
|
||||
errorCode: "INVALID_CONFIG_ID",
|
||||
});
|
||||
}
|
||||
|
||||
// 사용자 정보 가져오기
|
||||
const userInfo = (req as any).user;
|
||||
const userId = userInfo?.userId || "SYSTEM";
|
||||
|
||||
const updatedConfig = await externalCallConfigService.updateConfig(id, {
|
||||
...req.body,
|
||||
updated_by: userId,
|
||||
});
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
data: updatedConfig,
|
||||
message: "외부 호출 설정이 성공적으로 수정되었습니다.",
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("외부 호출 설정 수정 API 오류:", error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message:
|
||||
error instanceof Error ? error.message : "외부 호출 설정 수정 실패",
|
||||
errorCode: "EXTERNAL_CALL_CONFIG_UPDATE_ERROR",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 외부 호출 설정 삭제 (논리 삭제)
|
||||
* DELETE /api/external-call-configs/:id
|
||||
*/
|
||||
router.delete("/:id", async (req: Request, res: Response) => {
|
||||
try {
|
||||
const id = parseInt(req.params.id);
|
||||
if (isNaN(id)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "유효하지 않은 설정 ID입니다.",
|
||||
errorCode: "INVALID_CONFIG_ID",
|
||||
});
|
||||
}
|
||||
|
||||
// 사용자 정보 가져오기
|
||||
const userInfo = (req as any).user;
|
||||
const userId = userInfo?.userId || "SYSTEM";
|
||||
|
||||
await externalCallConfigService.deleteConfig(id, userId);
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
message: "외부 호출 설정이 성공적으로 삭제되었습니다.",
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("외부 호출 설정 삭제 API 오류:", error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message:
|
||||
error instanceof Error ? error.message : "외부 호출 설정 삭제 실패",
|
||||
errorCode: "EXTERNAL_CALL_CONFIG_DELETE_ERROR",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 외부 호출 설정 테스트
|
||||
* POST /api/external-call-configs/:id/test
|
||||
*/
|
||||
router.post("/:id/test", async (req: Request, res: Response) => {
|
||||
try {
|
||||
const id = parseInt(req.params.id);
|
||||
if (isNaN(id)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "유효하지 않은 설정 ID입니다.",
|
||||
errorCode: "INVALID_CONFIG_ID",
|
||||
});
|
||||
}
|
||||
|
||||
const testResult = await externalCallConfigService.testConfig(id);
|
||||
|
||||
return res.json({
|
||||
success: testResult.success,
|
||||
message: testResult.message,
|
||||
data: testResult,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("외부 호출 설정 테스트 API 오류:", error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message:
|
||||
error instanceof Error ? error.message : "외부 호출 설정 테스트 실패",
|
||||
errorCode: "EXTERNAL_CALL_CONFIG_TEST_ERROR",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
313
backend-node/src/services/externalCallConfigService.ts
Normal file
313
backend-node/src/services/externalCallConfigService.ts
Normal file
@@ -0,0 +1,313 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { logger } from "../utils/logger";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
// 외부 호출 설정 타입 정의
|
||||
export interface ExternalCallConfig {
|
||||
id?: number;
|
||||
config_name: string;
|
||||
call_type: string;
|
||||
api_type?: string;
|
||||
config_data: any;
|
||||
description?: string;
|
||||
company_code?: string;
|
||||
is_active?: string;
|
||||
created_by?: string;
|
||||
updated_by?: string;
|
||||
}
|
||||
|
||||
export interface ExternalCallConfigFilter {
|
||||
company_code?: string;
|
||||
call_type?: string;
|
||||
api_type?: string;
|
||||
is_active?: string;
|
||||
search?: string;
|
||||
}
|
||||
|
||||
export class ExternalCallConfigService {
|
||||
/**
|
||||
* 외부 호출 설정 목록 조회
|
||||
*/
|
||||
async getConfigs(
|
||||
filter: ExternalCallConfigFilter = {}
|
||||
): Promise<ExternalCallConfig[]> {
|
||||
try {
|
||||
logger.info("=== 외부 호출 설정 목록 조회 시작 ===");
|
||||
logger.info(`필터 조건:`, filter);
|
||||
|
||||
const where: any = {};
|
||||
|
||||
// 회사 코드 필터
|
||||
if (filter.company_code) {
|
||||
where.company_code = filter.company_code;
|
||||
}
|
||||
|
||||
// 호출 타입 필터
|
||||
if (filter.call_type) {
|
||||
where.call_type = filter.call_type;
|
||||
}
|
||||
|
||||
// API 타입 필터
|
||||
if (filter.api_type) {
|
||||
where.api_type = filter.api_type;
|
||||
}
|
||||
|
||||
// 활성화 상태 필터
|
||||
if (filter.is_active) {
|
||||
where.is_active = filter.is_active;
|
||||
}
|
||||
|
||||
// 검색어 필터 (설정 이름 또는 설명)
|
||||
if (filter.search) {
|
||||
where.OR = [
|
||||
{ config_name: { contains: filter.search, mode: "insensitive" } },
|
||||
{ description: { contains: filter.search, mode: "insensitive" } },
|
||||
];
|
||||
}
|
||||
|
||||
const configs = await prisma.external_call_configs.findMany({
|
||||
where,
|
||||
orderBy: [{ is_active: "desc" }, { created_date: "desc" }],
|
||||
});
|
||||
|
||||
logger.info(`외부 호출 설정 조회 결과: ${configs.length}개`);
|
||||
return configs as ExternalCallConfig[];
|
||||
} catch (error) {
|
||||
logger.error("외부 호출 설정 목록 조회 실패:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 외부 호출 설정 단일 조회
|
||||
*/
|
||||
async getConfigById(id: number): Promise<ExternalCallConfig | null> {
|
||||
try {
|
||||
logger.info(`=== 외부 호출 설정 조회: ID ${id} ===`);
|
||||
|
||||
const config = await prisma.external_call_configs.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (config) {
|
||||
logger.info(`외부 호출 설정 조회 성공: ${config.config_name}`);
|
||||
} else {
|
||||
logger.warn(`외부 호출 설정을 찾을 수 없음: ID ${id}`);
|
||||
}
|
||||
|
||||
return config as ExternalCallConfig | null;
|
||||
} catch (error) {
|
||||
logger.error(`외부 호출 설정 조회 실패 (ID: ${id}):`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 외부 호출 설정 생성
|
||||
*/
|
||||
async createConfig(data: ExternalCallConfig): Promise<ExternalCallConfig> {
|
||||
try {
|
||||
logger.info("=== 외부 호출 설정 생성 시작 ===");
|
||||
logger.info(`생성할 설정:`, {
|
||||
config_name: data.config_name,
|
||||
call_type: data.call_type,
|
||||
api_type: data.api_type,
|
||||
company_code: data.company_code || "*",
|
||||
});
|
||||
|
||||
// 중복 이름 검사
|
||||
const existingConfig = await prisma.external_call_configs.findFirst({
|
||||
where: {
|
||||
config_name: data.config_name,
|
||||
company_code: data.company_code || "*",
|
||||
is_active: "Y",
|
||||
},
|
||||
});
|
||||
|
||||
if (existingConfig) {
|
||||
throw new Error(
|
||||
`동일한 이름의 외부 호출 설정이 이미 존재합니다: ${data.config_name}`
|
||||
);
|
||||
}
|
||||
|
||||
const newConfig = await prisma.external_call_configs.create({
|
||||
data: {
|
||||
config_name: data.config_name,
|
||||
call_type: data.call_type,
|
||||
api_type: data.api_type,
|
||||
config_data: data.config_data,
|
||||
description: data.description,
|
||||
company_code: data.company_code || "*",
|
||||
is_active: data.is_active || "Y",
|
||||
created_by: data.created_by,
|
||||
updated_by: data.updated_by,
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`외부 호출 설정 생성 완료: ${newConfig.config_name} (ID: ${newConfig.id})`
|
||||
);
|
||||
return newConfig as ExternalCallConfig;
|
||||
} catch (error) {
|
||||
logger.error("외부 호출 설정 생성 실패:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 외부 호출 설정 수정
|
||||
*/
|
||||
async updateConfig(
|
||||
id: number,
|
||||
data: Partial<ExternalCallConfig>
|
||||
): Promise<ExternalCallConfig> {
|
||||
try {
|
||||
logger.info(`=== 외부 호출 설정 수정 시작: ID ${id} ===`);
|
||||
|
||||
// 기존 설정 존재 확인
|
||||
const existingConfig = await this.getConfigById(id);
|
||||
if (!existingConfig) {
|
||||
throw new Error(`외부 호출 설정을 찾을 수 없습니다: ID ${id}`);
|
||||
}
|
||||
|
||||
// 이름 중복 검사 (다른 설정과 중복되는지)
|
||||
if (data.config_name && data.config_name !== existingConfig.config_name) {
|
||||
const duplicateConfig = await prisma.external_call_configs.findFirst({
|
||||
where: {
|
||||
config_name: data.config_name,
|
||||
company_code: data.company_code || existingConfig.company_code,
|
||||
is_active: "Y",
|
||||
id: { not: id },
|
||||
},
|
||||
});
|
||||
|
||||
if (duplicateConfig) {
|
||||
throw new Error(
|
||||
`동일한 이름의 외부 호출 설정이 이미 존재합니다: ${data.config_name}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const updatedConfig = await prisma.external_call_configs.update({
|
||||
where: { id },
|
||||
data: {
|
||||
...(data.config_name && { config_name: data.config_name }),
|
||||
...(data.call_type && { call_type: data.call_type }),
|
||||
...(data.api_type !== undefined && { api_type: data.api_type }),
|
||||
...(data.config_data && { config_data: data.config_data }),
|
||||
...(data.description !== undefined && {
|
||||
description: data.description,
|
||||
}),
|
||||
...(data.company_code && { company_code: data.company_code }),
|
||||
...(data.is_active && { is_active: data.is_active }),
|
||||
...(data.updated_by && { updated_by: data.updated_by }),
|
||||
updated_date: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`외부 호출 설정 수정 완료: ${updatedConfig.config_name} (ID: ${id})`
|
||||
);
|
||||
return updatedConfig as ExternalCallConfig;
|
||||
} catch (error) {
|
||||
logger.error(`외부 호출 설정 수정 실패 (ID: ${id}):`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 외부 호출 설정 삭제 (논리 삭제)
|
||||
*/
|
||||
async deleteConfig(id: number, deletedBy?: string): Promise<void> {
|
||||
try {
|
||||
logger.info(`=== 외부 호출 설정 삭제 시작: ID ${id} ===`);
|
||||
|
||||
// 기존 설정 존재 확인
|
||||
const existingConfig = await this.getConfigById(id);
|
||||
if (!existingConfig) {
|
||||
throw new Error(`외부 호출 설정을 찾을 수 없습니다: ID ${id}`);
|
||||
}
|
||||
|
||||
// 논리 삭제 (is_active = 'N')
|
||||
await prisma.external_call_configs.update({
|
||||
where: { id },
|
||||
data: {
|
||||
is_active: "N",
|
||||
updated_by: deletedBy,
|
||||
updated_date: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`외부 호출 설정 삭제 완료: ${existingConfig.config_name} (ID: ${id})`
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error(`외부 호출 설정 삭제 실패 (ID: ${id}):`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 외부 호출 설정 테스트
|
||||
*/
|
||||
async testConfig(id: number): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
logger.info(`=== 외부 호출 설정 테스트 시작: ID ${id} ===`);
|
||||
|
||||
const config = await this.getConfigById(id);
|
||||
if (!config) {
|
||||
throw new Error(`외부 호출 설정을 찾을 수 없습니다: ID ${id}`);
|
||||
}
|
||||
|
||||
// TODO: ExternalCallService를 사용하여 실제 테스트 호출
|
||||
// 현재는 기본적인 검증만 수행
|
||||
const configData = config.config_data as any;
|
||||
|
||||
let isValid = true;
|
||||
let validationMessage = "";
|
||||
|
||||
switch (config.api_type) {
|
||||
case "discord":
|
||||
if (!configData.webhookUrl) {
|
||||
isValid = false;
|
||||
validationMessage = "Discord 웹훅 URL이 필요합니다.";
|
||||
}
|
||||
break;
|
||||
case "slack":
|
||||
if (!configData.webhookUrl) {
|
||||
isValid = false;
|
||||
validationMessage = "Slack 웹훅 URL이 필요합니다.";
|
||||
}
|
||||
break;
|
||||
case "kakao-talk":
|
||||
if (!configData.accessToken) {
|
||||
isValid = false;
|
||||
validationMessage = "카카오톡 액세스 토큰이 필요합니다.";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (config.call_type === "rest-api" && !configData.url) {
|
||||
isValid = false;
|
||||
validationMessage = "API URL이 필요합니다.";
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
logger.warn(`외부 호출 설정 테스트 실패: ${validationMessage}`);
|
||||
return { success: false, message: validationMessage };
|
||||
}
|
||||
|
||||
logger.info(`외부 호출 설정 테스트 성공: ${config.config_name}`);
|
||||
return { success: true, message: "설정이 유효합니다." };
|
||||
} catch (error) {
|
||||
logger.error(`외부 호출 설정 테스트 실패 (ID: ${id}):`, error);
|
||||
return {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : "테스트 실패",
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new ExternalCallConfigService();
|
||||
@@ -124,4 +124,3 @@ export type SupportedExternalCallSettings =
|
||||
| DiscordSettings
|
||||
| GenericApiSettings
|
||||
| EmailSettings;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user