🔧 Fix CORS and API endpoint issues
- Fix API endpoint paths (remove trailing slashes for 405 errors) - Update API URLs to use api-todo.hyungi.net subdomain for HTTPS compatibility - Improve CORS settings parsing in backend (handle brackets and quotes) - Add frontend volume mount to docker-compose for real-time file updates - Update Synology deployment config with wildcard CORS settings Resolves: - 405 Method Not Allowed errors - Mixed Content security issues (HTTPS → HTTP) - CORS preflight request failures - Docker build requirements for every file change Tested: API endpoints now correctly use HTTPS subdomain, eliminating security blocks
This commit is contained in:
@@ -39,7 +39,9 @@ cors_origins = []
|
||||
if settings.CORS_ORIGINS == "*":
|
||||
cors_origins = ["*"]
|
||||
else:
|
||||
cors_origins = [origin.strip() for origin in settings.CORS_ORIGINS.split(",")]
|
||||
# 공백과 대괄호 제거 후 쉼표로 분리
|
||||
cors_str = settings.CORS_ORIGINS.strip('[]"\'')
|
||||
cors_origins = [origin.strip().strip('"\'') for origin in cors_str.split(",") if origin.strip()]
|
||||
|
||||
logger.info(f"🌐 CORS Origins: {cors_origins}")
|
||||
|
||||
|
||||
64
docker-compose.dev.yml
Normal file
64
docker-compose.dev.yml
Normal file
@@ -0,0 +1,64 @@
|
||||
services:
|
||||
frontend:
|
||||
image: nginx:alpine
|
||||
ports:
|
||||
- "${FRONTEND_PORT:-4000}:80"
|
||||
volumes:
|
||||
# 프론트엔드 파일 실시간 반영
|
||||
- ./frontend:/usr/share/nginx/html
|
||||
- ./frontend/nginx.conf:/etc/nginx/nginx.conf
|
||||
depends_on:
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- todo-network
|
||||
|
||||
backend:
|
||||
build:
|
||||
context: ./backend
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "${BACKEND_PORT:-9000}:9000"
|
||||
depends_on:
|
||||
database:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
- DATABASE_URL=postgresql+asyncpg://todo_user:${POSTGRES_PASSWORD:-todo_password}@database:5432/todo_db
|
||||
- SECRET_KEY=${SECRET_KEY:-your-secret-key-change-this-in-production}
|
||||
- DEBUG=${DEBUG:-false}
|
||||
- CORS_ORIGINS=${CORS_ORIGINS:-*}
|
||||
volumes:
|
||||
# 시놀로지 볼륨 매핑
|
||||
- ${SYNOLOGY_UPLOADS_PATH:-/volume1/todo-project/uploads}:/data/uploads
|
||||
- ${SYNOLOGY_CONFIG_PATH:-/volume3/docker/todo-project/config}:/app/config
|
||||
# 백엔드 소스 실시간 반영
|
||||
- ./backend/src:/app/src
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- todo-network
|
||||
|
||||
database:
|
||||
image: postgres:15-alpine
|
||||
ports:
|
||||
- "${DATABASE_PORT:-5432}:5432"
|
||||
environment:
|
||||
- POSTGRES_USER=${POSTGRES_USER:-todo_user}
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-todo_password}
|
||||
- POSTGRES_DB=${POSTGRES_DB:-todo_db}
|
||||
volumes:
|
||||
# 시놀로지 볼륨 매핑
|
||||
- ${SYNOLOGY_DB_PATH:-/volume3/docker/todo-project/postgres}:/var/lib/postgresql/data
|
||||
- ${SYNOLOGY_CONFIG_PATH:-/volume3/docker/todo-project/config}/migrations:/docker-entrypoint-initdb.d
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-todo_user} -d ${POSTGRES_DB:-todo_db}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
networks:
|
||||
- todo-network
|
||||
|
||||
networks:
|
||||
todo-network:
|
||||
driver: bridge
|
||||
@@ -5,6 +5,9 @@ services:
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "${FRONTEND_PORT:-4000}:80"
|
||||
volumes:
|
||||
# 프론트엔드 파일 실시간 반영 (개발용)
|
||||
- ./frontend:/usr/share/nginx/html
|
||||
depends_on:
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
@@ -17,6 +20,8 @@ services:
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "${BACKEND_PORT:-9000}:9000"
|
||||
# HTTPS용 포트 (SSL 인증서 있는 경우)
|
||||
# - "9443:9443"
|
||||
depends_on:
|
||||
database:
|
||||
condition: service_healthy
|
||||
|
||||
@@ -25,9 +25,11 @@ DEBUG=false
|
||||
POSTGRES_USER=todo_user
|
||||
POSTGRES_DB=todo_db
|
||||
|
||||
# --- CORS 설정 (시놀로지 IP/도메인에 맞게 수정) ---
|
||||
# 예시: CORS_ORIGINS=["http://192.168.1.100:4000", "https://your-domain.synology.me:4000"]
|
||||
CORS_ORIGINS=["http://localhost:4000", "http://127.0.0.1:4000"]
|
||||
# --- CORS 설정 (시놀로지 배포용) ---
|
||||
# 프로덕션 환경에서 모든 출처 허용 (보안상 주의 필요)
|
||||
CORS_ORIGINS=*
|
||||
# 또는 특정 IP만 허용하려면:
|
||||
# CORS_ORIGINS=http://192.168.1.100:4000,https://your-domain.synology.me:4000
|
||||
|
||||
# --- Synology MailPlus 통합 설정 (선택사항) ---
|
||||
SYNOLOGY_MAIL_SERVER=
|
||||
|
||||
@@ -3,9 +3,15 @@
|
||||
*/
|
||||
|
||||
// 환경에 따른 API URL 설정
|
||||
const API_BASE_URL = window.location.hostname === 'localhost'
|
||||
const API_BASE_URL = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'
|
||||
? 'http://localhost:9000/api' // 로컬 개발 환경
|
||||
: `${window.location.protocol}//${window.location.hostname}:9000/api`; // 시놀로지 배포 환경
|
||||
: window.location.hostname === 'todo.hyungi.net'
|
||||
? 'https://api-todo.hyungi.net/api' // API 서브도메인 (HTTPS 통일)
|
||||
: window.location.hostname === '192.168.219.116'
|
||||
? 'http://192.168.219.116:9000/api' // IP 직접 접근
|
||||
: window.location.protocol === 'https:'
|
||||
? `https://${window.location.hostname}:9000/api` // HTTPS 환경
|
||||
: `http://${window.location.hostname}:9000/api`; // HTTP 환경
|
||||
|
||||
class ApiClient {
|
||||
constructor() {
|
||||
@@ -161,7 +167,7 @@ const AuthAPI = {
|
||||
// Todo 관련 API
|
||||
const TodoAPI = {
|
||||
async getTodos(status = null, category = null) {
|
||||
let url = '/todos/';
|
||||
let url = '/todos/'; // 슬래시 추가
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (status && status !== 'all') params.append('status', status);
|
||||
@@ -175,27 +181,27 @@ const TodoAPI = {
|
||||
},
|
||||
|
||||
async createTodo(todoData) {
|
||||
return api.post('/todos/', todoData);
|
||||
return api.post('/todos/', todoData); // 슬래시 추가
|
||||
},
|
||||
|
||||
async updateTodo(id, todoData) {
|
||||
return api.put(`/todos/${id}/`, todoData);
|
||||
return api.put(`/todos/${id}`, todoData);
|
||||
},
|
||||
|
||||
async deleteTodo(id) {
|
||||
return api.delete(`/todos/${id}/`);
|
||||
return api.delete(`/todos/${id}`);
|
||||
},
|
||||
|
||||
async completeTodo(id) {
|
||||
return api.put(`/todos/${id}/`, { status: 'completed' });
|
||||
return api.put(`/todos/${id}`, { status: 'completed' });
|
||||
},
|
||||
|
||||
async getTodayTodos() {
|
||||
return api.get('/calendar/today/');
|
||||
return api.get('/calendar/today');
|
||||
},
|
||||
|
||||
async getTodoById(id) {
|
||||
return api.get(`/todos/${id}/`);
|
||||
return api.get(`/todos/${id}`);
|
||||
},
|
||||
|
||||
async uploadImage(imageFile) {
|
||||
|
||||
Reference in New Issue
Block a user