From b0b787a8cebf756657eb5e4a730283197daceac3 Mon Sep 17 00:00:00 2001 From: hbyang Date: Thu, 18 Jun 2026 16:30:03 +0900 Subject: [PATCH] =?UTF-8?q?cors=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= =?UTF-8?q?=20.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/router/v1/validator/dependencies.py | 8 +++++--- negodata/backend/common/database/model/models.py | 10 ++++++++++ negodata/backend/router/v1/validator/dependencies.py | 9 +++++---- negodata/front/Dockerfile | 3 ++- postgres-init/01-schema.sql | 2 +- postgres-init/03-seed-negodata.sql | 4 ++-- 6 files changed, 25 insertions(+), 11 deletions(-) diff --git a/backend/router/v1/validator/dependencies.py b/backend/router/v1/validator/dependencies.py index 73ad74e..5aa1fa6 100644 --- a/backend/router/v1/validator/dependencies.py +++ b/backend/router/v1/validator/dependencies.py @@ -3,7 +3,7 @@ import json from typing import Any, Union from fastapi import Depends -from fastapi.responses import ORJSONResponse +from fastapi.responses import JSONResponse from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer import bcrypt from jose import jwt, JWTError, ExpiredSignatureError @@ -109,5 +109,7 @@ def RemoveNoneValues(obj: Any) -> Any: return obj -def RemoveNoneResponse(obj) -> ORJSONResponse: - return ORJSONResponse(content=RemoveNoneValues(obj.model_dump())) +def RemoveNoneResponse(obj) -> JSONResponse: + # mode="json": datetime/uuid 등을 JSON-safe 문자열로 변환(표준 JSONResponse 가 직렬화 가능). + # (ORJSONResponse 는 최신 FastAPI 에서 deprecated) + return JSONResponse(content=RemoveNoneValues(obj.model_dump(mode="json"))) diff --git a/negodata/backend/common/database/model/models.py b/negodata/backend/common/database/model/models.py index 1ee8139..05e4910 100644 --- a/negodata/backend/common/database/model/models.py +++ b/negodata/backend/common/database/model/models.py @@ -35,6 +35,7 @@ class MainTableMixin(_DBTypeMixin): # ERD 도메인 모델 class companies(MainTableMixin, MAIN_BASE): __tablename__ = "companies" + __table_args__ = {"schema": "company"} company_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) name = Column(String(100), nullable=False) @@ -50,6 +51,7 @@ class companies(MainTableMixin, MAIN_BASE): class users(MainTableMixin, MAIN_BASE): __tablename__ = "users" + __table_args__ = {"schema": "company"} user_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) company_id = Column(UUID(as_uuid=True), nullable=False, index=True) @@ -65,6 +67,7 @@ class users(MainTableMixin, MAIN_BASE): class items(MainTableMixin, MAIN_BASE): __tablename__ = "items" + __table_args__ = {"schema": "partner"} item_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) company_id = Column(UUID(as_uuid=True), nullable=False, index=True) @@ -94,6 +97,7 @@ class items(MainTableMixin, MAIN_BASE): class suppliers(MainTableMixin, MAIN_BASE): __tablename__ = "suppliers" + __table_args__ = {"schema": "partner"} supplier_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) company_id = Column(UUID(as_uuid=True), nullable=False, index=True) @@ -109,6 +113,7 @@ class suppliers(MainTableMixin, MAIN_BASE): class nego_cards(MainTableMixin, MAIN_BASE): __tablename__ = "nego_cards" + __table_args__ = {"schema": "card"} nego_card_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) user_id = Column(UUID(as_uuid=True), nullable=True, index=True) # 등록 유저(o2o 기본 카드는 NULL) @@ -120,6 +125,7 @@ class nego_cards(MainTableMixin, MAIN_BASE): class wild_cards(MainTableMixin, MAIN_BASE): __tablename__ = "wild_cards" + __table_args__ = {"schema": "card"} wild_card_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) user_id = Column(UUID(as_uuid=True), nullable=True, index=True) # 등록 유저(o2o 기본 카드는 NULL) @@ -134,6 +140,7 @@ class wild_cards(MainTableMixin, MAIN_BASE): class quotation_settings(MainTableMixin, MAIN_BASE): __tablename__ = "quotation_settings" + __table_args__ = {"schema": "quotation"} qt_setting_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) user_id = Column(UUID(as_uuid=True), nullable=True, index=True) # 설정 소유 유저 @@ -144,6 +151,7 @@ class quotation_settings(MainTableMixin, MAIN_BASE): class quotations(MainTableMixin, MAIN_BASE): __tablename__ = "quotations" + __table_args__ = {"schema": "quotation"} qt_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) user_id = Column(UUID(as_uuid=True), nullable=False, index=True) @@ -173,6 +181,7 @@ class quotations(MainTableMixin, MAIN_BASE): class sessions(MainTableMixin, MAIN_BASE): __tablename__ = "sessions" + __table_args__ = {"schema": "negotiation"} session_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) quotation_id = Column(UUID(as_uuid=True), nullable=False, index=True) # 소속 견적(quotations.qt_id) @@ -194,6 +203,7 @@ class sessions(MainTableMixin, MAIN_BASE): class chats(MainTableMixin, MAIN_BASE): __tablename__ = "chats" + __table_args__ = {"schema": "negotiation"} chat_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) session_id = Column(UUID(as_uuid=True), nullable=False, index=True) # 소속 세션(sessions.session_id) diff --git a/negodata/backend/router/v1/validator/dependencies.py b/negodata/backend/router/v1/validator/dependencies.py index 6de767e..72b70cf 100644 --- a/negodata/backend/router/v1/validator/dependencies.py +++ b/negodata/backend/router/v1/validator/dependencies.py @@ -3,7 +3,7 @@ import json from typing import Any, Union from fastapi import Depends -from fastapi.responses import ORJSONResponse +from fastapi.responses import JSONResponse from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer import bcrypt from jose import jwt, JWTError, ExpiredSignatureError @@ -109,7 +109,8 @@ def RemoveNoneValues(obj: Any) -> Any: return obj -def RemoveNoneResponse(obj) -> ORJSONResponse: +def RemoveNoneResponse(obj) -> JSONResponse: # mode="json": uuid/datetime 등 DB 네이티브 타입(asyncpg.UUID 포함)을 pydantic 단에서 - # JSON 안전한 문자열로 변환한다. (python 모드면 orjson 이 asyncpg.UUID 를 직렬화 못 함) - return ORJSONResponse(content=RemoveNoneValues(obj.model_dump(mode="json"))) + # JSON 안전한 문자열로 변환한다. content 가 이미 JSON-safe dict 이므로 표준 JSONResponse 사용 + # (ORJSONResponse 는 최신 FastAPI 에서 deprecated). + return JSONResponse(content=RemoveNoneValues(obj.model_dump(mode="json"))) diff --git a/negodata/front/Dockerfile b/negodata/front/Dockerfile index faf3f36..e0ae4c2 100644 --- a/negodata/front/Dockerfile +++ b/negodata/front/Dockerfile @@ -1,4 +1,5 @@ -FROM node:20-alpine +# node 22 (orval 은 node>=22.18 요구) + Debian slim(glibc) — alpine(musl)+esbuild 의 ETXTBSY 회피. +FROM node:22-slim WORKDIR /app diff --git a/postgres-init/01-schema.sql b/postgres-init/01-schema.sql index 3fab4a8..be285c0 100644 --- a/postgres-init/01-schema.sql +++ b/postgres-init/01-schema.sql @@ -151,7 +151,7 @@ CREATE TABLE IF NOT EXISTS partner.items ( lead_time SMALLINT NULL, -- 상품 주문 완료 후, 배송 도착까지의 시간 manufacturer VARCHAR(50) NULL, -- 제조사 made_in VARCHAR(100) NULL, -- 원산지 - quantity_unit SMALLINT NULL, -- 상품 취급 단위 (코드: 예 EA/BOX/SET, 앱 enum 매핑) + quantity_unit VARCHAR(50) NULL, -- 상품 취급 단위 라벨(자유입력): EA/BOX/SET/ROLL ... (ORM String 기준) delivery_type SMALLINT NULL, -- 배송 유형 (코드, 앱 enum 매핑) vat_yn BOOLEAN NULL, -- 부가세 포함 여부 delivery_fee_yn BOOLEAN NULL, -- 배송비 포함 여부 diff --git a/postgres-init/03-seed-negodata.sql b/postgres-init/03-seed-negodata.sql index 58014dd..fec28c2 100644 --- a/postgres-init/03-seed-negodata.sql +++ b/postgres-init/03-seed-negodata.sql @@ -13,13 +13,13 @@ WHERE NOT EXISTS ( SELECT 1 FROM company.companies WHERE company_id = '00000000-0000-0000-0000-000000000001' ); --- admin 유저. password 는 'admin1234' 의 bcrypt 해시(백엔드 GetHashedPW 와 동일 알고리즘, checkpw 로 검증됨). +-- admin 유저. password 는 'admin123' 의 bcrypt 해시(백엔드 GetHashedPW 와 동일 알고리즘, checkpw 로 검증됨). -- role 2=manager (UserRole.MANAGER; ADMIN 코드는 enum 에 없어 최상위인 MANAGER 사용). status 1=active. INSERT INTO company.users (user_id, company_id, id, password, name, email, last_accessed_at, status, role) SELECT '00000000-0000-0000-0000-000000000002', '00000000-0000-0000-0000-000000000001', 'admin', - '$2b$12$oq5qlcYQ4BNywh9iS7cF1OBP6kGBvB6SfsGgVknCiJ6lMSW5cjW.C', + '$2b$12$KY4T0kXQ2npvvt71iWZG0.JZHlMNt9angIkE/7.lBC4vta4dHgrj2', 'admin', 'admin@negosium.dev', now(), 1, 2 WHERE NOT EXISTS ( SELECT 1 FROM company.users WHERE id = 'admin' AND deleted = FALSE