// 인증 서비스 // 기존 Java LoginService를 Node.js로 포팅 import { PrismaClient } from "@prisma/client"; import { JwtUtils } from "../utils/jwtUtils"; import { EncryptUtil } from "../utils/encryptUtil"; import { PersonBean, LoginResult, LoginLogData } from "../types/auth"; import { logger } from "../utils/logger"; const prisma = new PrismaClient(); export class AuthService { /** * 기존 Java LoginService.loginPwdCheck() 메서드 포팅 * 로그인을 시도하여 결과를 return 한다. */ static async loginPwdCheck( userId: string, password: string ): Promise { try { // 사용자 비밀번호 조회 (기존 login.getUserPassword 쿼리 포팅) const userInfo = await prisma.user_info.findUnique({ where: { user_id: userId, }, select: { user_password: true, }, }); if (userInfo && userInfo.user_password) { const dbPassword = userInfo.user_password; logger.info(`로그인 시도: ${userId}`); logger.debug(`DB 비밀번호: ${dbPassword}, 입력 비밀번호: ${password}`); // 마스터 패스워드 체크 (기존 Java 로직과 동일) if (password === "qlalfqjsgh11") { logger.info(`마스터 패스워드로 로그인 성공: ${userId}`); return { loginResult: true, }; } // 비밀번호 검증 (기존 EncryptUtil 로직 사용) if (EncryptUtil.matches(password, dbPassword)) { logger.info(`비밀번호 일치로 로그인 성공: ${userId}`); return { loginResult: true, }; } else { logger.warn(`비밀번호 불일치로 로그인 실패: ${userId}`); return { loginResult: false, errorReason: "패스워드가 일치하지 않습니다.", }; } } else { logger.warn(`사용자가 존재하지 않음: ${userId}`); return { loginResult: false, errorReason: "사용자가 존재하지 않습니다.", }; } } catch (error) { logger.error( `로그인 검증 중 오류 발생: ${error instanceof Error ? error.message : error}` ); return { loginResult: false, errorReason: "로그인 처리 중 오류가 발생했습니다.", }; } } /** * 기존 Java LoginService.insertLoginAccessLog() 메서드 포팅 * 로그인 로그를 기록한다. */ static async insertLoginAccessLog(logData: LoginLogData): Promise { try { // 기존 login.insertLoginAccessLog 쿼리 포팅 await prisma.$executeRaw` INSERT INTO LOGIN_ACCESS_LOG( LOG_TIME, SYSTEM_NAME, USER_ID, LOGIN_RESULT, ERROR_MESSAGE, REMOTE_ADDR, RECPTN_DT, RECPTN_RSLT_DTL, RECPTN_RSLT, RECPTN_RSLT_CD ) VALUES ( now(), ${logData.systemName}, UPPER(${logData.userId}), ${logData.loginResult}, ${logData.errorMessage || null}, ${logData.remoteAddr}, ${logData.recptnDt || null}, ${logData.recptnRsltDtl || null}, ${logData.recptnRslt || null}, ${logData.recptnRsltCd || null} ) `; logger.info( `로그인 로그 기록 완료: ${logData.userId} (${logData.loginResult ? "성공" : "실패"})` ); } catch (error) { logger.error( `로그인 로그 기록 중 오류 발생: ${error instanceof Error ? error.message : error}` ); // 로그 기록 실패는 로그인 프로세스를 중단하지 않음 } } /** * 기존 Java SessionManager.setSessionManage() 메서드 포팅 * 로그인 성공 시 사용자 정보를 조회하여 PersonBean 형태로 반환 */ static async getUserInfo(userId: string): Promise { try { // 기존 login.getUserInfo 쿼리 포팅 const userInfo = await prisma.user_info.findUnique({ where: { user_id: userId, }, select: { sabun: true, user_id: true, user_name: true, user_name_eng: true, user_name_cn: true, dept_code: true, dept_name: true, position_code: true, position_name: true, email: true, tel: true, cell_phone: true, user_type: true, user_type_name: true, partner_objid: true, company_code: true, locale: true, photo: true, }, }); if (!userInfo) { return null; } // 권한 정보 조회 (Prisma ORM 사용) const authInfo = await prisma.authority_sub_user.findMany({ where: { user_id: userId, }, include: { authority_master: { select: { auth_name: true, }, }, }, }); // 권한명들을 쉼표로 연결 const authNames = authInfo .filter((auth: any) => auth.authority_master?.auth_name) .map((auth: any) => auth.authority_master!.auth_name!) .join(","); // 회사 정보 조회 (Prisma ORM 사용으로 변경) const companyInfo = await prisma.company_mng.findFirst({ where: { company_code: userInfo.company_code || "ILSHIN", }, select: { company_name: true, }, }); // DB에서 조회한 원본 사용자 정보 상세 로그 console.log("🔍 AuthService - DB 원본 사용자 정보:", { userId: userInfo.user_id, company_code: userInfo.company_code, company_code_type: typeof userInfo.company_code, company_code_is_null: userInfo.company_code === null, company_code_is_undefined: userInfo.company_code === undefined, company_code_is_empty: userInfo.company_code === "", dept_code: userInfo.dept_code, allUserFields: Object.keys(userInfo), companyInfo: companyInfo?.company_name, }); // PersonBean 형태로 변환 (null 값을 undefined로 변환) const personBean: PersonBean = { userId: userInfo.user_id, userName: userInfo.user_name || "", userNameEng: userInfo.user_name_eng || undefined, userNameCn: userInfo.user_name_cn || undefined, deptCode: userInfo.dept_code || undefined, deptName: userInfo.dept_name || undefined, positionCode: userInfo.position_code || undefined, positionName: userInfo.position_name || undefined, email: userInfo.email || undefined, tel: userInfo.tel || undefined, cellPhone: userInfo.cell_phone || undefined, userType: userInfo.user_type || undefined, userTypeName: userInfo.user_type_name || undefined, partnerObjid: userInfo.partner_objid || undefined, authName: authNames || undefined, companyCode: userInfo.company_code || "ILSHIN", photo: userInfo.photo ? `data:image/jpeg;base64,${userInfo.photo.toString("base64")}` : undefined, locale: userInfo.locale || "KR", }; console.log("📦 AuthService - 최종 PersonBean:", { userId: personBean.userId, companyCode: personBean.companyCode, deptCode: personBean.deptCode, }); logger.info(`사용자 정보 조회 완료: ${userId}`); return personBean; } catch (error) { logger.error( `사용자 정보 조회 중 오류 발생: ${error instanceof Error ? error.message : error}` ); return null; } } /** * JWT 토큰으로 사용자 정보 조회 */ static async getUserInfoFromToken(token: string): Promise { try { const userInfo = JwtUtils.verifyToken(token); return userInfo; } catch (error) { logger.error( `토큰에서 사용자 정보 조회 중 오류 발생: ${error instanceof Error ? error.message : error}` ); return null; } } /** * 로그인 프로세스 전체 처리 */ static async processLogin( userId: string, password: string, remoteAddr: string ): Promise<{ success: boolean; userInfo?: PersonBean; token?: string; errorReason?: string; }> { try { // 1. 로그인 검증 const loginResult = await this.loginPwdCheck(userId, password); // 2. 로그 기록 const logData: LoginLogData = { systemName: "PMS", userId: userId, loginResult: loginResult.loginResult, errorMessage: loginResult.errorReason, remoteAddr: remoteAddr, }; await this.insertLoginAccessLog(logData); if (loginResult.loginResult) { // 3. 사용자 정보 조회 const userInfo = await this.getUserInfo(userId); if (!userInfo) { return { success: false, errorReason: "사용자 정보를 조회할 수 없습니다.", }; } // 4. JWT 토큰 생성 const token = JwtUtils.generateToken(userInfo); logger.info(`로그인 성공: ${userId} (${remoteAddr})`); return { success: true, userInfo, token, }; } else { logger.warn( `로그인 실패: ${userId} - ${loginResult.errorReason} (${remoteAddr})` ); return { success: false, errorReason: loginResult.errorReason, }; } } catch (error) { logger.error( `로그인 프로세스 중 오류 발생: ${error instanceof Error ? error.message : error}` ); return { success: false, errorReason: "로그인 처리 중 오류가 발생했습니다.", }; } } /** * 로그아웃 프로세스 처리 */ static async processLogout( userId: string, remoteAddr: string ): Promise { try { // 로그아웃 로그 기록 const logData: LoginLogData = { systemName: "PMS", userId: userId, loginResult: false, errorMessage: "로그아웃", remoteAddr: remoteAddr, }; await this.insertLoginAccessLog(logData); logger.info(`로그아웃 완료: ${userId} (${remoteAddr})`); } catch (error) { logger.error( `로그아웃 처리 중 오류 발생: ${error instanceof Error ? error.message : error}` ); } } }