# Custom nginx configuration for OpenCCB Studio # This overrides the default location block to route API requests correctly # Allow large ZIP uploads (RAG bulk import can exceed 2GB). client_max_body_size 4096m; client_body_timeout 1800s; # API routes that need to go to port 3001 # Prefer the explicit `/cms-api/*` prefix for frontend fetches. This avoids collisions # with Next.js pages like `/courses` and `/admin` that share the same host. location /cms-api/ { rewrite ^/cms-api/(.*)$ /$1 break; proxy_pass http://openccb-studio:3001; proxy_request_buffering off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; proxy_connect_timeout 120s; proxy_send_timeout 3600s; proxy_read_timeout 3600s; send_timeout 3600s; } location /lms-api/ { rewrite ^/lms-api/(.*)$ /$1 break; proxy_pass http://openccb-experience:3002; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } # Auth pages like GET /auth/login and GET /auth/register must be served by Next.js (3000), # while the auth API endpoints (POST /auth/login, POST /auth/register, /auth/me, /auth/sso/*) # must continue to go to the CMS backend (3001). location @studio_frontend_auth { proxy_pass http://openccb-studio:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location = /auth/login { error_page 418 = @studio_frontend_auth; if ($request_method != POST) { return 418; } proxy_pass http://openccb-studio:3001; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location = /auth/register { error_page 418 = @studio_frontend_auth; if ($request_method != POST) { return 418; } proxy_pass http://openccb-studio:3001; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location = /auth/callback { proxy_pass http://openccb-studio:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location = /auth/me { proxy_pass http://openccb-studio:3001; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location /auth/sso/ { proxy_pass http://openccb-studio:3001; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location /auth/ { proxy_pass http://openccb-studio:3001; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location = /organization { proxy_pass http://openccb-studio:3001; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location /organization/ { proxy_pass http://openccb-studio:3001; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location = /branding { proxy_pass http://openccb-studio:3001; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } # `GET /courses` is ambiguous: the dashboard API uses it for JSON, but the Studio frontend # also uses `/courses` as a page route and for RSC/prefetch requests. # Route page/navigation-style requests to Next.js (3000), and keep API fetches on the CMS backend (3001). location = /courses { error_page 418 = @studio_frontend_auth; if ($http_accept ~* "text/html|text/x-component") { return 418; } if ($http_rsc != "") { return 418; } if ($http_next_router_state_tree != "") { return 418; } proxy_pass http://openccb-studio:3001; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location = /courses/ { proxy_pass http://openccb-studio:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location /courses/ { proxy_pass http://openccb-studio:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location /modules/ { proxy_pass http://openccb-studio:3001; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location /lessons/ { proxy_pass http://openccb-studio:3001; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location /assets/ { proxy_pass http://openccb-studio:3001; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location /users/ { proxy_pass http://openccb-studio:3001; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location /grading/ { proxy_pass http://openccb-studio:3001; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location = /question-bank { proxy_pass http://openccb-studio:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location = /question-bank/ { proxy_pass http://openccb-studio:3000/question-bank; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location /question-bank/ { proxy_pass http://openccb-studio:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location = /admin { proxy_pass http://openccb-studio:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location = /admin/ { proxy_pass http://openccb-studio:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location = /admin/organizations { return 302 /admin; } location = /admin/organizations/ { return 302 /admin; } location /admin/ { proxy_pass http://openccb-studio:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; } location /api/ { proxy_pass http://openccb-studio:3001; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header Connection ""; proxy_http_version 1.1; }