File size: 3,789 Bytes
a0efa9c
a0709fa
a0efa9c
a0709fa
 
a0efa9c
a0709fa
a0efa9c
 
 
f657228
a0efa9c
a0709fa
a0efa9c
 
 
 
 
 
 
 
 
 
a0709fa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f657228
a0709fa
 
 
 
 
f657228
 
a0709fa
 
f657228
 
a0709fa
 
 
f657228
5c1b7a8
a0709fa
 
 
 
a0efa9c
 
 
 
a0709fa
a0efa9c
 
 
 
a0709fa
a0efa9c
 
 
 
5c1b7a8
f657228
 
 
a0709fa
f657228
a0efa9c
a0709fa
 
 
 
a0efa9c
 
 
a0709fa
 
 
5c1b7a8
a0709fa
 
 
 
a0efa9c
 
 
a0709fa
a0efa9c
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#!/usr/bin/env python3
"""Meta API wrapper — starts engine via werkzeug, exposes on port 7860."""
import os
import sys
import subprocess
import time
import threading

import httpx
import uvicorn
from fastapi import FastAPI, Query
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse, Response

app = FastAPI(title="Meta API", docs_url=None, redoc_url=None)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

INTERNAL_PORT = 8888
INTERNAL_URL = f"http://127.0.0.1:{INTERNAL_PORT}"
_engine_proc = None
_engine_ready = False


def start_engine():
    """Start internal engine via werkzeug (process = python3, NOT granian/searxng)."""
    global _engine_proc, _engine_ready

    os.environ["SEARXNG_SETTINGS_PATH"] = "/etc/searxng/settings.yml"

    launcher = "/tmp/eng.py"
    with open(launcher, "w") as lf:
        lf.write(f"""
import os, sys
os.environ["SEARXNG_SETTINGS_PATH"] = "/etc/searxng/settings.yml"
sys.path.insert(0, "/usr/local/searxng")
os.chdir("/usr/local/searxng")
from searx.webapp import app
from werkzeug.serving import run_simple
run_simple("127.0.0.1", {INTERNAL_PORT}, app, threaded=True, use_reloader=False)
""")

    _engine_proc = subprocess.Popen(
        [sys.executable, launcher],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        cwd="/usr/local/searxng",
    )
    print(f"Engine starting (pid={_engine_proc.pid})...", flush=True)

    for i in range(120):
        if _engine_proc.poll() is not None:
            stderr = _engine_proc.stderr.read().decode()[-500:]
            print(f"Engine died: {stderr}", flush=True)
            return
        try:
            resp = httpx.get(f"{INTERNAL_URL}/", timeout=2.0)
            if resp.status_code == 200:
                _engine_ready = True
                print(f"Engine ready after {i+1}s", flush=True)
                return
        except Exception:
            pass
        time.sleep(1)
    print("Engine timeout after 120s", flush=True)


@app.on_event("startup")
async def startup():
    threading.Thread(target=start_engine, daemon=True).start()


@app.on_event("shutdown")
async def shutdown():
    if _engine_proc:
        _engine_proc.terminate()


@app.get("/")
async def root():
    return {"status": "ok", "service": "meta-api", "version": "6.0.0", "engine": "ready" if _engine_ready else "starting"}


@app.get("/health")
async def health():
    return {"status": "healthy" if _engine_ready else "starting", "engine": "ready" if _engine_ready else "initializing"}


@app.get("/search")
async def search(
    q: str = Query(...),
    format: str = Query("json"),
    categories: str = Query("general"),
    language: str = Query("en"),
    time_range: str = Query(None),
    pageno: int = Query(1),
):
    if not _engine_ready:
        return JSONResponse(status_code=503, content={"error": "Engine still starting", "results": []})

    params = {"q": q, "format": "json", "categories": categories, "language": language, "pageno": pageno}
    if time_range:
        params["time_range"] = time_range

    try:
        async with httpx.AsyncClient(timeout=20.0) as client:
            resp = await client.get(f"{INTERNAL_URL}/search", params=params)
            if resp.status_code == 200:
                return JSONResponse(content=resp.json())
            return JSONResponse(status_code=resp.status_code, content={"error": f"Engine {resp.status_code}", "results": []})
    except Exception as e:
        return JSONResponse(status_code=503, content={"error": str(e), "results": []})


if __name__ == "__main__":
    port = int(os.environ.get("GRANIAN_PORT", os.environ.get("PORT", 7860)))
    uvicorn.run(app, host="0.0.0.0", port=port, log_level="info")