diff --git a/docker-compose.yml b/docker-compose.yml index 30881bb..ec6f0cf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,6 +20,7 @@ services: - html:/usr/share/nginx/html - ./nginx/proxy.conf:/etc/nginx/conf.d/proxy.conf:ro - ./nginx/studio.conf:/etc/nginx/vhost.d/studio.norteamericano.com:ro + - ./nginx/learning.conf:/etc/nginx/vhost.d/learning.norteamericano.com:ro restart: always networks: - openccb-network diff --git a/nginx/learning.conf b/nginx/learning.conf new file mode 100644 index 0000000..391991e --- /dev/null +++ b/nginx/learning.conf @@ -0,0 +1,13 @@ +# Custom nginx configuration for OpenCCB Learning +# Keep the learning frontend on port 3003 and expose LMS API via same-origin /lms-api. + +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; +} diff --git a/nginx/studio.conf b/nginx/studio.conf index d5f4431..36d02e9 100644 --- a/nginx/studio.conf +++ b/nginx/studio.conf @@ -15,6 +15,17 @@ location /cms-api/ { proxy_http_version 1.1; } +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). @@ -222,8 +233,28 @@ location /grading/ { 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:3001; + 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; @@ -252,8 +283,16 @@ location = /admin/ { proxy_http_version 1.1; } +location = /admin/organizations { + return 302 /admin; +} + +location = /admin/organizations/ { + return 302 /admin; +} + location /admin/ { - proxy_pass http://openccb-studio:3001; + 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; diff --git a/web/experience/src/lib/api.ts b/web/experience/src/lib/api.ts index b31070e..e25a770 100644 --- a/web/experience/src/lib/api.ts +++ b/web/experience/src/lib/api.ts @@ -10,7 +10,12 @@ const getApiBaseUrl = (defaultPort: string, envVar?: string) => { return `http://localhost:${defaultPort}`; }; -export const getLmsApiUrl = () => getApiBaseUrl("3002", process.env.NEXT_PUBLIC_LMS_API_URL); +export const getLmsApiUrl = () => { + if (typeof window !== 'undefined' && window.location.hostname === 'learning.norteamericano.com') { + return `${window.location.protocol}//learning.norteamericano.com/lms-api`; + } + return getApiBaseUrl("3002", process.env.NEXT_PUBLIC_LMS_API_URL); +}; export const getCmsApiUrl = () => getApiBaseUrl("3001", process.env.NEXT_PUBLIC_CMS_API_URL); export const getImageUrl = (path?: string) => { diff --git a/web/studio/next.config.mjs b/web/studio/next.config.mjs index e7d61e6..e8bb0a0 100644 --- a/web/studio/next.config.mjs +++ b/web/studio/next.config.mjs @@ -25,7 +25,12 @@ const nextConfig = { ], }, async rewrites() { - return [ + // Using `fallback` ensures Next.js pages (including dynamic routes like + // /courses/[id]) are matched BEFORE these proxy rewrites. Without this, + // `afterFiles` rewrites (the default for arrays) would intercept RSC + // prefetch requests to dynamic pages and proxy them to Rust instead. + return { + fallback: [ { source: '/assets/:path*', destination: 'http://localhost:3001/assets/:path*', @@ -143,7 +148,8 @@ const nextConfig = { source: '/health', destination: 'http://localhost:3001/health', }, - ]; + ], + }; }, }; diff --git a/web/studio/src/app/admin/layout.tsx b/web/studio/src/app/admin/layout.tsx index 0037b5b..419ba59 100644 --- a/web/studio/src/app/admin/layout.tsx +++ b/web/studio/src/app/admin/layout.tsx @@ -20,7 +20,7 @@ export default function AdminLayout({ children }: { children: React.ReactNode }) const menuItems = [ { icon: LayoutDashboard, label: "Dashboard", href: "/admin" }, - { icon: Building2, label: "Organizations", href: "/admin/organizations" }, + { icon: Building2, label: "Organizations", href: "/admin" }, { icon: Users, label: "Users", href: "/admin/users" }, { icon: Mic, label: "Audio Evaluations", href: "/admin/audio-evaluations" }, { icon: ClipboardList, label: "Audit Logs", href: "/admin/audit" }, diff --git a/web/studio/src/components/AuthHeader.tsx b/web/studio/src/components/AuthHeader.tsx index 0255b72..23a3b8e 100644 --- a/web/studio/src/components/AuthHeader.tsx +++ b/web/studio/src/components/AuthHeader.tsx @@ -10,7 +10,7 @@ export default function AuthHeader() {