From 77e707167f890acd6cb4e5d5fa07f27a33373e8f Mon Sep 17 00:00:00 2001 From: AutonetSellCar Deploy Date: Sat, 3 Jan 2026 18:32:00 +0900 Subject: [PATCH] Fix Mixed Content issue on staging (HTTPS/HTTP mismatch) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add Next.js API rewrites to proxy /api and /uploads requests - Update docker-compose.staging.yml to use relative API paths - Set NEXT_PUBLIC_API_URL to empty for staging (use rewrites) - Add BACKEND_URL env var for Next.js server-side proxying - Update all files to use relative paths when API_URL is empty This fixes the issue where the staging site (HTTPS) was trying to load resources from HTTP backend, causing Mixed Content errors. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- docker-compose.staging.yml | 3 ++- frontend/next.config.js | 26 +++++++++++++++++++- frontend/src/app/admin/cars/page.tsx | 2 +- frontend/src/app/admin/dealers/page.tsx | 2 +- frontend/src/app/admin/hero-banners/page.tsx | 2 +- frontend/src/app/admin/settings/page.tsx | 2 +- frontend/src/app/cars/[id]/page.tsx | 2 +- frontend/src/app/cc/page.tsx | 2 +- frontend/src/app/cc/success/page.tsx | 2 +- frontend/src/app/cost/page.tsx | 2 +- frontend/src/app/dealer/apply/page.tsx | 2 +- frontend/src/app/dealer/my-card/page.tsx | 2 +- frontend/src/app/exchange-rate/page.tsx | 2 +- frontend/src/app/my-shares/page.tsx | 2 +- frontend/src/app/profile/page.tsx | 2 +- frontend/src/app/share/[code]/page.tsx | 2 +- frontend/src/components/FilmStripSlider.tsx | 2 +- frontend/src/lib/api.ts | 8 +++--- frontend/src/lib/useVisitorTracking.ts | 2 +- 19 files changed, 48 insertions(+), 21 deletions(-) diff --git a/docker-compose.staging.yml b/docker-compose.staging.yml index ac41e38..900730d 100644 --- a/docker-compose.staging.yml +++ b/docker-compose.staging.yml @@ -4,12 +4,13 @@ services: context: ./frontend dockerfile: Dockerfile args: - - NEXT_PUBLIC_API_URL=http://192.168.0.202:8001 + - NEXT_PUBLIC_API_URL= container_name: autonet-frontend-staging ports: - "3001:3000" environment: - NODE_ENV=staging + - BACKEND_URL=http://backend-staging:8000 depends_on: - backend-staging restart: unless-stopped diff --git a/frontend/next.config.js b/frontend/next.config.js index d18b973..50be07c 100644 --- a/frontend/next.config.js +++ b/frontend/next.config.js @@ -28,10 +28,34 @@ const nextConfig = { hostname: '192.168.0.202', pathname: '/uploads/**', }, + { + protocol: 'https', + hostname: 'staging.autonetsellcar.com', + pathname: '/uploads/**', + }, + { + protocol: 'https', + hostname: 'autonetsellcar.com', + pathname: '/uploads/**', + }, ], }, env: { - NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL || 'http://192.168.0.202:8000', + // Don't override empty string - it means use relative paths with rewrites + NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL, + }, + async rewrites() { + const backendUrl = process.env.BACKEND_URL || 'http://localhost:8000'; + return [ + { + source: '/api/:path*', + destination: `${backendUrl}/api/:path*`, + }, + { + source: '/uploads/:path*', + destination: `${backendUrl}/uploads/:path*`, + }, + ]; }, } diff --git a/frontend/src/app/admin/cars/page.tsx b/frontend/src/app/admin/cars/page.tsx index 5b015cc..be839ac 100644 --- a/frontend/src/app/admin/cars/page.tsx +++ b/frontend/src/app/admin/cars/page.tsx @@ -108,7 +108,7 @@ const FUEL_TYPES = [ { value: 'LPG', label: 'LPG' }, ]; -const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; +const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || ''; // 이미지 URL 변환 (로컬 경로는 백엔드 URL 추가) const getImageUrl = (url: string | undefined): string => { diff --git a/frontend/src/app/admin/dealers/page.tsx b/frontend/src/app/admin/dealers/page.tsx index 2c4f16e..d625364 100644 --- a/frontend/src/app/admin/dealers/page.tsx +++ b/frontend/src/app/admin/dealers/page.tsx @@ -5,7 +5,7 @@ import { useRouter } from 'next/navigation'; import { useAuthStore } from '@/lib/store'; import { useTranslation } from '@/lib/i18n'; -const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; +const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || ''; interface DealerApplication { id: number; diff --git a/frontend/src/app/admin/hero-banners/page.tsx b/frontend/src/app/admin/hero-banners/page.tsx index 391df25..a2077af 100644 --- a/frontend/src/app/admin/hero-banners/page.tsx +++ b/frontend/src/app/admin/hero-banners/page.tsx @@ -33,7 +33,7 @@ const defaultFormData: BannerFormData = { car_id: null, }; -const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; +const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || ''; // 이미지 URL 변환 (로컬 경로는 백엔드 URL 추가) const getImageUrl = (url: string | undefined): string => { diff --git a/frontend/src/app/admin/settings/page.tsx b/frontend/src/app/admin/settings/page.tsx index a24c9b8..431bf74 100644 --- a/frontend/src/app/admin/settings/page.tsx +++ b/frontend/src/app/admin/settings/page.tsx @@ -42,7 +42,7 @@ interface ExchangeRateWeights { cny: number; } -const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; +const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || ''; export default function SettingsPage() { const [settings, setSettings] = useState(null); diff --git a/frontend/src/app/cars/[id]/page.tsx b/frontend/src/app/cars/[id]/page.tsx index 0ec7ab8..6b3f58e 100644 --- a/frontend/src/app/cars/[id]/page.tsx +++ b/frontend/src/app/cars/[id]/page.tsx @@ -9,7 +9,7 @@ import { useTranslation } from '@/lib/i18n'; import { useTranslate } from '@/lib/useTranslate'; // 이미지 URL 변환 (로컬 경로는 백엔드 URL 추가) -const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; +const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || ''; const getImageUrl = (url: string | undefined): string => { if (!url) return ''; diff --git a/frontend/src/app/cc/page.tsx b/frontend/src/app/cc/page.tsx index 044fb9f..3b14316 100644 --- a/frontend/src/app/cc/page.tsx +++ b/frontend/src/app/cc/page.tsx @@ -6,7 +6,7 @@ import { useAuthStore } from '@/lib/store'; import { useTranslation } from '@/lib/i18n'; import SidebarLayout from '@/components/SidebarLayout'; -const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; +const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || ''; interface CCPackage { id: number; diff --git a/frontend/src/app/cc/success/page.tsx b/frontend/src/app/cc/success/page.tsx index ed04136..34f75aa 100644 --- a/frontend/src/app/cc/success/page.tsx +++ b/frontend/src/app/cc/success/page.tsx @@ -6,7 +6,7 @@ import Link from 'next/link'; import { useAuthStore } from '@/lib/store'; import { useTranslation } from '@/lib/i18n'; -const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; +const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || ''; interface CheckoutResult { status: string; diff --git a/frontend/src/app/cost/page.tsx b/frontend/src/app/cost/page.tsx index 2457aee..ce901e2 100644 --- a/frontend/src/app/cost/page.tsx +++ b/frontend/src/app/cost/page.tsx @@ -19,7 +19,7 @@ const CUSTOMS_FEE_USD = 200; // $200 const SMALL_CAR_RATIO = 2.75; // 27.5% of total const COMPACT_CAR_RATIO = 2.25; // 22.5% of total -const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; +const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || ''; // Container matching simulation interface ContainerSlot { diff --git a/frontend/src/app/dealer/apply/page.tsx b/frontend/src/app/dealer/apply/page.tsx index 29bd78b..ee38ab5 100644 --- a/frontend/src/app/dealer/apply/page.tsx +++ b/frontend/src/app/dealer/apply/page.tsx @@ -6,7 +6,7 @@ import Link from 'next/link'; import { useAuthStore } from '@/lib/store'; import { useTranslation } from '@/lib/i18n'; -const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; +const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || ''; interface DealerApplication { id: number; diff --git a/frontend/src/app/dealer/my-card/page.tsx b/frontend/src/app/dealer/my-card/page.tsx index b51a643..a5eb151 100644 --- a/frontend/src/app/dealer/my-card/page.tsx +++ b/frontend/src/app/dealer/my-card/page.tsx @@ -6,7 +6,7 @@ import Link from 'next/link'; import { useAuthStore } from '@/lib/store'; import { useTranslation } from '@/lib/i18n'; -const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; +const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || ''; interface DealerInfo { id: number; diff --git a/frontend/src/app/exchange-rate/page.tsx b/frontend/src/app/exchange-rate/page.tsx index 4914d5a..20603f8 100644 --- a/frontend/src/app/exchange-rate/page.tsx +++ b/frontend/src/app/exchange-rate/page.tsx @@ -3,7 +3,7 @@ import { useState, useEffect } from 'react'; import { useTranslation } from '@/lib/i18n'; -const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; +const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || ''; interface ExchangeRateData { currency_code: string; diff --git a/frontend/src/app/my-shares/page.tsx b/frontend/src/app/my-shares/page.tsx index 4d346be..2e8db7b 100644 --- a/frontend/src/app/my-shares/page.tsx +++ b/frontend/src/app/my-shares/page.tsx @@ -6,7 +6,7 @@ import { useAuthStore } from '@/lib/store'; import { useTranslation } from '@/lib/i18n'; import SidebarLayout from '@/components/SidebarLayout'; -const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; +const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || ''; interface VehicleShare { id: number; diff --git a/frontend/src/app/profile/page.tsx b/frontend/src/app/profile/page.tsx index f3d2561..ab6cb45 100644 --- a/frontend/src/app/profile/page.tsx +++ b/frontend/src/app/profile/page.tsx @@ -6,7 +6,7 @@ import { useAuthStore } from '@/lib/store'; import { useTranslation } from '@/lib/i18n'; import { authApi } from '@/lib/api'; -const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; +const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || ''; export default function ProfilePage() { const { t, language } = useTranslation(); diff --git a/frontend/src/app/share/[code]/page.tsx b/frontend/src/app/share/[code]/page.tsx index f98c639..d9deec0 100644 --- a/frontend/src/app/share/[code]/page.tsx +++ b/frontend/src/app/share/[code]/page.tsx @@ -6,7 +6,7 @@ import Link from 'next/link'; import { useAuthStore } from '@/lib/store'; import { useTranslation, translateCarName } from '@/lib/i18n'; -const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; +const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || ''; interface SharedVehicleData { share: { diff --git a/frontend/src/components/FilmStripSlider.tsx b/frontend/src/components/FilmStripSlider.tsx index 26adbe4..1d6632f 100644 --- a/frontend/src/components/FilmStripSlider.tsx +++ b/frontend/src/components/FilmStripSlider.tsx @@ -362,7 +362,7 @@ interface BannerCardProps { height: number; } -const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; +const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || ''; // 이미지 URL 변환 (로컬 경로는 백엔드 URL 추가) const getImageUrl = (url: string): string => { diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 6d6137a..a513168 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -1,10 +1,12 @@ import axios from 'axios'; import { Car, CarListResponse, CarMaker, CarModel, User, HeroBanner, HeroBannerSettings, CarView } from '@/types'; -const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; +// When NEXT_PUBLIC_API_URL is empty, use relative path (for HTTPS proxy setup) +const API_URL = process.env.NEXT_PUBLIC_API_URL; +const BASE_URL = API_URL ? `${API_URL}/api` : '/api'; const api = axios.create({ - baseURL: `${API_URL}/api`, + baseURL: BASE_URL, headers: { 'Content-Type': 'application/json', }, @@ -931,7 +933,7 @@ export const ccApi = { // Get PDF URL for iframe/embed viewing getPerformanceCheckPdfUrl: (carId: number): string => { const token = typeof window !== 'undefined' ? localStorage.getItem('token') : null; - const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; + const baseUrl = process.env.NEXT_PUBLIC_API_URL || ''; return `${baseUrl}/api/carmodoo/car/${carId}/performance-check/pdf`; }, }; diff --git a/frontend/src/lib/useVisitorTracking.ts b/frontend/src/lib/useVisitorTracking.ts index 5304e68..2a32f75 100644 --- a/frontend/src/lib/useVisitorTracking.ts +++ b/frontend/src/lib/useVisitorTracking.ts @@ -3,7 +3,7 @@ import { useEffect, useRef } from 'react'; import { usePathname } from 'next/navigation'; -const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; +const API_URL = process.env.NEXT_PUBLIC_API_URL || ''; // Generate a simple session ID const generateSessionId = (): string => {