cors 문제 해결 .

This commit is contained in:
hbyang 2026-06-18 16:30:03 +09:00
parent dd03c2aecd
commit b0b787a8ce
6 changed files with 25 additions and 11 deletions

View File

@ -3,7 +3,7 @@ import json
from typing import Any, Union from typing import Any, Union
from fastapi import Depends from fastapi import Depends
from fastapi.responses import ORJSONResponse from fastapi.responses import JSONResponse
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
import bcrypt import bcrypt
from jose import jwt, JWTError, ExpiredSignatureError from jose import jwt, JWTError, ExpiredSignatureError
@ -109,5 +109,7 @@ def RemoveNoneValues(obj: Any) -> Any:
return obj return obj
def RemoveNoneResponse(obj) -> ORJSONResponse: def RemoveNoneResponse(obj) -> JSONResponse:
return ORJSONResponse(content=RemoveNoneValues(obj.model_dump())) # mode="json": datetime/uuid 등을 JSON-safe 문자열로 변환(표준 JSONResponse 가 직렬화 가능).
# (ORJSONResponse 는 최신 FastAPI 에서 deprecated)
return JSONResponse(content=RemoveNoneValues(obj.model_dump(mode="json")))

View File

@ -35,6 +35,7 @@ class MainTableMixin(_DBTypeMixin):
# ERD 도메인 모델 # ERD 도메인 모델
class companies(MainTableMixin, MAIN_BASE): class companies(MainTableMixin, MAIN_BASE):
__tablename__ = "companies" __tablename__ = "companies"
__table_args__ = {"schema": "company"}
company_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) company_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
name = Column(String(100), nullable=False) name = Column(String(100), nullable=False)
@ -50,6 +51,7 @@ class companies(MainTableMixin, MAIN_BASE):
class users(MainTableMixin, MAIN_BASE): class users(MainTableMixin, MAIN_BASE):
__tablename__ = "users" __tablename__ = "users"
__table_args__ = {"schema": "company"}
user_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) user_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
company_id = Column(UUID(as_uuid=True), nullable=False, index=True) 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): class items(MainTableMixin, MAIN_BASE):
__tablename__ = "items" __tablename__ = "items"
__table_args__ = {"schema": "partner"}
item_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) item_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
company_id = Column(UUID(as_uuid=True), nullable=False, index=True) 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): class suppliers(MainTableMixin, MAIN_BASE):
__tablename__ = "suppliers" __tablename__ = "suppliers"
__table_args__ = {"schema": "partner"}
supplier_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) supplier_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
company_id = Column(UUID(as_uuid=True), nullable=False, index=True) 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): class nego_cards(MainTableMixin, MAIN_BASE):
__tablename__ = "nego_cards" __tablename__ = "nego_cards"
__table_args__ = {"schema": "card"}
nego_card_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) 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) 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): class wild_cards(MainTableMixin, MAIN_BASE):
__tablename__ = "wild_cards" __tablename__ = "wild_cards"
__table_args__ = {"schema": "card"}
wild_card_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) 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) 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): class quotation_settings(MainTableMixin, MAIN_BASE):
__tablename__ = "quotation_settings" __tablename__ = "quotation_settings"
__table_args__ = {"schema": "quotation"}
qt_setting_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) 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) # 설정 소유 유저 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): class quotations(MainTableMixin, MAIN_BASE):
__tablename__ = "quotations" __tablename__ = "quotations"
__table_args__ = {"schema": "quotation"}
qt_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) qt_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(UUID(as_uuid=True), nullable=False, index=True) 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): class sessions(MainTableMixin, MAIN_BASE):
__tablename__ = "sessions" __tablename__ = "sessions"
__table_args__ = {"schema": "negotiation"}
session_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) 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) 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): class chats(MainTableMixin, MAIN_BASE):
__tablename__ = "chats" __tablename__ = "chats"
__table_args__ = {"schema": "negotiation"}
chat_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) 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) session_id = Column(UUID(as_uuid=True), nullable=False, index=True) # 소속 세션(sessions.session_id)

View File

@ -3,7 +3,7 @@ import json
from typing import Any, Union from typing import Any, Union
from fastapi import Depends from fastapi import Depends
from fastapi.responses import ORJSONResponse from fastapi.responses import JSONResponse
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
import bcrypt import bcrypt
from jose import jwt, JWTError, ExpiredSignatureError from jose import jwt, JWTError, ExpiredSignatureError
@ -109,7 +109,8 @@ def RemoveNoneValues(obj: Any) -> Any:
return obj return obj
def RemoveNoneResponse(obj) -> ORJSONResponse: def RemoveNoneResponse(obj) -> JSONResponse:
# mode="json": uuid/datetime 등 DB 네이티브 타입(asyncpg.UUID 포함)을 pydantic 단에서 # mode="json": uuid/datetime 등 DB 네이티브 타입(asyncpg.UUID 포함)을 pydantic 단에서
# JSON 안전한 문자열로 변환한다. (python 모드면 orjson 이 asyncpg.UUID 를 직렬화 못 함) # JSON 안전한 문자열로 변환한다. content 가 이미 JSON-safe dict 이므로 표준 JSONResponse 사용
return ORJSONResponse(content=RemoveNoneValues(obj.model_dump(mode="json"))) # (ORJSONResponse 는 최신 FastAPI 에서 deprecated).
return JSONResponse(content=RemoveNoneValues(obj.model_dump(mode="json")))

View File

@ -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 WORKDIR /app

View File

@ -151,7 +151,7 @@ CREATE TABLE IF NOT EXISTS partner.items (
lead_time SMALLINT NULL, -- 상품 주문 완료 후, 배송 도착까지의 시간 lead_time SMALLINT NULL, -- 상품 주문 완료 후, 배송 도착까지의 시간
manufacturer VARCHAR(50) NULL, -- 제조사 manufacturer VARCHAR(50) NULL, -- 제조사
made_in VARCHAR(100) 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 매핑) delivery_type SMALLINT NULL, -- 배송 유형 (코드, 앱 enum 매핑)
vat_yn BOOLEAN NULL, -- 부가세 포함 여부 vat_yn BOOLEAN NULL, -- 부가세 포함 여부
delivery_fee_yn BOOLEAN NULL, -- 배송비 포함 여부 delivery_fee_yn BOOLEAN NULL, -- 배송비 포함 여부

View File

@ -13,13 +13,13 @@ WHERE NOT EXISTS (
SELECT 1 FROM company.companies WHERE company_id = '00000000-0000-0000-0000-000000000001' 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. -- 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) INSERT INTO company.users (user_id, company_id, id, password, name, email, last_accessed_at, status, role)
SELECT '00000000-0000-0000-0000-000000000002', SELECT '00000000-0000-0000-0000-000000000002',
'00000000-0000-0000-0000-000000000001', '00000000-0000-0000-0000-000000000001',
'admin', 'admin',
'$2b$12$oq5qlcYQ4BNywh9iS7cF1OBP6kGBvB6SfsGgVknCiJ6lMSW5cjW.C', '$2b$12$KY4T0kXQ2npvvt71iWZG0.JZHlMNt9angIkE/7.lBC4vta4dHgrj2',
'admin', 'admin@negosium.dev', now(), 1, 2 'admin', 'admin@negosium.dev', now(), 1, 2
WHERE NOT EXISTS ( WHERE NOT EXISTS (
SELECT 1 FROM company.users WHERE id = 'admin' AND deleted = FALSE SELECT 1 FROM company.users WHERE id = 'admin' AND deleted = FALSE