Align Sinbad2 HTTPS deployment with orcid2sword reverse-proxy pattern.

This adds nginx dual-path routing, forwarded proxy headers, Uvicorn proxy-headers, production security settings, and deployment docs for https://sinbad2.ujaen.es/generadorexamenesllm/.
This commit is contained in:
Mireya Cueto Garrido
2026-06-03 10:12:05 +02:00
parent ca6d370585
commit 7dcc7dc0e1
13 changed files with 230 additions and 31 deletions
+1
View File
@@ -17,6 +17,7 @@ RUN npm run build
# --- Serve stage ---
FROM nginx:1.27-alpine AS serve
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY proxy_params.conf /etc/nginx/snippets/proxy_params.conf
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
+14 -5
View File
@@ -64,13 +64,22 @@ docker compose up --build
Las variables `VITE_APP_BASE_PATH`, `VITE_API_URL` y `VITE_GOOGLE_CLIENT_ID` pueden pasarse como
variables de entorno al ejecutar `docker compose`.
## Despliegue HTTPS en subruta (Sinbad2)
## Despliegue HTTPS en Sinbad2 (patrón orcid2sword)
La app **no** va bajo `/deckofcars/` (eso es otro sitio). La URL pública es:
Documentación completa: [`deploy/DESPLIEGUE_SINBAD2.md`](../deploy/DESPLIEGUE_SINBAD2.md)
`https://sinbad2.ujaen.es/generadorexamenesllm/`
Resumen:
En Apache del servidor deben existir estas líneas (el prefijo se quita al llegar al contenedor en el puerto 8075):
| Capa | Responsabilidad |
|------|-----------------|
| Apache (UJA) | Certificado SSL, `ProxyPass` a `:8075` |
| GitLab CI | `docker compose up`, build con base path |
| Nginx contenedor | SPA + proxy `/auth/` y `/exam/` al backend |
| Backend Uvicorn | `--proxy-headers`, HSTS, CORS HTTPS |
URL pública: **`https://sinbad2.ujaen.es/generadorexamenesllm/`**
Apache (fragmento):
```apache
ProxyPass /generadorexamenesllm http://host.docker.internal:8075/
@@ -84,7 +93,7 @@ VITE_APP_BASE_PATH=/generadorexamenesllm/
VITE_API_URL=
```
Si Apache no tiene ese `ProxyPass`, el navegador verá la web principal de Sinbad2 en lugar de esta app.
El navegador habla solo con HTTPS (Apache → nginx → backend); no hay mixed content ni CORS cruzado entre puertos.
## Manejo de errores
+55 -19
View File
@@ -1,46 +1,82 @@
# Nginx del contenedor frontend (HTTP interno, puerto 80 → publicado en 8075).
#
# Flujo HTTPS (igual que orcid2sword en Sinbad2):
# 1. Usuario → https://sinbad2.ujaen.es/generadorexamenesllm/
# 2. Apache termina TLS y hace ProxyPass al puerto 8075 (HTTP).
# 3. Con ProxyPass ... http://host:8075/ Apache QUITA el prefijo /generadorexamenesllm
# y el contenedor recibe /, /assets/, /auth/, etc.
# 4. Acceso directo al puerto 8075 (sin Apache) usa el prefijo /generadorexamenesllm/
# porque el build de Vite lleva VITE_APP_BASE_PATH=/generadorexamenesllm/
map $http_x_forwarded_proto $forwarded_proto {
default $http_x_forwarded_proto;
"" $scheme;
}
map $http_x_forwarded_host $forwarded_host {
default $http_x_forwarded_host;
"" $host;
}
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# Backend API dentro de la misma base HTTPS (evita mixed content).
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;
gzip_min_length 1024;
# --- API: rutas sin prefijo (Apache quita /generadorexamenesllm) ---
location /auth/ {
proxy_pass http://backend:8074/auth/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
include /etc/nginx/snippets/proxy_params.conf;
}
location /exam/ {
proxy_pass http://backend:8074/exam/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
include /etc/nginx/snippets/proxy_params.conf;
}
location = /health {
proxy_pass http://backend:8074/health;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
include /etc/nginx/snippets/proxy_params.conf;
}
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}
# SPA: cualquier ruta desconocida sirve index.html (React Router).
location / {
try_files $uri $uri/ /index.html;
}
# Cache de assets con hash.
location /assets/ {
# --- Mismo contenido/API bajo prefijo público (acceso directo :8075 o si Apache no quita prefijo) ---
location ^~ /generadorexamenesllm/auth/ {
proxy_pass http://backend:8074/auth/;
include /etc/nginx/snippets/proxy_params.conf;
}
location ^~ /generadorexamenesllm/exam/ {
proxy_pass http://backend:8074/exam/;
include /etc/nginx/snippets/proxy_params.conf;
}
location = /generadorexamenesllm/health {
proxy_pass http://backend:8074/health;
include /etc/nginx/snippets/proxy_params.conf;
}
location ^~ /generadorexamenesllm/assets/ {
alias /usr/share/nginx/html/assets/;
expires 1y;
add_header Cache-Control "public, immutable";
}
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;
gzip_min_length 1024;
location ^~ /generadorexamenesllm/ {
try_files $uri $uri/ /index.html;
}
}
+7
View File
@@ -0,0 +1,7 @@
proxy_http_version 1.1;
proxy_set_header Host $forwarded_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 $forwarded_proto;
proxy_set_header X-Forwarded-Host $forwarded_host;
proxy_redirect off;