diff --git a/backend/src/routes/autoEnrollmentRoutes.js b/backend/src/routes/autoEnrollmentRoutes.js index ba64e20..d20564f 100644 --- a/backend/src/routes/autoEnrollmentRoutes.js +++ b/backend/src/routes/autoEnrollmentRoutes.js @@ -570,22 +570,25 @@ router.post( os_version: "unknown", api_id: api_id, api_key: api_key, - host_group_id: req.auto_enrollment_token.default_host_group_id, status: "pending", notes: `Auto-enrolled via ${req.auto_enrollment_token.token_name} on ${new Date().toISOString()}`, updated_at: new Date(), }, - include: { - host_groups: { - select: { - id: true, - name: true, - color: true, - }, - }, - }, }); + // Create host group membership if default host group is specified + let hostGroupMembership = null; + if (req.auto_enrollment_token.default_host_group_id) { + hostGroupMembership = await prisma.host_group_memberships.create({ + data: { + id: uuidv4(), + host_id: host.id, + host_group_id: req.auto_enrollment_token.default_host_group_id, + created_at: new Date(), + }, + }); + } + // Update token usage stats await prisma.auto_enrollment_tokens.update({ where: { id: req.auto_enrollment_token.id }, @@ -600,6 +603,19 @@ router.post( `Auto-enrolled host: ${friendly_name} (${host.id}) via token: ${req.auto_enrollment_token.token_name}`, ); + // Get host group details for response if membership was created + let hostGroup = null; + if (hostGroupMembership) { + hostGroup = await prisma.host_groups.findUnique({ + where: { id: req.auto_enrollment_token.default_host_group_id }, + select: { + id: true, + name: true, + color: true, + }, + }); + } + res.status(201).json({ message: "Host enrolled successfully", host: { @@ -607,7 +623,7 @@ router.post( friendly_name: host.friendly_name, api_id: api_id, api_key: api_key, - host_group: host.host_groups, + host_group: hostGroup, status: host.status, }, }); @@ -698,13 +714,24 @@ router.post( os_version: "unknown", api_id: api_id, api_key: api_key, - host_group_id: req.auto_enrollment_token.default_host_group_id, status: "pending", notes: `Auto-enrolled via ${req.auto_enrollment_token.token_name} on ${new Date().toISOString()}`, updated_at: new Date(), }, }); + // Create host group membership if default host group is specified + if (req.auto_enrollment_token.default_host_group_id) { + await prisma.host_group_memberships.create({ + data: { + id: uuidv4(), + host_id: host.id, + host_group_id: req.auto_enrollment_token.default_host_group_id, + created_at: new Date(), + }, + }); + } + results.success.push({ id: host.id, friendly_name: host.friendly_name, 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"); }; diff --git a/setup.sh b/setup.sh index 196e0df..f120f66 100755 --- a/setup.sh +++ b/setup.sh @@ -1169,7 +1169,47 @@ server { add_header X-XSS-Protection "1; mode=block"; } + # Bull Board proxy + location /bullboard { + proxy_pass http://127.0.0.1:$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; + + # 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 routes + # Bull Board proxy + location /bullboard { + proxy_pass http://127.0.0.1:$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; + + # 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; + } + } + location /api/ { proxy_pass http://127.0.0.1:$BACKEND_PORT; proxy_http_version 1.1; @@ -1250,7 +1290,47 @@ server { add_header X-XSS-Protection "1; mode=block"; } + # Bull Board proxy + location /bullboard { + proxy_pass http://127.0.0.1:$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; + + # 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 + # Bull Board proxy + location /bullboard { + proxy_pass http://127.0.0.1:$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; + + # 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; + } + } + location /api/ { proxy_pass http://127.0.0.1:$BACKEND_PORT; proxy_http_version 1.1; @@ -1319,7 +1399,47 @@ server { add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; } + # Bull Board proxy + location /bullboard { + proxy_pass http://127.0.0.1:$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; + + # 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 routes + # Bull Board proxy + location /bullboard { + proxy_pass http://127.0.0.1:$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; + + # 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; + } + } + location /api/ { proxy_pass http://127.0.0.1:$BACKEND_PORT; proxy_http_version 1.1;