fix: 10 пунктов ревью — /run (кортеж store.add), валидация limit/offset в панели, close() соединений SQLite, троттлинг под локом, read-only бот без OMNI_BOT_OWNERS, eviction JOBS, push_new отдаёт свежие записи, ai.py не спит после последней попытки, дедуп реестра, подключён abuse-реестр (+191 источник); tests/test_regress.py 16 проверок + CI py3.10-3.12
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
Запуск: python -m omni serve --port 8077
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import threading
|
||||
@@ -25,11 +26,36 @@ def _jid(prefix):
|
||||
return "%s%d" % (prefix, SEQ[0])
|
||||
|
||||
|
||||
UI_TOKEN = os.environ.get("OMNI_UI_TOKEN", "")
|
||||
|
||||
|
||||
def _ui_auth(req):
|
||||
"""Панель наружу: без токена мутации открыты любому, кто дошёл до порта."""
|
||||
if not UI_TOKEN:
|
||||
return True
|
||||
return req.headers.get("X-Omni-Token") == UI_TOKEN
|
||||
|
||||
|
||||
def _intv(q, key, default, lo, hi):
|
||||
"""int из query без падений: ?limit=abc раньше давал трейс и обрыв соединения,
|
||||
?limit=-1 отдавал всю базу (SQLite LIMIT -1 = без лимита)."""
|
||||
try:
|
||||
v = int((q.get(key) or [str(default)])[0])
|
||||
except (TypeError, ValueError):
|
||||
return default
|
||||
return max(lo, min(hi, v))
|
||||
|
||||
|
||||
def _job(jid, **kw):
|
||||
with _LOCK:
|
||||
cur = JOBS.get(jid) or {}
|
||||
cur.update(kw)
|
||||
cur["_ts"] = time.time()
|
||||
JOBS[jid] = cur
|
||||
if len(JOBS) > 400: # раньше росло без границ (500 запусков -> 500 записей)
|
||||
dead = sorted(JOBS.items(), key=lambda kv: kv[1].get("_ts", 0))[:len(JOBS) - 300]
|
||||
for k, _v in dead:
|
||||
JOBS.pop(k, None)
|
||||
|
||||
|
||||
def _cat_health():
|
||||
@@ -212,7 +238,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
|
||||
if path == "/api/runs":
|
||||
try:
|
||||
limit = max(1, min(500, int((q.get("limit") or ["80"])[0])))
|
||||
limit = _intv(q, "limit", 80, 1, 500)
|
||||
except ValueError:
|
||||
limit = 80
|
||||
import time as _t
|
||||
@@ -241,7 +267,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
if tag in ("all", "все"):
|
||||
tag = None
|
||||
try:
|
||||
lim = max(1, min(200, int((q.get("limit") or ["40"])[0])))
|
||||
lim = _intv(q, "limit", 40, 1, 200)
|
||||
except ValueError:
|
||||
lim = 40
|
||||
return self._json(_ab.scan(limit=lim, tag=tag))
|
||||
@@ -251,7 +277,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
if tag in ("all", "все"):
|
||||
tag = None
|
||||
try:
|
||||
lim = max(1, min(40, int((q.get("limit") or ["14"])[0])))
|
||||
lim = _intv(q, "limit", 14, 1, 40)
|
||||
except ValueError:
|
||||
lim = 14
|
||||
try:
|
||||
@@ -302,8 +328,8 @@ class Handler(BaseHTTPRequestHandler):
|
||||
if path == "/api/items":
|
||||
cat = (q.get("category") or [None])[0]
|
||||
query = (q.get("q") or [""])[0].strip()
|
||||
limit = min(int((q.get("limit") or ["40"])[0]), 200)
|
||||
offset = int((q.get("offset") or ["0"])[0])
|
||||
limit = _intv(q, "limit", 40, 1, 200)
|
||||
offset = _intv(q, "offset", 0, 0, 1_000_000)
|
||||
sort = (q.get("sort") or ["score"])[0]
|
||||
priced = (q.get("priced") or ["0"])[0] in ("1", "true", "on")
|
||||
typ = (q.get("type") or ["all"])[0]
|
||||
@@ -349,7 +375,9 @@ class Handler(BaseHTTPRequestHandler):
|
||||
|
||||
def do_POST(self):
|
||||
u = urllib.parse.urlsplit(self.path)
|
||||
n = int(self.headers.get("Content-Length") or 0)
|
||||
if not _ui_auth(self):
|
||||
return self._json({"error": "unauthorized"}, 401)
|
||||
n = min(int(self.headers.get("Content-Length") or 0), 2_000_000)
|
||||
body = {}
|
||||
if n:
|
||||
try:
|
||||
@@ -360,8 +388,8 @@ class Handler(BaseHTTPRequestHandler):
|
||||
if u.path == "/api/discover":
|
||||
from . import discover as _d
|
||||
q = urllib.parse.parse_qs(u.query)
|
||||
hosts = max(5, min(120, int((q.get("hosts") or ["40"])[0])))
|
||||
add = max(1, min(60, int((q.get("add") or ["25"])[0])))
|
||||
hosts = _intv(q, "hosts", 40, 5, 120)
|
||||
add = _intv(q, "add", 25, 1, 60)
|
||||
dry = (q.get("dry") or ["0"])[0] not in ("0", "false", "")
|
||||
res = _d.discover(limit_hosts=hosts, workers=8, max_add=add, dry=dry)
|
||||
return self._json(res)
|
||||
@@ -421,4 +449,6 @@ class Handler(BaseHTTPRequestHandler):
|
||||
def serve(port=8077):
|
||||
srv = ThreadingHTTPServer(("127.0.0.1", port), Handler)
|
||||
print(f"omni ui: http://127.0.0.1:{port} (источников {len(sources.all_sources())})")
|
||||
if not UI_TOKEN:
|
||||
print("внимание: OMNI_UI_TOKEN не задан — POST /api/* без авторизации; наружу только за nginx с basic-auth")
|
||||
srv.serve_forever()
|
||||
Reference in New Issue
Block a user