fix: banner translations and deployment improvements

- Add translateCarName import from i18n.ts for proper multilingual support
- Change default API language from 'ko' to 'en' for hero banners
- Add checkbox column for Local Cars banner registration
- Update Dockerfile with Playwright dependencies
- Add PostgreSQL migration script
- Add banner translation fix script

🤖 Generated with [Claude Code](https://claude.ai/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
AutonetSellCar Deploy
2025-12-31 10:41:42 +09:00
parent 898ab3a0eb
commit e661d91c72
10 changed files with 1145 additions and 490 deletions

View File

@@ -40,7 +40,7 @@ def get_localized_field(obj, field: str, lang: str) -> Optional[str]:
@router.get("/", response_model=List[HeroBannerLocalizedResponse])
def get_hero_banners(
lang: str = Query("ko", regex="^(ko|en|mn|ru)$"),
lang: str = Query("en", regex="^(ko|en|mn|ru)$"),
db: Session = Depends(get_db)
):
"""활성 히어로 배너 목록 조회 (Public)"""

View File

@@ -64,7 +64,10 @@ class Settings(BaseSettings):
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
db_path = os.path.join(base_dir, "autonet.db")
return f"sqlite:///{db_path}"
return f"postgresql://{self.DB_USER}:{self.DB_PASSWORD}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}"
# URL-encode password for special characters like @ # etc
from urllib.parse import quote_plus
encoded_password = quote_plus(self.DB_PASSWORD)
return f"postgresql://{self.DB_USER}:{encoded_password}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}"
@property
def REDIS_URL(self) -> str:

View File

@@ -1,6 +1,8 @@
from fastapi import FastAPI
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import RedirectResponse
from contextlib import asynccontextmanager
import os
import asyncio
@@ -15,6 +17,42 @@ from datetime import datetime, timedelta
app_settings = get_settings()
class TrailingSlashMiddleware(BaseHTTPMiddleware):
"""
Middleware to normalize trailing slashes on API paths.
Uses redirect to strip trailing slashes from non-root routes.
Routes defined with "/" (like /hero-banners/) keep trailing slash.
Routes defined without "/" (like /cars) get redirected.
"""
# Routes that are defined WITH trailing slash (router.get("/"))
TRAILING_SLASH_ROUTES = {
"/api/hero-banners/",
"/api/settings/",
"/api/notifications/",
"/api/vehicle-requests/",
}
async def dispatch(self, request: Request, call_next):
path = request.url.path
# Skip if it's a known trailing-slash route
if path in self.TRAILING_SLASH_ROUTES:
return await call_next(request)
# Redirect trailing slash from other /api/* paths
if path.startswith("/api/") and path.endswith("/") and len(path) > 5:
new_path = path.rstrip("/")
if request.url.query:
new_url = f"{new_path}?{request.url.query}"
else:
new_url = new_path
return RedirectResponse(url=new_url, status_code=307)
return await call_next(request)
# Create tables
Base.metadata.create_all(bind=engine)
@@ -118,6 +156,9 @@ app = FastAPI(
lifespan=lifespan
)
# Trailing slash middleware (must be added before CORS)
app.add_middleware(TrailingSlashMiddleware)
# CORS - credentials=True requires explicit origins (not "*")
app.add_middleware(
CORSMiddleware,
@@ -126,6 +167,8 @@ app.add_middleware(
"http://127.0.0.1:3000",
"http://localhost:8000",
"http://192.168.0.202:3000", # Local network
"https://autonetsellcar.com", # Production
"http://autonetsellcar.com", # Production (HTTP redirect)
],
allow_credentials=True,
allow_methods=["*"],