File size: 2,940 Bytes
b44bd66
5208038
b44bd66
 
 
 
 
 
5208038
b44bd66
 
 
 
5208038
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b44bd66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5208038
 
 
 
 
b44bd66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
"""Run the public Node API and private Python worker as one service."""
import json
import os
from pathlib import Path
import signal
import subprocess
import sys
import time
from urllib.request import urlopen

from dotenv import load_dotenv


def wait_for_analysis(process, should_stop, timeout_seconds=25 * 60):
    deadline = time.monotonic() + timeout_seconds
    while not should_stop():
        exit_code = process.poll()
        if exit_code is not None:
            print(f"Analysis process exited before it was ready ({exit_code})", flush=True)
            return False
        try:
            with urlopen("http://127.0.0.1:8000/healthz", timeout=1) as response:
                payload = json.load(response) if response.status == 200 else None
                if isinstance(payload, dict) and payload.get("ok") is True:
                    return process.poll() is None
        except (OSError, ValueError):
            pass
        if time.monotonic() >= deadline:
            print(f"Analysis process did not become ready within {timeout_seconds} seconds", flush=True)
            return False
        time.sleep(0.5)
    return False


def main():
    root = Path(__file__).resolve().parent
    load_dotenv(root / ".env")
    port = int(os.environ.get("PORT", "4000"))
    if not 1 <= port <= 65535 or port == 8000:
        raise ValueError("PORT must be between 1 and 65535 and differ from AI port 8000")
    env = dict(os.environ, PORT=str(port),
               ANALYSIS_API_URL="http://127.0.0.1:8000",
               BACKEND_INTERNAL_URL=f"http://127.0.0.1:{port}")
    children = []
    stopping = False

    def stop(signum, frame):
        nonlocal stopping
        stopping = True

    for sig in (signal.SIGINT, signal.SIGTERM):
        signal.signal(sig, stop)
    try:
        analysis = subprocess.Popen([sys.executable, "-m", "uvicorn", "app:app",
            "--host", "127.0.0.1", "--port", "8000", "--workers", "1"], cwd=root, env=env)
        children.append(analysis)
        if not wait_for_analysis(analysis, lambda: stopping):
            return 0 if stopping else 1
        children.append(subprocess.Popen(["node", "dist/server.js"], cwd=root, env=env))
        while not stopping:
            for child in children:
                if child.poll() is not None:
                    print(f"Backend process {child.pid} exited ({child.returncode}); stopping service", flush=True)
                    return child.returncode or 1
            time.sleep(0.25)
        return 0
    finally:
        for child in children:
            if child.poll() is None:
                child.terminate()
        deadline = time.monotonic() + 15
        for child in children:
            try:
                child.wait(timeout=max(0, deadline - time.monotonic()))
            except subprocess.TimeoutExpired:
                child.kill()
                child.wait()


if __name__ == "__main__":
    sys.exit(main())