From 02d47513d6ff1f501c8ac16c18411135dd5cc959 Mon Sep 17 00:00:00 2001 From: Victor Vobis Date: Sat, 21 Feb 2026 19:49:05 +0100 Subject: [PATCH] moving from python to GOAT nginx --- .env | 1 + __pycache__/proxy.cpython-314.pyc | Bin 6130 -> 10581 bytes backend.py | 93 ------------------------- nginx.conf | 111 ++++++++++++++++++++++++++++++ proxy.py | 83 ---------------------- services.py | 4 -- site/minirt.html | 9 +-- site/minishell.html | 2 +- start_app.sh | 3 + 9 files changed, 121 insertions(+), 185 deletions(-) delete mode 100644 backend.py create mode 100644 nginx.conf delete mode 100644 proxy.py delete mode 100644 services.py create mode 100755 start_app.sh diff --git a/.env b/.env index e69de29..8b13789 100644 --- a/.env +++ b/.env @@ -0,0 +1 @@ + diff --git a/__pycache__/proxy.cpython-314.pyc b/__pycache__/proxy.cpython-314.pyc index ae62ba31489186748097cf68bc47b995b817d17e..5a24722f6460c730230288efac17dafefc06e390 100644 GIT binary patch literal 10581 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π37*Y{R_$cE3v4s^zWo}kte47$(#fL Q!#_Cq)Vy2|iL6TN- """) - - -async def handle_service(request: web.Request): - [service, medium, target] = [ - request.match_info.get('service'), - request.match_info.get('medium'), - request.match_info.get('target') - ] - - if not target or target == '': - return web.json_response(status=404, data={"success": "false", "message": "Not Found"}) - - if 'vnc' in request.url.path: - return await proxy_http(request) - - if not service or not medium: - return web.json_response({"success": "false", "message": "Missing Serice or Medium"}) - - if not target: - target = "index.html" - - serve_path = f"{file_root}/service/{service}/{medium}/{target}" - - if not os.path.exists(serve_path): - data = { "success": "false", "message": "Not Found" } - return web.json_response( - status=404, - data=data, - ) - return web.FileResponse(serve_path) - - -async def serve_http(request: web.Request): - path_info = request.match_info.get('path_info', '') - - print(f"Req: {request} with path: {path_info}") - - if '..' in path_info: - data = {"success": "false", "message": "Forbidden"} - return web.json_response( - status=403, - data=data, - content_type='application/json' - ) - elif path_info == '': - path_info = "index.html" - - serve_path = f"{file_root}/{path_info}" - - print(f"looking for path {serve_path}") - - if not os.path.exists(serve_path): - data = { "success": "false", "message": "Not Found" } - return web.json_response( - status=404, - data=data, - ) - return web.FileResponse(serve_path) - -def build_http_server(): - app = web.Application() - app.add_routes([ - web.get('/{service}/{medium}/{target:.*}', handle_service), - web.get("/{service}/websockify", proxy_ws), - web.get("/{path_info:.*}", serve_http), - ]) - - return app - -def main(host, port): - app = build_http_server() - web.run_app(app, host=host, port=int(port)) - -try: - main(host, port) - -except ValueError as e: - print(f"Argv Error: {e}") - print_usage() - -except Exception as e: - print("Caught exception: {e}") - print_usage() diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..2f6d8a6 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,111 @@ +#user http; +worker_processes 1; + +#error_log logs/error.log; +#error_log logs/error.log notice; +#error_log logs/error.log info; + +pid /tmp/nginx.pid; + + +# Load all installed modules +include /etc/nginx/modules.d/*.conf; + +events { + worker_connections 1024; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + # '$status $body_bytes_sent "$http_referer" ' + # '"$http_user_agent" "$http_x_forwarded_for"'; + + #access_log logs/access.log main; + + sendfile on; + #tcp_nopush on; + + #keepalive_timeout 0; + keepalive_timeout 65; + + #gzip on; + + upstream minirt { + # server 127.0.0.1:7080; + } + + upstream minishell { + # server 127.0.0.1:6080; + } + + server { + listen 8080; + server_name localhost; + root ./site; + + #charset koi8-r; + + #access_log logs/host.access.log main; + + location / { + try_files $uri $uri/ /index.html; + } + + location /proxy/minirt/websockify { + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_http_version 1.1; + + proxy_read_timeout 61s; + proxy_buffering off; + proxy_pass http://minirt/websockify; + } + + location /proxy/minirt/ { + proxy_pass http://minirt/; + } + } + + + # another virtual host using mix of IP-, name-, and port-based configuration + # + #server { + # listen 8000; + # listen somename:8080; + # server_name somename alias another.alias; + + # location / { + # root html; + # index index.html index.htm; + # } + #} + + + # HTTPS server + # + #server { + # listen 443 ssl; + # server_name localhost; + + # ssl_certificate cert.pem; + # ssl_certificate_key cert.key; + + # ssl_session_cache shared:SSL:1m; + # ssl_session_timeout 5m; + + # ssl_ciphers HIGH:!aNULL:!MD5; + # ssl_prefer_server_ciphers on; + + # location / { + # root html; + # index index.html index.htm; + # } + #} + +} diff --git a/proxy.py b/proxy.py deleted file mode 100644 index a2a99fa..0000000 --- a/proxy.py +++ /dev/null @@ -1,83 +0,0 @@ -import aiohttp -from aiohttp import web -import asyncio -from datetime import datetime - -from services import services - - -connections = {} -max_duration = 60 - -async def proxy_http(request: web.Request): - service_name = request.match_info.get('service') - - if not service_name or not services[service_name]: - return web.json_response(status=404, data={ "success": "false", "message": "Unknown service name" }) - url = services[service_name] - - target = request.match_info.get('target') - target = f"{url}/{target}" - - async with aiohttp.ClientSession() as session: - async with session.request( - request.method, - f"http://{target}", - headers=request.headers, - data=await request.read() - ) as resp: - return web.Response( - body=await resp.read(), - status=resp.status, - headers=resp.headers - ) - -async def proxy_ws(request: web.Request): - service_name = request.match_info.get('service') - - if service_name in connections: - return web.Response(text='Service already in use', status=409) - - if not service_name or not services[service_name]: - return web.json_response(status=404, data={ "success": "false", "message": "Unknown service name" }) - - ws = web.WebSocketResponse() - await ws.prepare(request) - - connections[service_name] = (ws, datetime.now()) - - url = services[service_name] - - target = request.match_info.get('target') - target = f"{url}/{target}" - - - try: - async with aiohttp.ClientSession() as session: - async with session.ws_connect(f"ws://{target}/websockify") as remote_ws: - async def forward_to_remote(): - async for msg in ws: - if msg.type == aiohttp.WSMsgType.BINARY: - await remote_ws.send_bytes(msg.data) - - async def forward_to_client(): - async for msg in remote_ws: - if msg.type == aiohttp.WSMsgType.BINARY: - await ws.send_bytes(msg.data) - - async def check_timeout(): - while True: - await asyncio.sleep(10) - elapsed = (datetime.now() - connections[service_name][1]).total_seconds() - if elapsed > max_duration: - await ws.close() - break - - await asyncio.gather(forward_to_remote(), forward_to_client(), check_timeout()) - finally: - if service_name in connections: - del connections[service_name] - - return ws - - diff --git a/services.py b/services.py deleted file mode 100644 index 2fe9c3e..0000000 --- a/services.py +++ /dev/null @@ -1,4 +0,0 @@ -services: dict[str, str] = { - "minirt": "localhost:7080", - "minishell": "localhost:8080" -} diff --git a/site/minirt.html b/site/minirt.html index cffa568..e258f5d 100644 --- a/site/minirt.html +++ b/site/minirt.html @@ -63,7 +63,7 @@

- As a final note: since we finished the project, I was planning on showcasing it here, and so I have reimplemented the graphical part to use Raylib since it seemed more appropriate to use. + As a final note: since we finished the project, I was planning on showcasing it here, and so I have reimplemented the rendering layer to use Raylib since it seemed more appropriate to use.

@@ -71,7 +71,9 @@

- There are two ways to run the app: either directly in your browser with the WASM compiled program, or over a live noVNC connection to a running instance on the server. The raytracer itself is multithreaded, but since JS/WASM runs on a single thread, the WASM version is very slow. I scaled down the resolution for that version in order to have it not run at -5 FPS. + There are two ways to run the app: either directly in your browser with the WASM compiled program, or over a live noVNC connection to a running instance on the server. The connection to the VNC server will automatically be closed after 60 seconds in order to save resources. +

+

The raytracer itself is multithreaded, but since JS/WASM runs on a single thread, the WASM version is very slow. I scaled down the resolution for that version in order to have it not run at -5 FPS.

@@ -80,10 +82,9 @@

- diff --git a/site/minishell.html b/site/minishell.html index 12fa85f..a168ead 100644 --- a/site/minishell.html +++ b/site/minishell.html @@ -86,7 +86,7 @@ Have fun!

diff --git a/start_app.sh b/start_app.sh new file mode 100755 index 0000000..6b1dd65 --- /dev/null +++ b/start_app.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +nginx -p $(pwd) -c ./nginx.conf -g "daemon off;"