From 678d1c225029cf2477df54d1483771cdac0a6c25 Mon Sep 17 00:00:00 2001 From: Gabriel Matte Date: Sun, 10 Nov 2024 20:42:02 -0500 Subject: [PATCH] trying to setup dynamic nginx --- .../src/pages/Student/JoinRoom/JoinRoom.tsx | 2 +- .../pages/Teacher/ManageRoom/ManageRoom.tsx | 5 +- nginx/Dockerfile | 70 ++++++++++++++++++- nginx/conf.d/default.conf | 60 ++++++++++++++++ nginx/conf.d/local.conf | 60 ++++++++++++++++ nginx/default.conf | 31 -------- nginx/njs/main.js | 43 ++++++++++++ quizRoom/Dockerfile | 2 +- server/routers/rooms.js | 11 --- 9 files changed, 236 insertions(+), 48 deletions(-) create mode 100644 nginx/conf.d/default.conf create mode 100644 nginx/conf.d/local.conf delete mode 100644 nginx/default.conf create mode 100644 nginx/njs/main.js diff --git a/client/src/pages/Student/JoinRoom/JoinRoom.tsx b/client/src/pages/Student/JoinRoom/JoinRoom.tsx index 343f883..d647c29 100644 --- a/client/src/pages/Student/JoinRoom/JoinRoom.tsx +++ b/client/src/pages/Student/JoinRoom/JoinRoom.tsx @@ -34,7 +34,7 @@ const JoinRoom: React.FC = () => { }, []); const handleCreateSocket = () => { - const socket = webSocketService.connect("localhost:4500"); + const socket = webSocketService.connect(`/api/room/${roomName}/socket`); socket.on('join-success', () => { setIsWaitingForTeacher(true); diff --git a/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx b/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx index bb9f97e..78ec409 100644 --- a/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx +++ b/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx @@ -79,9 +79,10 @@ const ManageRoom: React.FC = () => { } }; - const createWebSocketRoom = () => { + const createWebSocketRoom = async() => { setConnectingError(''); - const socket = webSocketService.connect("localhost:4500"); + const room = await(await fetch('/api/room',{method:'post'})).json(); + const socket = webSocketService.connect(`/api/room/${room.id}/socket`); socket.on('connect', () => { webSocketService.createRoom(); diff --git a/nginx/Dockerfile b/nginx/Dockerfile index 6597d48..9f1280d 100644 --- a/nginx/Dockerfile +++ b/nginx/Dockerfile @@ -1,3 +1,69 @@ -FROM nginx +# Stage 1: Build stage +FROM nginx:1.27-alpine AS builder -COPY ./default.conf /etc/nginx/conf.d/default.conf \ No newline at end of file +# Install required packages +RUN apk add --no-cache nginx-mod-http-js nginx-mod-http-keyval + +# Stage 2: Final stage +FROM alpine:3.19 + +# Copy Nginx and NJS modules from builder +COPY --from=builder /usr/sbin/nginx /usr/sbin/ +COPY --from=builder /usr/lib/nginx/modules/ /usr/lib/nginx/modules/ +COPY --from=builder /etc/nginx/ /etc/nginx/ +COPY --from=builder /usr/lib/nginx/ /usr/lib/nginx/ + +# Install required runtime dependencies +RUN apk add --no-cache \ + pcre2 \ + ca-certificates \ + pcre \ + libgcc \ + libstdc++ \ + zlib \ + libxml2 \ + libedit \ + geoip \ + libxslt \ + && mkdir -p /var/cache/nginx \ + && mkdir -p /var/log/nginx \ + && mkdir -p /etc/nginx/conf.d \ + && mkdir -p /etc/nginx/njs \ + && ln -sf /dev/stdout /var/log/nginx/access.log \ + && ln -sf /dev/stderr /var/log/nginx/error.log \ + && addgroup -S nginx \ + && adduser -D -S -h /var/cache/nginx -s /sbin/nologin -G nginx nginx + +# Copy necessary libraries from builder +COPY --from=builder /usr/lib/libxml2.so* /usr/lib/ +COPY --from=builder /usr/lib/libexslt.so* /usr/lib/ +COPY --from=builder /usr/lib/libgd.so* /usr/lib/ +COPY --from=builder /usr/lib/libxslt.so* /usr/lib/ + +# Modify nginx.conf to load modules +RUN echo 'load_module modules/ngx_http_js_module.so;' > /tmp/nginx.conf && \ + cat /etc/nginx/nginx.conf >> /tmp/nginx.conf && \ + mv /tmp/nginx.conf /etc/nginx/nginx.conf + +# Copy our configuration +COPY conf.d/default.conf /etc/nginx/conf.d/ +COPY njs/main.js /etc/nginx/njs/ + +# Set proper permissions +RUN chown -R nginx:nginx /var/cache/nginx \ + && chown -R nginx:nginx /var/log/nginx \ + && chown -R nginx:nginx /etc/nginx/conf.d \ + && touch /var/run/nginx.pid \ + && chown -R nginx:nginx /var/run/nginx.pid + +# Verify the configuration +# RUN nginx -t --dry-run + +# Switch to non-root user +USER nginx + +# Expose HTTP port +EXPOSE 80 + +# Start Nginx +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/nginx/conf.d/default.conf b/nginx/conf.d/default.conf new file mode 100644 index 0000000..c454326 --- /dev/null +++ b/nginx/conf.d/default.conf @@ -0,0 +1,60 @@ +js_import njs/main.js; +js_set $quiz_room_host main.getQuizRoomHost; + +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +# Cache for room information +# keyval_zone zone=rooms:10m; +# keyval_zone zone=room_hosts:10m; +# keyval $room_id $room_info zone=rooms; + +upstream frontend { + server frontend:5173; +} + +upstream backend { + server backend:3000; +} + +server { + listen 80; + + location /api { + rewrite /backend/(.*) /$1 break; + proxy_pass http://backend; + } + + location /socket.io { + rewrite /backend/(.*) /$1 break; + proxy_pass http://backend; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + proxy_hide_header 'Access-Control-Allow-Origin'; + } + + location /quiz/([^/]+)/socket { + # Routing logic + set $room_id $1; + js_content main.routeWebSocket; + + #Proxy headers + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + proxy_buffering off; + } + + location / { + proxy_pass http://frontend; + } + +} diff --git a/nginx/conf.d/local.conf b/nginx/conf.d/local.conf new file mode 100644 index 0000000..083e8eb --- /dev/null +++ b/nginx/conf.d/local.conf @@ -0,0 +1,60 @@ +js_import njs/main.js; +js_set $quiz_room_host main.getQuizRoomHost; + +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +# Cache for room information +# keyval_zone zone=rooms:10m; +# keyval_zone zone=room_hosts:10m; +# keyval $room_id $room_info zone=rooms; + +upstream frontend { + server localhost:5173; +} + +upstream backend { + server localhost:3000; +} + +server { + listen 80; + + location /api { + rewrite /backend/(.*) /$1 break; + proxy_pass http://backend; + } + + location /socket.io { + rewrite /backend/(.*) /$1 break; + proxy_pass http://backend; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + proxy_hide_header 'Access-Control-Allow-Origin'; + } + + location /quiz/([^/]+)/socket { + # Routing logic + set $room_id $1; + js_content main.routeWebSocket; + + #Proxy headers + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + proxy_buffering off; + } + + location / { + proxy_pass http://frontend; + } + +} diff --git a/nginx/default.conf b/nginx/default.conf deleted file mode 100644 index b698731..0000000 --- a/nginx/default.conf +++ /dev/null @@ -1,31 +0,0 @@ -upstream frontend { - server frontend:5173; -} - -upstream backend { - server backend:3000; -} - -server { - listen 80; - - location /api { - rewrite /backend/(.*) /$1 break; - proxy_pass http://backend; - } - - location /socket.io { - rewrite /backend/(.*) /$1 break; - proxy_pass http://backend; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header Host $host; - proxy_hide_header 'Access-Control-Allow-Origin'; - } - - location / { - proxy_pass http://frontend; - } - -} diff --git a/nginx/njs/main.js b/nginx/njs/main.js new file mode 100644 index 0000000..436625b --- /dev/null +++ b/nginx/njs/main.js @@ -0,0 +1,43 @@ +async function fetchRoomInfo(r) { + try { + // Make request to API to get room info + let res = await r.subrequest(`/api/room/${r.variables.room_id}`); + + if (res.status !== 200) { + r.error(`Failed to fetch room info: ${res.status}`); + return null; + } + + return JSON.parse(res.responseText); + } catch (error) { + r.error(`Error fetching room info: ${error}`); + return null; + } +} + +// Main routing function for WebSocket connections +async function routeWebSocket(r) { + try { + const roomInfo = await fetchRoomInfo(r); + + if (!roomInfo || !roomInfo.host) { + r.return(404, 'Room not found or invalid'); + return; + } + + // Route the WebSocket connection to the room's host + r.internalRedirect(`@quiz_room_${roomInfo.host}`); + + } catch (error) { + r.error(`WebSocket routing error: ${error}`); + r.return(500, 'Internal routing error'); + } +} + +// Helper function to get room host for dynamic upstream +function getQuizRoomHost(r) { + const roomInfo = JSON.parse(r.variables.room_info); + return roomInfo.host || ''; +} + +export default { routeWebSocket, getQuizRoomHost }; \ No newline at end of file diff --git a/quizRoom/Dockerfile b/quizRoom/Dockerfile index 8e4af13..4a8d6cb 100644 --- a/quizRoom/Dockerfile +++ b/quizRoom/Dockerfile @@ -1,5 +1,5 @@ # Use the Node base image -FROM node:18 as quizroom +FROM node:18 AS quizroom # Create a working directory WORKDIR /usr/src/app diff --git a/server/routers/rooms.js b/server/routers/rooms.js index ac0cc13..3321f95 100644 --- a/server/routers/rooms.js +++ b/server/routers/rooms.js @@ -45,20 +45,9 @@ router.get("/:id", async (req, res) => { try { const data = await roomsController.getRoomStatus(); res.json(data); - } catch (error) { - res.status(500).json({ error: "Failed to join rooms" }); - } -}); - -router.get("/:id/status", async (req, res) => { - try { - const data = await roomsController.getRoomStatus(req.params.id); - res.json(data); } catch (error) { res.status(500).json({ error: "Failed to list room infos" }); } }); - - module.exports = router;