diff --git a/backend/src/server.js b/backend/src/server.js index ee2d06f..2703238 100644 --- a/backend/src/server.js +++ b/backend/src/server.js @@ -445,8 +445,8 @@ app.use(`/api/${apiVersion}/ws`, wsRoutes); let bullBoardRouter = null; const bullBoardSessions = new Map(); // Store authenticated sessions -// Mount Bull Board at /admin instead of /api/v1/admin to avoid path conflicts -app.use(`/admin/queues`, (_req, res, next) => { +// Mount Bull Board at /bullboard for cleaner URL +app.use(`/bullboard`, (_req, res, next) => { // Relax COOP/COEP for Bull Board in non-production to avoid browser warnings if (process.env.NODE_ENV !== "production") { res.setHeader("Cross-Origin-Opener-Policy", "same-origin-allow-popups"); @@ -457,7 +457,7 @@ app.use(`/admin/queues`, (_req, res, next) => { }); // Authentication middleware for Bull Board -app.use(`/admin/queues`, async (req, res, next) => { +app.use(`/bullboard`, async (req, res, next) => { // Skip authentication for static assets only if (req.path.includes("/static/") || req.path.includes("/favicon")) { return next(); @@ -534,7 +534,7 @@ app.use(`/admin/queues`, async (req, res, next) => { }); }); -app.use(`/admin/queues`, (req, res, next) => { +app.use(`/bullboard`, (req, res, next) => { if (bullBoardRouter) { return bullBoardRouter(req, res, next); } @@ -542,7 +542,7 @@ app.use(`/admin/queues`, (req, res, next) => { }); // Error handler specifically for Bull Board routes -app.use("/admin/queues", (err, req, res, _next) => { +app.use("/bullboard", (err, req, res, _next) => { console.error("Bull Board error on", req.method, req.url); console.error("Error details:", err.message); console.error("Stack:", err.stack); @@ -874,7 +874,7 @@ async function startServer() { // Set up Bull Board for queue monitoring const serverAdapter = new ExpressAdapter(); // Set basePath to match where we mount the router - serverAdapter.setBasePath("/admin/queues"); + serverAdapter.setBasePath("/bullboard"); const { QUEUE_NAMES } = require("./services/automation"); const bullAdapters = Object.values(QUEUE_NAMES).map( @@ -888,7 +888,7 @@ async function startServer() { // Set the router for the Bull Board middleware (secured middleware above) bullBoardRouter = serverAdapter.getRouter(); - console.log("✅ Bull Board mounted at /admin/queues (secured)"); + console.log("✅ Bull Board mounted at /bullboard (secured)"); // Initialize WS layer with the underlying HTTP server initAgentWs(server, prisma); diff --git a/docker/nginx.conf.template b/docker/nginx.conf.template index 8dc395a..5a9d954 100644 --- a/docker/nginx.conf.template +++ b/docker/nginx.conf.template @@ -29,6 +29,29 @@ server { try_files $uri $uri/ /index.html; } + # Bull Board proxy + location /bullboard { + proxy_pass http://${BACKEND_HOST}:${BACKEND_PORT}; + 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 $scheme; + proxy_set_header X-Forwarded-Host $host; + + # Preserve original client IP through proxy chain + proxy_set_header X-Original-Forwarded-For $http_x_forwarded_for; + + # CORS headers for Bull Board + add_header Access-Control-Allow-Origin * always; + add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always; + add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization" always; + + # Handle preflight requests + if ($request_method = 'OPTIONS') { + return 204; + } + } + # API proxy location /api/ { proxy_pass http://${BACKEND_HOST}:${BACKEND_PORT}; @@ -52,63 +75,6 @@ server { } } - # SSE (Server-Sent Events) specific configuration - location /api/v1/ws/status/ { - proxy_pass http://${BACKEND_HOST}:${BACKEND_PORT}; - 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 $scheme; - proxy_set_header X-Forwarded-Host $host; - - # Critical SSE settings - proxy_buffering off; - proxy_cache off; - proxy_set_header Connection ''; - proxy_http_version 1.1; - chunked_transfer_encoding off; - - # Timeout settings for long-lived connections - proxy_read_timeout 24h; - proxy_send_timeout 24h; - proxy_connect_timeout 60s; - - # Disable nginx buffering for real-time streaming - proxy_request_buffering off; - proxy_max_temp_file_size 0; - - # CORS headers for SSE - commented out to let backend handle CORS - # add_header Access-Control-Allow-Origin * always; - # add_header Access-Control-Allow-Methods "GET, OPTIONS" always; - # add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization" always; - - # Handle preflight requests - if ($request_method = 'OPTIONS') { - return 204; - } - } - - # WebSocket upgrade handling - location /api/v1/agents/ws { - proxy_pass http://${BACKEND_HOST}:${BACKEND_PORT}; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - 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 $scheme; - proxy_set_header X-Forwarded-Host $host; - - # WebSocket timeout settings - proxy_read_timeout 24h; - proxy_send_timeout 24h; - proxy_connect_timeout 60s; - - # Disable buffering for WebSocket - proxy_buffering off; - proxy_cache off; - } # Static assets caching location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { diff --git a/frontend/src/pages/Automation.jsx b/frontend/src/pages/Automation.jsx index f69fb71..b03bd53 100644 --- a/frontend/src/pages/Automation.jsx +++ b/frontend/src/pages/Automation.jsx @@ -227,7 +227,7 @@ const Automation = () => { // Use the proxied URL through the frontend (port 3000) // This avoids CORS issues as everything goes through the same origin - const url = `/admin/queues?token=${encodeURIComponent(token)}`; + const url = `/bullboard?token=${encodeURIComponent(token)}`; window.open(url, "_blank", "width=1200,height=800"); };