2026-03-11 18:34:58 +09:00
|
|
|
// 스마트공장 활용 로그 전송 유틸리티
|
|
|
|
|
// https://log.smart-factory.kr 에 사용자 접속 로그를 전송
|
|
|
|
|
|
|
|
|
|
import axios from "axios";
|
|
|
|
|
import { logger } from "./logger";
|
2026-04-07 10:35:16 +09:00
|
|
|
import { query } from "../database/db";
|
2026-03-11 18:34:58 +09:00
|
|
|
|
|
|
|
|
const SMART_FACTORY_LOG_URL =
|
|
|
|
|
"https://log.smart-factory.kr/apisvc/sendLogDataJSON.do";
|
|
|
|
|
|
|
|
|
|
/**
|
2026-04-07 10:35:16 +09:00
|
|
|
* 스마트공장 활용 로그 전송 + DB 저장
|
2026-03-11 18:34:58 +09:00
|
|
|
* 로그인 성공 시 비동기로 호출하여 응답을 블로킹하지 않음
|
|
|
|
|
*/
|
|
|
|
|
export async function sendSmartFactoryLog(params: {
|
|
|
|
|
userId: string;
|
2026-04-07 10:35:16 +09:00
|
|
|
userName?: string;
|
2026-03-11 18:34:58 +09:00
|
|
|
remoteAddr: string;
|
|
|
|
|
useType?: string;
|
2026-04-03 11:23:02 +09:00
|
|
|
companyCode?: string;
|
2026-03-11 18:34:58 +09:00
|
|
|
}): Promise<void> {
|
2026-04-07 10:35:16 +09:00
|
|
|
const now = new Date();
|
|
|
|
|
const logDt = formatDateTime(now);
|
|
|
|
|
const useType = params.useType || "접속";
|
|
|
|
|
|
2026-04-03 11:23:02 +09:00
|
|
|
// 회사별 키 우선 조회, 없으면 공통 키 폴백
|
|
|
|
|
const apiKey = (params.companyCode && process.env[`SMART_FACTORY_API_KEY_${params.companyCode}`])
|
|
|
|
|
|| process.env.SMART_FACTORY_API_KEY;
|
2026-03-11 18:34:58 +09:00
|
|
|
|
|
|
|
|
if (!apiKey) {
|
|
|
|
|
logger.warn(
|
|
|
|
|
"SMART_FACTORY_API_KEY 환경변수가 설정되지 않아 스마트공장 로그 전송을 건너뜁니다."
|
|
|
|
|
);
|
2026-04-07 10:35:16 +09:00
|
|
|
// SKIPPED 상태로 DB 기록
|
|
|
|
|
await saveLog({
|
|
|
|
|
companyCode: params.companyCode || "",
|
|
|
|
|
userId: params.userId,
|
|
|
|
|
userName: params.userName,
|
|
|
|
|
useType,
|
|
|
|
|
connectIp: params.remoteAddr,
|
|
|
|
|
sendStatus: "SKIPPED",
|
|
|
|
|
responseStatus: null,
|
|
|
|
|
errorMessage: "API 키 미설정",
|
|
|
|
|
logDt: now,
|
|
|
|
|
});
|
2026-03-11 18:34:58 +09:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const logData = {
|
|
|
|
|
crtfcKey: apiKey,
|
|
|
|
|
logDt,
|
2026-04-07 10:35:16 +09:00
|
|
|
useSe: useType,
|
2026-03-11 18:34:58 +09:00
|
|
|
sysUser: params.userId,
|
|
|
|
|
conectIp: params.remoteAddr,
|
|
|
|
|
dataUsgqty: "",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const encodedLogData = encodeURIComponent(JSON.stringify(logData));
|
|
|
|
|
|
|
|
|
|
const response = await axios.get(SMART_FACTORY_LOG_URL, {
|
|
|
|
|
params: { logData: encodedLogData },
|
|
|
|
|
timeout: 5000,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
logger.info("스마트공장 로그 전송 완료", {
|
|
|
|
|
userId: params.userId,
|
|
|
|
|
status: response.status,
|
|
|
|
|
});
|
2026-04-07 10:35:16 +09:00
|
|
|
|
|
|
|
|
// SUCCESS 상태로 DB 기록
|
|
|
|
|
await saveLog({
|
|
|
|
|
companyCode: params.companyCode || "",
|
|
|
|
|
userId: params.userId,
|
|
|
|
|
userName: params.userName,
|
|
|
|
|
useType,
|
|
|
|
|
connectIp: params.remoteAddr,
|
|
|
|
|
sendStatus: "SUCCESS",
|
|
|
|
|
responseStatus: response.status,
|
|
|
|
|
errorMessage: null,
|
|
|
|
|
logDt: now,
|
|
|
|
|
});
|
2026-03-11 18:34:58 +09:00
|
|
|
} catch (error) {
|
2026-04-07 10:35:16 +09:00
|
|
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
2026-03-11 18:34:58 +09:00
|
|
|
// 스마트공장 로그 전송 실패해도 로그인에 영향 없도록 에러만 기록
|
|
|
|
|
logger.error("스마트공장 로그 전송 실패", {
|
|
|
|
|
userId: params.userId,
|
2026-04-07 10:35:16 +09:00
|
|
|
error: errorMsg,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// FAIL 상태로 DB 기록
|
|
|
|
|
await saveLog({
|
|
|
|
|
companyCode: params.companyCode || "",
|
|
|
|
|
userId: params.userId,
|
|
|
|
|
userName: params.userName,
|
|
|
|
|
useType,
|
|
|
|
|
connectIp: params.remoteAddr,
|
|
|
|
|
sendStatus: "FAIL",
|
|
|
|
|
responseStatus: null,
|
|
|
|
|
errorMessage: errorMsg,
|
|
|
|
|
logDt: now,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** DB에 로그 저장 */
|
|
|
|
|
async function saveLog(params: {
|
|
|
|
|
companyCode: string;
|
|
|
|
|
userId: string;
|
|
|
|
|
userName?: string;
|
|
|
|
|
useType: string;
|
|
|
|
|
connectIp: string;
|
|
|
|
|
sendStatus: string;
|
|
|
|
|
responseStatus: number | null;
|
|
|
|
|
errorMessage: string | null;
|
|
|
|
|
logDt: Date;
|
|
|
|
|
}): Promise<void> {
|
|
|
|
|
try {
|
|
|
|
|
await query(
|
|
|
|
|
`INSERT INTO smart_factory_log
|
|
|
|
|
(company_code, user_id, user_name, use_type, connect_ip, send_status, response_status, error_message, log_dt)
|
|
|
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
|
|
|
|
|
[
|
|
|
|
|
params.companyCode,
|
|
|
|
|
params.userId,
|
|
|
|
|
params.userName || null,
|
|
|
|
|
params.useType,
|
|
|
|
|
params.connectIp,
|
|
|
|
|
params.sendStatus,
|
|
|
|
|
params.responseStatus,
|
|
|
|
|
params.errorMessage,
|
|
|
|
|
params.logDt,
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
} catch (dbError) {
|
|
|
|
|
// DB 저장 실패해도 로그인 프로세스에 영향 없도록
|
|
|
|
|
logger.error("스마트공장 로그 DB 저장 실패", {
|
|
|
|
|
userId: params.userId,
|
|
|
|
|
error: dbError instanceof Error ? dbError.message : dbError,
|
2026-03-11 18:34:58 +09:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** yyyy-MM-dd HH:mm:ss.SSS 형식 */
|
|
|
|
|
function formatDateTime(date: Date): string {
|
|
|
|
|
const y = date.getFullYear();
|
|
|
|
|
const M = String(date.getMonth() + 1).padStart(2, "0");
|
|
|
|
|
const d = String(date.getDate()).padStart(2, "0");
|
|
|
|
|
const H = String(date.getHours()).padStart(2, "0");
|
|
|
|
|
const m = String(date.getMinutes()).padStart(2, "0");
|
|
|
|
|
const s = String(date.getSeconds()).padStart(2, "0");
|
|
|
|
|
const ms = String(date.getMilliseconds()).padStart(3, "0");
|
|
|
|
|
return `${y}-${M}-${d} ${H}:${m}:${s}.${ms}`;
|
|
|
|
|
}
|