Commit restored mcp.verae.ai site pages (app, register, macos, windows, explorer), hasher/download artifacts, index/sample/hasher updates, ledger and global_client improvements, NATS stamp helper, and TestFlight enroll service. Ignore debug-trace/ and editor backups. Standard Readme for download and enroll docs.
115 lines
4 KiB
Python
115 lines
4 KiB
Python
#!/usr/bin/env python3
|
|
"""Minimal HTTP service: POST /api/testflight-invite → ASC external TestFlight enroll.
|
|
|
|
Bind 127.0.0.1 only; Caddy reverse_proxies https://mcp.verae.ai/api/testflight-invite
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
|
|
# Allow running from installed dir
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
from enroll_lib import enroll, validate_email # noqa: E402
|
|
|
|
HOST = os.environ.get("ENROLL_BIND", "127.0.0.1")
|
|
PORT = int(os.environ.get("ENROLL_PORT", "8011"))
|
|
|
|
|
|
class Handler(BaseHTTPRequestHandler):
|
|
server_version = "verae-testflight-enroll/1.0"
|
|
|
|
def log_message(self, fmt, *args):
|
|
# Avoid logging emails/bodies
|
|
sys.stderr.write("%s - %s\n" % (self.address_string(), fmt % args))
|
|
|
|
def _cors(self):
|
|
origin = self.headers.get("Origin") or ""
|
|
allowed = {
|
|
"https://mcp.verae.ai",
|
|
"https://mcp.georgelambert.org",
|
|
"http://127.0.0.1:8000",
|
|
"http://localhost:8000",
|
|
}
|
|
if origin in allowed:
|
|
self.send_header("Access-Control-Allow-Origin", origin)
|
|
self.send_header("Vary", "Origin")
|
|
self.send_header("Access-Control-Allow-Methods", "POST, OPTIONS")
|
|
self.send_header("Access-Control-Allow-Headers", "Content-Type")
|
|
|
|
def _json(self, code: int, obj: dict):
|
|
raw = json.dumps(obj).encode("utf-8")
|
|
self.send_response(code)
|
|
self.send_header("Content-Type", "application/json; charset=utf-8")
|
|
self._cors()
|
|
self.send_header("Content-Length", str(len(raw)))
|
|
self.end_headers()
|
|
self.wfile.write(raw)
|
|
|
|
def do_OPTIONS(self):
|
|
self.send_response(204)
|
|
self._cors()
|
|
self.end_headers()
|
|
|
|
def do_GET(self):
|
|
if self.path.split("?")[0] in ("/healthz", "/api/testflight-invite/healthz"):
|
|
self._json(200, {"ok": True, "service": "verae-testflight-enroll"})
|
|
return
|
|
self._json(404, {"ok": False, "error": "not_found"})
|
|
|
|
def do_POST(self):
|
|
path = self.path.split("?")[0]
|
|
if path not in ("/api/testflight-invite", "/"):
|
|
self._json(404, {"ok": False, "error": "not_found"})
|
|
return
|
|
length = int(self.headers.get("Content-Length") or "0")
|
|
if length > 8192:
|
|
self._json(413, {"ok": False, "error": "too_large", "message": "Request too large."})
|
|
return
|
|
raw = self.rfile.read(length) if length else b"{}"
|
|
try:
|
|
body = json.loads(raw.decode("utf-8") or "{}")
|
|
except json.JSONDecodeError:
|
|
self._json(400, {"ok": False, "error": "invalid_json", "message": "Expected JSON body."})
|
|
return
|
|
email = body.get("email") or body.get("apple_id") or ""
|
|
note = body.get("note") or ""
|
|
try:
|
|
validate_email(email)
|
|
except ValueError:
|
|
self._json(400, {
|
|
"ok": False,
|
|
"error": "invalid_email",
|
|
"message": "Enter a valid Apple ID email address.",
|
|
})
|
|
return
|
|
try:
|
|
result = enroll(email, note=str(note)[:500])
|
|
except RuntimeError as e:
|
|
code = str(e)
|
|
self._json(503, {
|
|
"ok": False,
|
|
"error": code,
|
|
"message": "Enrollment service is not configured. Email marchon@gmail.com with your Apple ID.",
|
|
})
|
|
return
|
|
except Exception:
|
|
self._json(500, {
|
|
"ok": False,
|
|
"error": "internal",
|
|
"message": "Enrollment failed unexpectedly. Email marchon@gmail.com with your Apple ID.",
|
|
})
|
|
return
|
|
self._json(200 if result.get("ok") else 502, result)
|
|
|
|
|
|
def main():
|
|
httpd = ThreadingHTTPServer((HOST, PORT), Handler)
|
|
print(f"verae-testflight-enroll listening on {HOST}:{PORT}", flush=True)
|
|
httpd.serve_forever()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|