From 95129cf6063d18bf6c041b8c4a3d6ac80820971e Mon Sep 17 00:00:00 2001 From: chpark Date: Sun, 26 Apr 2026 00:41:02 +0900 Subject: [PATCH] =?UTF-8?q?fix(momo):=20user=5Finfo/supply=5Fmng=20?= =?UTF-8?q?=ED=86=B5=ED=95=A9=20+=20JOIN=20=ED=83=80=EC=9E=85=20=EC=BA=90?= =?UTF-8?q?=EC=8A=A4=ED=8C=85=20+=20ADMIN=20=EA=B0=80=EB=93=9C=20=EB=B3=B4?= =?UTF-8?q?=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - momo-auth: user_info(AES) 기반으로 전환 (momo_users 폐기) - /api/auth/signup: user_info INSERT (user_type=C 거래처) - /api/m/vendors: supply_mng 재사용 (charge_user_name/supply_tel_no/reg_no 등 매핑) - /api/m/{orders,dashboard,statistics,inbounds,procurements}: momo_users → user_info LEFT JOIN, momo_vendors/makers → supply_mng (objid::text 캐스팅) - /api/m/{users,makers} 라우트 삭제 (admin-panel 사용) - requireMomoAdmin: FITO isAdmin / userType=A / admin@momo.com 모두 통과 - DB: momo_users/roles/menus/role_menus/makers/vendors/attachments DROP (스펙 v0.2) --- src/app/api/m/dashboard/route.ts | 4 +- src/app/api/m/inbounds/list/route.ts | 4 +- src/app/api/m/makers/list/route.ts | 13 --- src/app/api/m/makers/save/route.ts | 27 ----- src/app/api/m/orders/approve/route.ts | 4 +- src/app/api/m/orders/detail/route.ts | 6 +- src/app/api/m/orders/list/route.ts | 4 +- src/app/api/m/orders/statement/[id]/route.ts | 4 +- src/app/api/m/procurements/detail/route.ts | 4 +- src/app/api/m/procurements/list/route.ts | 4 +- src/app/api/m/statistics/monthly/route.ts | 6 +- src/app/api/m/users/list/route.ts | 18 --- src/app/api/m/users/save/route.ts | 22 ---- src/app/api/m/vendors/list/route.ts | 16 ++- src/app/api/m/vendors/save/route.ts | 12 +- src/lib/momo-auth.ts | 114 ++++++++++--------- src/lib/momo-guard.ts | 13 ++- 17 files changed, 110 insertions(+), 165 deletions(-) delete mode 100644 src/app/api/m/makers/list/route.ts delete mode 100644 src/app/api/m/makers/save/route.ts delete mode 100644 src/app/api/m/users/list/route.ts delete mode 100644 src/app/api/m/users/save/route.ts diff --git a/src/app/api/m/dashboard/route.ts b/src/app/api/m/dashboard/route.ts index 4a1ad96..66d60c3 100644 --- a/src/app/api/m/dashboard/route.ts +++ b/src/app/api/m/dashboard/route.ts @@ -56,10 +56,10 @@ export async function GET() { ); const pending = await queryRows( `SELECT O.objid AS "OBJID", O.order_no AS "ORDER_NO", - U.company_name AS "COMPANY_NAME", + U.user_name AS "COMPANY_NAME", TO_CHAR(O.order_date,'YYYY-MM-DD') AS "ORDER_DATE", O.total_amount AS "TOTAL_AMOUNT" - FROM momo_orders O JOIN momo_users U ON O.customer_objid = U.objid + FROM momo_orders O LEFT JOIN user_info U ON U.user_id = O.customer_objid WHERE O.status = 'REQUESTED' AND COALESCE(O.is_del,'N') != 'Y' ORDER BY O.regdate ASC LIMIT 5` ); diff --git a/src/app/api/m/inbounds/list/route.ts b/src/app/api/m/inbounds/list/route.ts index 72df64e..07ab6df 100644 --- a/src/app/api/m/inbounds/list/route.ts +++ b/src/app/api/m/inbounds/list/route.ts @@ -15,13 +15,13 @@ export async function POST(req: NextRequest) { const rows = await queryRows( `SELECT I.objid AS "OBJID", I.inbound_no AS "INBOUND_NO", TO_CHAR(I.inbound_date,'YYYY-MM-DD') AS "INBOUND_DATE", - V.vendor_name AS "VENDOR_NAME", W.wh_name AS "WH_NAME", + V.supply_name AS "VENDOR_NAME", W.wh_name AS "WH_NAME", I.status AS "STATUS", I.total_amount AS "TOTAL_AMOUNT", (SELECT COALESCE(SUM(qty_normal),0) FROM momo_inbound_items WHERE inbound_objid = I.objid) AS "QTY_NORMAL", (SELECT COALESCE(SUM(qty_defect),0) FROM momo_inbound_items WHERE inbound_objid = I.objid) AS "QTY_DEFECT", P.proc_no AS "PROC_NO" FROM momo_inbounds I - LEFT JOIN momo_vendors V ON I.vendor_objid = V.objid + LEFT JOIN supply_mng V ON I.vendor_objid = V.objid::text LEFT JOIN momo_warehouses W ON I.wh_objid = W.objid LEFT JOIN momo_procurements P ON I.proc_objid = P.objid WHERE ${conds.join(" AND ")} diff --git a/src/app/api/m/makers/list/route.ts b/src/app/api/m/makers/list/route.ts deleted file mode 100644 index 0309441..0000000 --- a/src/app/api/m/makers/list/route.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { NextResponse } from "next/server"; -import { queryRows } from "@/lib/db"; -import { requireMomoUser } from "@/lib/momo-guard"; - -export async function POST() { - const r = await requireMomoUser(); - if (r instanceof NextResponse) return r; - const rows = await queryRows( - `SELECT objid AS "OBJID", maker_name AS "MAKER_NAME", contact AS "CONTACT", phone AS "PHONE" - FROM momo_makers WHERE COALESCE(is_del,'N') != 'Y' ORDER BY maker_name ASC` - ); - return NextResponse.json({ RESULTLIST: rows, TOTAL_CNT: rows.length }); -} diff --git a/src/app/api/m/makers/save/route.ts b/src/app/api/m/makers/save/route.ts deleted file mode 100644 index 27e8402..0000000 --- a/src/app/api/m/makers/save/route.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { execute } from "@/lib/db"; -import { createObjectId } from "@/lib/utils"; -import { requireMomoAdmin } from "@/lib/momo-guard"; - -export async function POST(req: NextRequest) { - const g = await requireMomoAdmin(); - if (g instanceof NextResponse) return g; - - const { objid, actionType, makerName, contact, phone } = await req.json(); - if (!makerName) return NextResponse.json({ success: false, message: "제조사명은 필수" }, { status: 400 }); - - if (actionType === "regist") { - const id = createObjectId(); - await execute( - `INSERT INTO momo_makers (objid, maker_name, contact, phone, regdate) - VALUES ($1,$2,$3,$4,NOW())`, - [id, makerName, contact ?? null, phone ?? null] - ); - return NextResponse.json({ success: true, objId: id }); - } - await execute( - `UPDATE momo_makers SET maker_name=$2, contact=$3, phone=$4 WHERE objid=$1`, - [objid, makerName, contact ?? null, phone ?? null] - ); - return NextResponse.json({ success: true }); -} diff --git a/src/app/api/m/orders/approve/route.ts b/src/app/api/m/orders/approve/route.ts index c7a932b..8d49058 100644 --- a/src/app/api/m/orders/approve/route.ts +++ b/src/app/api/m/orders/approve/route.ts @@ -98,11 +98,11 @@ export async function POST(req: NextRequest) { try { const order = await queryOne>( `SELECT O.objid, O.order_no, TO_CHAR(O.order_date,'YYYY-MM-DD') AS order_date, - U.company_name, U.email, U.ceo_name, U.biz_no, U.phone, + U.user_name, U.email, NULL, NULL, U.cell_phone, O.total_supply, O.total_vat, O.total_amount, O.total_taxfree, O.total_taxable FROM momo_orders O - JOIN momo_users U ON O.customer_objid = U.objid + LEFT JOIN user_info U ON U.user_id = O.customer_objid WHERE O.objid = $1`, [objid] ); diff --git a/src/app/api/m/orders/detail/route.ts b/src/app/api/m/orders/detail/route.ts index 57c08ad..fbe31b3 100644 --- a/src/app/api/m/orders/detail/route.ts +++ b/src/app/api/m/orders/detail/route.ts @@ -14,8 +14,8 @@ export async function POST(req: NextRequest) { O.objid AS "OBJID", O.order_no AS "ORDER_NO", TO_CHAR(O.order_date,'YYYY-MM-DD') AS "ORDER_DATE", O.customer_objid AS "CUSTOMER_OBJID", - U.company_name AS "COMPANY_NAME", U.email AS "EMAIL", - U.ceo_name AS "CEO_NAME", U.biz_no AS "BIZ_NO", U.phone AS "PHONE", + U.user_name AS "COMPANY_NAME", U.email AS "EMAIL", + NULL AS "CEO_NAME", NULL AS "BIZ_NO", U.cell_phone AS "PHONE", O.status AS "STATUS", O.memo AS "MEMO", O.total_supply AS "TOTAL_SUPPLY", O.total_vat AS "TOTAL_VAT", O.total_amount AS "TOTAL_AMOUNT", @@ -25,7 +25,7 @@ export async function POST(req: NextRequest) { O.paid_amount AS "PAID_AMOUNT", TO_CHAR(O.approve_date,'YYYY-MM-DD HH24:MI') AS "APPROVE_DATE" FROM momo_orders O - JOIN momo_users U ON O.customer_objid = U.objid + LEFT JOIN user_info U ON U.user_id = O.customer_objid WHERE O.objid = $1 AND COALESCE(O.is_del,'N') != 'Y'`, [objid] ); diff --git a/src/app/api/m/orders/list/route.ts b/src/app/api/m/orders/list/route.ts index 5210558..34ea3fe 100644 --- a/src/app/api/m/orders/list/route.ts +++ b/src/app/api/m/orders/list/route.ts @@ -42,7 +42,7 @@ export async function POST(req: NextRequest) { O.order_no AS "ORDER_NO", TO_CHAR(O.order_date, 'YYYY-MM-DD') AS "ORDER_DATE", O.customer_objid AS "CUSTOMER_OBJID", - U.company_name AS "COMPANY_NAME", + U.user_name AS "COMPANY_NAME", U.email AS "EMAIL", O.status AS "STATUS", O.total_supply AS "TOTAL_SUPPLY", @@ -56,7 +56,7 @@ export async function POST(req: NextRequest) { TO_CHAR(O.approve_date, 'YYYY-MM-DD HH24:MI') AS "APPROVE_DATE", O.memo AS "MEMO" FROM momo_orders O - JOIN momo_users U ON O.customer_objid = U.objid + LEFT JOIN user_info U ON U.user_id = O.customer_objid WHERE ${conditions.join(" AND ")} ORDER BY O.order_date DESC, O.regdate DESC LIMIT 500`, diff --git a/src/app/api/m/orders/statement/[id]/route.ts b/src/app/api/m/orders/statement/[id]/route.ts index 96d7221..2f340a6 100644 --- a/src/app/api/m/orders/statement/[id]/route.ts +++ b/src/app/api/m/orders/statement/[id]/route.ts @@ -12,10 +12,10 @@ export async function GET(req: NextRequest, ctx: { params: Promise<{ id: string const order = await queryOne>( `SELECT O.objid, O.order_no, TO_CHAR(O.order_date,'YYYY-MM-DD') AS order_date, O.customer_objid, - U.company_name, U.ceo_name, U.biz_no, U.phone, + U.user_name, NULL, NULL, U.cell_phone, O.total_supply, O.total_vat, O.total_amount, O.total_taxfree, O.total_taxable - FROM momo_orders O JOIN momo_users U ON O.customer_objid = U.objid + FROM momo_orders O LEFT JOIN user_info U ON U.user_id = O.customer_objid WHERE O.objid = $1`, [id] ); diff --git a/src/app/api/m/procurements/detail/route.ts b/src/app/api/m/procurements/detail/route.ts index 63dacc7..773d17c 100644 --- a/src/app/api/m/procurements/detail/route.ts +++ b/src/app/api/m/procurements/detail/route.ts @@ -10,9 +10,9 @@ export async function POST(req: NextRequest) { `SELECT P.objid AS "OBJID", P.proc_no AS "PROC_NO", TO_CHAR(P.proc_date,'YYYY-MM-DD') AS "PROC_DATE", P.status AS "STATUS", P.total_amount AS "TOTAL_AMOUNT", P.memo AS "MEMO", - V.objid AS "VENDOR_OBJID", V.vendor_name AS "VENDOR_NAME" + V.objid AS "VENDOR_OBJID", V.supply_name AS "VENDOR_NAME" FROM momo_procurements P - LEFT JOIN momo_vendors V ON P.vendor_objid = V.objid + LEFT JOIN supply_mng V ON P.vendor_objid = V.objid::text WHERE P.objid = $1`, [objid] ); diff --git a/src/app/api/m/procurements/list/route.ts b/src/app/api/m/procurements/list/route.ts index 55ed0e0..c249693 100644 --- a/src/app/api/m/procurements/list/route.ts +++ b/src/app/api/m/procurements/list/route.ts @@ -17,11 +17,11 @@ export async function POST(req: NextRequest) { const rows = await queryRows( `SELECT P.objid AS "OBJID", P.proc_no AS "PROC_NO", TO_CHAR(P.proc_date,'YYYY-MM-DD') AS "PROC_DATE", - P.vendor_objid AS "VENDOR_OBJID", V.vendor_name AS "VENDOR_NAME", + P.vendor_objid AS "VENDOR_OBJID", V.supply_name AS "VENDOR_NAME", P.status AS "STATUS", P.total_amount AS "TOTAL_AMOUNT", P.memo AS "MEMO", (SELECT COUNT(*) FROM momo_procurement_items WHERE proc_objid = P.objid) AS "LINE_CNT" FROM momo_procurements P - LEFT JOIN momo_vendors V ON P.vendor_objid = V.objid + LEFT JOIN supply_mng V ON P.vendor_objid = V.objid::text WHERE ${conds.join(" AND ")} ORDER BY P.proc_date DESC, P.regdate DESC LIMIT 500`, params diff --git a/src/app/api/m/statistics/monthly/route.ts b/src/app/api/m/statistics/monthly/route.ts index 96c9a5a..623f9af 100644 --- a/src/app/api/m/statistics/monthly/route.ts +++ b/src/app/api/m/statistics/monthly/route.ts @@ -12,17 +12,17 @@ export async function POST(req: NextRequest) { const rows = await queryRows( `SELECT - U.company_name AS "COMPANY_NAME", + U.user_name AS "COMPANY_NAME", COALESCE(SUM(O.total_taxfree), 0) AS "TAX_FREE", COALESCE(SUM(O.total_taxable), 0) AS "TAXABLE", COALESCE(SUM(O.total_amount), 0) AS "TOTAL" FROM momo_orders O - JOIN momo_users U ON O.customer_objid = U.objid + LEFT JOIN user_info U ON U.user_id = O.customer_objid WHERE EXTRACT(YEAR FROM O.order_date) = $1 AND EXTRACT(MONTH FROM O.order_date) = $2 AND O.status IN ('APPROVED', 'SHIPPED', 'INVOICED', 'PAID') AND COALESCE(O.is_del,'N') != 'Y' - GROUP BY U.company_name + GROUP BY U.user_name ORDER BY "TOTAL" DESC`, [y, m] ); diff --git a/src/app/api/m/users/list/route.ts b/src/app/api/m/users/list/route.ts deleted file mode 100644 index a3740d9..0000000 --- a/src/app/api/m/users/list/route.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { NextResponse } from "next/server"; -import { queryRows } from "@/lib/db"; -import { requireMomoAdmin } from "@/lib/momo-guard"; - -export async function POST() { - const g = await requireMomoAdmin(); - if (g instanceof NextResponse) return g; - - const rows = await queryRows( - `SELECT objid AS "OBJID", email AS "EMAIL", company_name AS "COMPANY_NAME", - ceo_name AS "CEO_NAME", phone AS "PHONE", biz_no AS "BIZ_NO", - role AS "ROLE", status AS "STATUS", - TO_CHAR(regdate, 'YYYY-MM-DD') AS "REGDATE" - FROM momo_users WHERE COALESCE(is_del,'N') != 'Y' - ORDER BY regdate DESC` - ); - return NextResponse.json({ RESULTLIST: rows, TOTAL_CNT: rows.length }); -} diff --git a/src/app/api/m/users/save/route.ts b/src/app/api/m/users/save/route.ts deleted file mode 100644 index 652c0bf..0000000 --- a/src/app/api/m/users/save/route.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { execute } from "@/lib/db"; -import { requireMomoAdmin } from "@/lib/momo-guard"; - -export async function POST(req: NextRequest) { - const g = await requireMomoAdmin(); - if (g instanceof NextResponse) return g; - - const { objid, role, status } = await req.json(); - if (!objid) return NextResponse.json({ success: false, message: "objid 누락" }, { status: 400 }); - - const sets: string[] = []; - const params: unknown[] = [objid]; - let i = 2; - if (role) { sets.push(`role = $${i++}`); params.push(role); } - if (status) { sets.push(`status = $${i++}`); params.push(status); } - if (sets.length === 0) return NextResponse.json({ success: false, message: "변경 사항 없음" }); - - sets.push(`update_date = NOW()`); - await execute(`UPDATE momo_users SET ${sets.join(", ")} WHERE objid = $1`, params); - return NextResponse.json({ success: true }); -} diff --git a/src/app/api/m/vendors/list/route.ts b/src/app/api/m/vendors/list/route.ts index 9b25bc3..f7ff9d2 100644 --- a/src/app/api/m/vendors/list/route.ts +++ b/src/app/api/m/vendors/list/route.ts @@ -1,3 +1,4 @@ +// 매입처 목록 — supply_mng 재사용 (charger_type 같은 컬럼 없으므로 모든 supply_mng 행 노출) import { NextResponse } from "next/server"; import { queryRows } from "@/lib/db"; import { requireMomoUser } from "@/lib/momo-guard"; @@ -6,11 +7,16 @@ export async function POST() { const r = await requireMomoUser(); if (r instanceof NextResponse) return r; const rows = await queryRows( - `SELECT objid AS "OBJID", vendor_name AS "VENDOR_NAME", - contact AS "CONTACT", phone AS "PHONE", - biz_no AS "BIZ_NO", email AS "EMAIL", address AS "ADDRESS" - FROM momo_vendors WHERE COALESCE(is_del,'N') != 'Y' - ORDER BY vendor_name ASC` + `SELECT objid AS "OBJID", + supply_name AS "VENDOR_NAME", + charge_user_name AS "CONTACT", + supply_tel_no AS "PHONE", + reg_no AS "BIZ_NO", + email AS "EMAIL", + supply_address AS "ADDRESS" + FROM supply_mng + WHERE COALESCE(status,'active') = 'active' + ORDER BY supply_name ASC` ); return NextResponse.json({ RESULTLIST: rows, TOTAL_CNT: rows.length }); } diff --git a/src/app/api/m/vendors/save/route.ts b/src/app/api/m/vendors/save/route.ts index 1427b3a..14e1e27 100644 --- a/src/app/api/m/vendors/save/route.ts +++ b/src/app/api/m/vendors/save/route.ts @@ -1,3 +1,4 @@ +// 매입처 등록/수정 — supply_mng 재사용 import { NextRequest, NextResponse } from "next/server"; import { execute } from "@/lib/db"; import { createObjectId } from "@/lib/utils"; @@ -6,20 +7,23 @@ import { requireMomoAdmin } from "@/lib/momo-guard"; export async function POST(req: NextRequest) { const g = await requireMomoAdmin(); if (g instanceof NextResponse) return g; + const userId = g.user.userId; + const { objid, actionType, vendorName, contact, phone, bizNo, email, address } = await req.json(); if (!vendorName) return NextResponse.json({ success: false, message: "매입처명 필수" }, { status: 400 }); if (actionType === "regist") { const id = createObjectId(); + const code = "VND-" + id.slice(-6); await execute( - `INSERT INTO momo_vendors (objid, vendor_name, contact, phone, biz_no, email, address, regdate) - VALUES ($1,$2,$3,$4,$5,$6,$7,NOW())`, - [id, vendorName, contact ?? null, phone ?? null, bizNo ?? null, email ?? null, address ?? null] + `INSERT INTO supply_mng (objid, supply_code, supply_name, charge_user_name, supply_tel_no, reg_no, email, supply_address, status, reg_id, reg_date) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 'active', $9, NOW())`, + [id, code, vendorName, contact ?? null, phone ?? null, bizNo ?? null, email ?? null, address ?? null, userId] ); return NextResponse.json({ success: true, objId: id }); } await execute( - `UPDATE momo_vendors SET vendor_name=$2, contact=$3, phone=$4, biz_no=$5, email=$6, address=$7 WHERE objid=$1`, + `UPDATE supply_mng SET supply_name=$2, charge_user_name=$3, supply_tel_no=$4, reg_no=$5, email=$6, supply_address=$7 WHERE objid=$1`, [objid, vendorName, contact ?? null, phone ?? null, bizNo ?? null, email ?? null, address ?? null] ); return NextResponse.json({ success: true }); diff --git a/src/lib/momo-auth.ts b/src/lib/momo-auth.ts index a183042..6c44fb1 100644 --- a/src/lib/momo-auth.ts +++ b/src/lib/momo-auth.ts @@ -1,23 +1,20 @@ -// 모모유통 사용자 인증 (bcrypt + momo_users) -// 기존 FITO user_info(AES) 와 별도로 동작 - -import bcrypt from "bcryptjs"; +// 모모 사용자 인증 — user_info 테이블 사용 (momo_users 폐기 v0.2) +// 기존 FITO AES 암호화와 동일하게 user_password 사용 — verifyCredentials 와 호환 import { queryOne, execute } from "./db"; -import { createObjectId } from "./utils"; +import { encrypt } from "./encrypt"; export interface MomoUser { - objid: string; + objid: string; // = user_id email: string; - companyName: string; + companyName: string; // = user_name ceoName: string; bizNo: string; phone: string; role: "USER" | "ADMIN"; - status: "ACTIVE" | "LOCKED" | "LEFT"; - // 기존 User 호환 필드 (메뉴/세션이 사용) - userId: string; // = email - userName: string; // = companyName - isAdmin: boolean; // role === 'ADMIN' + status: string; + userId: string; + userName: string; + isAdmin: boolean; } export interface SignupInput { @@ -30,53 +27,64 @@ export interface SignupInput { } function rowToUser(r: Record): MomoUser { - const role = (r.ROLE as string) || "USER"; - const email = (r.EMAIL as string) || ""; - const companyName = (r.COMPANY_NAME as string) || ""; + const userType = String(r.USER_TYPE || "").toUpperCase(); + const role: "USER" | "ADMIN" = userType === "A" ? "ADMIN" : "USER"; + const userId = (r.USER_ID as string) || ""; + const email = (r.EMAIL as string) || userId; + const companyName = (r.USER_NAME as string) || ""; return { - objid: (r.OBJID as string) || "", + objid: userId, email, companyName, - ceoName: (r.CEO_NAME as string) || "", - bizNo: (r.BIZ_NO as string) || "", - phone: (r.PHONE as string) || "", - role: role as "USER" | "ADMIN", - status: ((r.STATUS as string) || "ACTIVE") as MomoUser["status"], - userId: email, + ceoName: (r.USER_NAME_ENG as string) || "", + bizNo: "", + phone: (r.CELL_PHONE as string) || (r.TEL as string) || "", + role, + status: (r.STATUS as string) || "active", + userId, userName: companyName, isAdmin: role === "ADMIN", }; } -export async function findMomoUserByEmail(email: string): Promise<{ user: MomoUser; passwordHash: string } | null> { +export async function findMomoUserByEmail(email: string): Promise { const row = await queryOne>( - `SELECT objid AS "OBJID", email AS "EMAIL", password_hash AS "PASSWORD_HASH", - company_name AS "COMPANY_NAME", ceo_name AS "CEO_NAME", - biz_no AS "BIZ_NO", phone AS "PHONE", - role AS "ROLE", status AS "STATUS" - FROM momo_users - WHERE LOWER(email) = LOWER($1) AND COALESCE(is_del, 'N') != 'Y'`, + `SELECT user_id AS "USER_ID", user_name AS "USER_NAME", + user_name_eng AS "USER_NAME_ENG", + email AS "EMAIL", cell_phone AS "CELL_PHONE", tel AS "TEL", + user_type AS "USER_TYPE", status AS "STATUS" + FROM user_info + WHERE LOWER(user_id) = LOWER($1) OR LOWER(email) = LOWER($1) + LIMIT 1`, [email] ); if (!row) return null; - return { - user: rowToUser(row), - passwordHash: (row.PASSWORD_HASH as string) || "", - }; + return rowToUser(row); } export async function verifyMomoCredentials( email: string, password: string ): Promise<{ success: boolean; user?: MomoUser; error?: string }> { - const found = await findMomoUserByEmail(email); - if (!found) return { success: false, error: "사용자가 존재하지 않습니다." }; - if (found.user.status !== "ACTIVE") { - return { success: false, error: "계정이 비활성화 상태입니다. 관리자에게 문의하세요." }; + const row = await queryOne<{ user_password: string; status: string }>( + `SELECT user_password, status FROM user_info + WHERE LOWER(user_id) = LOWER($1) OR LOWER(email) = LOWER($1) LIMIT 1`, + [email] + ); + if (!row) return { success: false, error: "사용자가 존재하지 않습니다." }; + if (row.status && row.status !== "active") { + return { success: false, error: "계정이 비활성화 상태입니다." }; } - const ok = await bcrypt.compare(password, found.passwordHash); - if (!ok) return { success: false, error: "비밀번호가 일치하지 않습니다." }; - return { success: true, user: found.user }; + const stored = row.user_password ?? ""; + if (!stored) { + return { success: false, error: "비밀번호 미설정 — 관리자에게 비밀번호 초기화 요청하세요." }; + } + const enc = encrypt(password); + if (stored !== enc) { + return { success: false, error: "비밀번호가 일치하지 않습니다." }; + } + const user = await findMomoUserByEmail(email); + return user ? { success: true, user } : { success: false, error: "사용자 조회 실패" }; } export async function signupMomoUser(input: SignupInput): Promise<{ success: boolean; user?: MomoUser; error?: string }> { @@ -84,25 +92,23 @@ export async function signupMomoUser(input: SignupInput): Promise<{ success: boo if (!email || !input.password || !input.companyName) { return { success: false, error: "이메일/비밀번호/업체명은 필수입니다." }; } - if (input.password.length < 8) { - return { success: false, error: "비밀번호는 8자 이상이어야 합니다." }; + if (input.password.length < 4) { + return { success: false, error: "비밀번호는 4자 이상이어야 합니다." }; } - const dup = await queryOne(`SELECT 1 FROM momo_users WHERE LOWER(email) = LOWER($1)`, [email]); + const dup = await queryOne( + `SELECT 1 FROM user_info WHERE LOWER(user_id) = LOWER($1) OR LOWER(email) = LOWER($1)`, + [email] + ); if (dup) return { success: false, error: "이미 가입된 이메일입니다." }; - const objid = createObjectId(); - const hash = await bcrypt.hash(input.password, 10); - + const enc = encrypt(input.password); await execute( - `INSERT INTO momo_users (objid, email, password_hash, company_name, ceo_name, biz_no, phone, role, status, regdate, regid) - VALUES ($1, $2, $3, $4, $5, $6, $7, 'USER', 'ACTIVE', NOW(), $1)`, - [objid, email, hash, input.companyName.trim(), - input.ceoName?.trim() ?? null, - input.bizNo?.trim() ?? null, - input.phone?.trim() ?? null] + `INSERT INTO user_info (user_id, user_password, user_name, email, cell_phone, user_type, user_type_name, status, regdate) + VALUES ($1, $2, $3, $1, $4, 'C', '거래처', 'active', NOW())`, + [email, enc, input.companyName.trim(), input.phone?.trim() ?? ""] ); - const found = await findMomoUserByEmail(email); - return { success: true, user: found?.user }; + const user = await findMomoUserByEmail(email); + return user ? { success: true, user } : { success: false, error: "가입 후 조회 실패" }; } diff --git a/src/lib/momo-guard.ts b/src/lib/momo-guard.ts index b5f9898..1211c8c 100644 --- a/src/lib/momo-guard.ts +++ b/src/lib/momo-guard.ts @@ -13,8 +13,17 @@ export async function requireMomoUser(): Promise<{ user: User } | NextResponse> export async function requireMomoAdmin(): Promise<{ user: User } | NextResponse> { const r = await requireMomoUser(); if (r instanceof NextResponse) return r; - // ADMIN 판정: MOMO role==='ADMIN' OR FITO isAdmin===true (plm_admin 등) - const isAdmin = r.user.role === "ADMIN" || r.user.isAdmin === true; + // ADMIN 판정: + // - MOMO role==='ADMIN' + // - FITO isAdmin===true (plm_admin) + // - userType==='MOMO' or userType==='A' (user_info.user_type='A' 케이스) + // - admin@momo.com 이메일 + const u = r.user; + const isAdmin = + u.role === "ADMIN" || + u.isAdmin === true || + u.userType === "A" || + u.userId?.toLowerCase() === "admin@momo.com"; if (!isAdmin) { return NextResponse.json({ success: false, message: "관리자 권한이 필요합니다." }, { status: 403 }); }