#!/usr/bin/env python3 """Simple static file server with /api proxy to backend.""" import os import http.server import socketserver import urllib.request import urllib.error from pathlib import Path PORT = 80 BACKEND = os.environ.get('BACKEND_URL', 'http://publish-backend:8080') STATIC_DIR = '/app/dist' UPLOADS_DIR = '/app/uploads' STATIC_PATH = Path(STATIC_DIR) UPLOADS_PATH = Path(UPLOADS_DIR) class ProxyHTTPRequestHandler(http.server.SimpleHTTPRequestHandler): """Serve static files + proxy /api/ requests to backend.""" def do_GET(self): if self.path.startswith('/api/') or self.path.startswith('/uploads/'): self.proxy_request() else: super().do_GET() def do_POST(self): if self.path.startswith('/api/'): self.proxy_request() else: self.send_error(404) def do_PUT(self): if self.path.startswith('/api/'): self.proxy_request() else: self.send_error(404) def do_DELETE(self): if self.path.startswith('/api/'): self.proxy_request() else: self.send_error(404) def proxy_request(self): """Forward request to backend and return response.""" url = BACKEND + self.path content_length = int(self.headers.get('Content-Length', 0)) body = self.rfile.read(content_length) if content_length else None headers = {} for key in ('Content-Type', 'Authorization', 'Accept', 'X-Requested-With'): if key in self.headers: headers[key] = self.headers[key] try: req = urllib.request.Request(url, data=body, headers=headers, method=self.command) with urllib.request.urlopen(req, timeout=30) as response: self.send_response(response.status) self.send_headers(response.headers) self.wfile.write(response.read()) except urllib.error.HTTPError as e: self.send_response(e.code) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(e.read()) except Exception as e: self.send_response(502) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(f'{{"error": "{e}"}}'.encode()) def send_headers(self, headers): """Copy headers from backend response.""" for key, value in headers.items(): if key not in ('Transfer-Encoding', 'Connection'): self.send_header(key, value) self.end_headers() def translate_path(self, path): """Serve from /app/dist for frontend, /app/uploads for files.""" if path.startswith('/uploads/'): return str(UPLOADS_PATH / path[9:]) return str(STATIC_PATH / path.lstrip('/')) class ReuseAddrTCPServer(socketserver.TCPServer): allow_reuse_address = True if __name__ == '__main__': os.chdir('/app') with ReuseAddrTCPServer(('', PORT), ProxyHTTPRequestHandler) as httpd: print(f'Serving on port {PORT}, backend at {BACKEND}') httpd.serve_forever()