docs: update tasks from E2E test run; add proxy server
- Docs/Tasks.md: document 122 E2E test failures (fail2ban missing) - e2e/proxy_server.py: add HTTP proxy for frontend dev server - e2e/resources/common.resource: update test resource
This commit is contained in:
2298
Docs/Tasks.md
2298
Docs/Tasks.md
File diff suppressed because it is too large
Load Diff
93
e2e/proxy_server.py
Normal file
93
e2e/proxy_server.py
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Simple HTTP server that serves frontend dist and proxies /api to backend."""
|
||||||
|
|
||||||
|
import http.server
|
||||||
|
import os
|
||||||
|
import socketserver
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
PORT = 5173
|
||||||
|
BACKEND_URL = "http://localhost:8000"
|
||||||
|
DIST_DIR = "/home/lukas/Volume/repo/BanGUI/frontend/dist"
|
||||||
|
|
||||||
|
|
||||||
|
class ProxyHandler(http.server.SimpleHTTPRequestHandler):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, directory=DIST_DIR, **kwargs)
|
||||||
|
|
||||||
|
def do_GET(self):
|
||||||
|
if self.path.startswith("/api/"):
|
||||||
|
self.proxy_request("GET")
|
||||||
|
else:
|
||||||
|
super().do_GET()
|
||||||
|
|
||||||
|
def do_POST(self):
|
||||||
|
if self.path.startswith("/api/"):
|
||||||
|
self.proxy_request("POST")
|
||||||
|
else:
|
||||||
|
self.send_error(405)
|
||||||
|
|
||||||
|
def do_PUT(self):
|
||||||
|
if self.path.startswith("/api/"):
|
||||||
|
self.proxy_request("PUT")
|
||||||
|
else:
|
||||||
|
self.send_error(405)
|
||||||
|
|
||||||
|
def do_DELETE(self):
|
||||||
|
if self.path.startswith("/api/"):
|
||||||
|
self.proxy_request("DELETE")
|
||||||
|
else:
|
||||||
|
self.send_error(405)
|
||||||
|
|
||||||
|
def do_PATCH(self):
|
||||||
|
if self.path.startswith("/api/"):
|
||||||
|
self.proxy_request("PATCH")
|
||||||
|
else:
|
||||||
|
self.send_error(405)
|
||||||
|
|
||||||
|
def proxy_request(self, method):
|
||||||
|
url = BACKEND_URL + self.path
|
||||||
|
content_length = self.headers.get("Content-Length")
|
||||||
|
data = None
|
||||||
|
if content_length:
|
||||||
|
data = self.rfile.read(int(content_length))
|
||||||
|
|
||||||
|
req = urllib.request.Request(url, method=method, data=data)
|
||||||
|
for key, value in self.headers.items():
|
||||||
|
if key.lower() not in ("host", "content-length"):
|
||||||
|
req.add_header(key, value)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(req) as resp:
|
||||||
|
self.send_response(resp.status)
|
||||||
|
for key, value in resp.headers.items():
|
||||||
|
if key.lower() not in ("transfer-encoding", "content-encoding"):
|
||||||
|
self.send_header(key, value)
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(resp.read())
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
self.send_response(e.code)
|
||||||
|
for key, value in e.headers.items():
|
||||||
|
self.send_header(key, value)
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(e.read())
|
||||||
|
except Exception as e:
|
||||||
|
self.send_error(502, str(e))
|
||||||
|
|
||||||
|
def end_headers(self):
|
||||||
|
self.send_header("Access-Control-Allow-Origin", "*")
|
||||||
|
self.send_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS")
|
||||||
|
self.send_header("Access-Control-Allow-Headers", "*")
|
||||||
|
super().end_headers()
|
||||||
|
|
||||||
|
def do_OPTIONS(self):
|
||||||
|
self.send_response(204)
|
||||||
|
self.end_headers()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
os.chdir(DIST_DIR)
|
||||||
|
with socketserver.TCPServer(("", PORT), ProxyHandler) as httpd:
|
||||||
|
print(f"Serving frontend at http://localhost:{PORT}")
|
||||||
|
print(f"Proxying /api to {BACKEND_URL}")
|
||||||
|
httpd.serve_forever()
|
||||||
@@ -14,14 +14,17 @@ ${XFF_HEADER} ${EMPTY}
|
|||||||
|
|
||||||
*** Keywords ***
|
*** Keywords ***
|
||||||
Wait For Backend Health
|
Wait For Backend Health
|
||||||
[Documentation] Polls /api/v1/health until 200 or timeout.
|
[Documentation] Polls /api/v1/health/live until 200 or timeout.
|
||||||
|
... Uses the liveness probe because it is independent of
|
||||||
|
... fail2ban availability, unlike the combined /api/v1/health
|
||||||
|
... which returns 503 when fail2ban is offline.
|
||||||
[Arguments] ${timeout}=120 ${interval}=5
|
[Arguments] ${timeout}=120 ${interval}=5
|
||||||
${deadline}= Evaluate time.time() + ${timeout}
|
${deadline}= Evaluate time.time() + ${timeout}
|
||||||
WHILE True
|
WHILE True
|
||||||
${now}= Evaluate time.time()
|
${now}= Evaluate time.time()
|
||||||
IF ${now} >= ${deadline} FAIL Backend did not become healthy within ${timeout} seconds
|
IF ${now} >= ${deadline} FAIL Backend did not become healthy within ${timeout} seconds
|
||||||
${response}= GET ${BACKEND_URL}/api/v1/health expected_status=any
|
${response}= GET ${BACKEND_URL}/api/v1/health/live expected_status=any
|
||||||
IF ${response.status} == 200 BREAK
|
IF ${response.status_code} == 200 BREAK
|
||||||
Sleep ${interval}
|
Sleep ${interval}
|
||||||
END
|
END
|
||||||
Log Backend is healthy.
|
Log Backend is healthy.
|
||||||
|
|||||||
Reference in New Issue
Block a user