Files
2026-07-12 10:17:17 -04:00

86 lines
3.2 KiB
Python

#!/usr/bin/env python3
"""Serve a Vite-built React SPA + proxy /pb/* to a PocketBase instance.
Usage: python3 proxy-server.py [--dist DIR] [--pb URL] [--port PORT]
Defaults: dist=./dist, pb=http://127.0.0.1:8091, port=4173
"""
from http.server import HTTPServer, SimpleHTTPRequestHandler
import urllib.request, os, argparse
class Handler(SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=opts.dist, **kwargs)
def end_headers(self):
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods',
'GET, POST, PUT, PATCH, DELETE, OPTIONS')
self.send_header('Access-Control-Allow-Headers',
'Authorization, Content-Type')
super().end_headers()
def do_OPTIONS(self):
self.send_response(204)
self.end_headers()
def do_GET(self):
if self.path.startswith('/pb') or self.path.startswith('/api'):
self._proxy()
else:
full = os.path.join(opts.dist, self.path.lstrip('/'))
if not os.path.exists(full) or (os.path.isdir(full) and self.path != '/'):
self.path = '/index.html'
super().do_GET()
def do_POST(self):
if self.path.startswith('/pb') or self.path.startswith('/api'):
self._proxy()
else:
self.send_error(405)
def do_PUT(self): self._proxy()
def do_PATCH(self): self._proxy()
def do_DELETE(self): self._proxy()
def _proxy(self):
# PB SDK calls /pb/api/collections/... — path already includes /api/
# Just strip the /pb prefix. Do NOT add another /api.
url = opts.pb + self.path
if self.path.startswith('/pb'):
url = opts.pb + self.path[3:]
if not url.startswith(opts.pb + '/api'):
url = opts.pb + '/api' + self.path[3:]
body = None
if self.headers.get('Content-Length'):
body = self.rfile.read(int(self.headers['Content-Length']))
req = urllib.request.Request(url, data=body, method=self.command)
for k, v in self.headers.items():
if k.lower() not in ('host', 'connection', 'origin', 'referer'):
req.add_header(k, v)
try:
resp = urllib.request.urlopen(req)
self.send_response(resp.status)
for k, v in resp.headers.items():
if k.lower() not in ('transfer-encoding', 'connection'):
self.send_header(k, v)
self.end_headers()
self.wfile.write(resp.read())
except urllib.error.HTTPError as e:
self.send_response(e.code)
self.end_headers()
self.wfile.write(e.read())
if __name__ == '__main__':
p = argparse.ArgumentParser()
p.add_argument('--dist', default='./dist')
p.add_argument('--pb', default='http://127.0.0.1:8091')
p.add_argument('--port', type=int, default=4173)
opts = p.parse_args()
server = HTTPServer(('0.0.0.0', opts.port), Handler)
print(f'Serving {opts.dist} on http://0.0.0.0:{opts.port} '
f'(PB proxy → {opts.pb})')
server.serve_forever()