Support HTTPS deployment under /generadorexamenesllm behind reverse proxy.

This updates frontend base-path routing, same-origin API proxying, and deployment defaults/docs so the app works correctly through the Sinbad2 Apache ProxyPass setup.
This commit is contained in:
Mireya Cueto Garrido
2026-06-02 13:10:52 +02:00
parent d7f9ae8841
commit 944482b96c
10 changed files with 76 additions and 18 deletions
+7 -2
View File
@@ -1,5 +1,10 @@
# URL base del backend (accesible desde el navegador)
VITE_API_URL=http://sinbad2.ujaen.es:8074
# Base pública de la app cuando va tras proxy (debe terminar en /)
# Ejemplo producción UJA: /generadorexamenesllm/
VITE_APP_BASE_PATH=/generadorexamenesllm/
# URL base del backend (accesible desde el navegador).
# Si se deja vacía, usa la misma base de la app (recomendado tras proxy HTTPS).
VITE_API_URL=
# (Opcional) Client ID de Google para "Iniciar sesión con Google".
# Debe coincidir con GOOGLE_CLIENT_ID del backend.
+3 -1
View File
@@ -2,8 +2,10 @@
FROM node:20-alpine AS build
WORKDIR /app
ARG VITE_API_URL=http://sinbad2.ujaen.es:8074
ARG VITE_APP_BASE_PATH=/generadorexamenesllm/
ARG VITE_API_URL=
ARG VITE_GOOGLE_CLIENT_ID=
ENV VITE_APP_BASE_PATH=$VITE_APP_BASE_PATH
ENV VITE_API_URL=$VITE_API_URL
ENV VITE_GOOGLE_CLIENT_ID=$VITE_GOOGLE_CLIENT_ID
+21 -2
View File
@@ -38,7 +38,8 @@ npm run dev # http://sinbad2.ujaen.es:8075
| Variable | Descripción |
| ----------------------- | -------------------------------------------------------- |
| `VITE_API_URL` | URL base del backend (por defecto `http://sinbad2.ujaen.es:8074`). |
| `VITE_APP_BASE_PATH` | Base pública de la SPA (debe terminar en `/`). En producción UJA: `/generadorexamenesllm/`. |
| `VITE_API_URL` | URL base de la API. Si se deja vacía, usa la misma base pública de la app. |
| `VITE_GOOGLE_CLIENT_ID` | (Opcional) Client ID de Google. Si está vacío, se oculta el botón de Google. |
> Las variables `VITE_*` se incrustan en el build, por lo que apuntan al backend
@@ -60,9 +61,27 @@ El `docker-compose.yml` de la raíz construye el frontend con un build multi-sta
docker compose up --build
```
Las variables `VITE_API_URL` y `VITE_GOOGLE_CLIENT_ID` pueden pasarse como
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)
Para ejecutarlo detrás de Apache en `https://sinbad2.ujaen.es/deckofcars` con:
```apache
ProxyPass /generadorexamenesllm http://host.docker.internal:8075/
ProxyPassReverse /generadorexamenesllm http://host.docker.internal:8075/
```
usa estos valores de build:
```env
VITE_APP_BASE_PATH=/generadorexamenesllm/
VITE_API_URL=
```
Con esto, el frontend sirve assets/rutas bajo `/generadorexamenesllm` y consume la API por HTTPS en la misma base (sin mixed content).
## Manejo de errores
Todas las respuestas de error del backend siguen el formato
+25
View File
@@ -4,6 +4,31 @@ server {
root /usr/share/nginx/html;
index index.html;
# Backend API dentro de la misma base HTTPS (evita mixed content).
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;
}
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;
}
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;
}
# SPA: cualquier ruta desconocida sirve index.html (React Router).
location / {
try_files $uri $uri/ /index.html;
+1 -1
View File
@@ -1,7 +1,7 @@
import axios from "axios";
export const API_URL =
import.meta.env.VITE_API_URL?.replace(/\/$/, "") || "http://sinbad2.ujaen.es:8074";
import.meta.env.VITE_API_URL?.replace(/\/$/, "") || import.meta.env.BASE_URL.replace(/\/$/, "");
const TOKEN_KEY = "genex_token";
+1 -1
View File
@@ -8,7 +8,7 @@ import "./index.css";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<BrowserRouter>
<BrowserRouter basename={import.meta.env.BASE_URL}>
<ToastProvider>
<AuthProvider>
<App />
+13 -7
View File
@@ -1,10 +1,16 @@
import { defineConfig } from "vite";
import { defineConfig, loadEnv } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
server: {
host: true,
port: 8075,
},
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), "");
const base = env.VITE_APP_BASE_PATH || "/";
return {
base,
plugins: [react()],
server: {
host: true,
port: 8075,
},
};
});