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:
85
tools/_port_serve.py
Normal file
85
tools/_port_serve.py
Normal file
@@ -0,0 +1,85 @@
|
||||
"""serve.py: валидация int-параметров, eviction JOBS, токен на POST, лимит тела."""
|
||||
from pathlib import Path
|
||||
|
||||
p = Path(r"C:/Users/User/tmp/omni-parse/omni/serve.py")
|
||||
s = p.read_text(encoding="utf-8")
|
||||
rep = []
|
||||
|
||||
if "\nimport os\n" not in s:
|
||||
s = s.replace("import json\n", "import json\nimport os\n", 1)
|
||||
rep.append("+ import os")
|
||||
|
||||
helper = '''
|
||||
|
||||
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))
|
||||
'''
|
||||
|
||||
assert "def _job(jid, **kw):" in s
|
||||
s = s.replace("def _job(jid, **kw):", helper.strip("\n") + "\n\n\ndef _job(jid, **kw):", 1)
|
||||
|
||||
s = s.replace(''' with _LOCK:
|
||||
cur = JOBS.get(jid) or {}
|
||||
cur.update(kw)
|
||||
JOBS[jid] = cur''',
|
||||
''' 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)''')
|
||||
rep.append("JOBS eviction")
|
||||
|
||||
pairs = [
|
||||
('limit = max(1, min(500, int((q.get("limit") or ["80"])[0])))', 'limit = _intv(q, "limit", 80, 1, 500)'),
|
||||
('lim = max(1, min(200, int((q.get("limit") or ["40"])[0])))', 'lim = _intv(q, "limit", 40, 1, 200)'),
|
||||
('lim = max(1, min(40, int((q.get("limit") or ["14"])[0])))', 'lim = _intv(q, "limit", 14, 1, 40)'),
|
||||
('limit = min(int((q.get("limit") or ["40"])[0]), 200)', 'limit = _intv(q, "limit", 40, 1, 200)'),
|
||||
('offset = int((q.get("offset") or ["0"])[0])', 'offset = _intv(q, "offset", 0, 0, 1_000_000)'),
|
||||
('hosts = max(5, min(120, int((q.get("hosts") or ["40"])[0])))', 'hosts = _intv(q, "hosts", 40, 5, 120)'),
|
||||
('add = max(1, min(60, int((q.get("add") or ["25"])[0])))', 'add = _intv(q, "add", 25, 1, 60)'),
|
||||
]
|
||||
for old, new in pairs:
|
||||
if old in s:
|
||||
s = s.replace(old, new)
|
||||
rep.append("int -> " + new.split(" = ")[0] + " " + new.split("_intv")[1][:26])
|
||||
|
||||
old_post = ''' def do_POST(self):
|
||||
u = urllib.parse.urlsplit(self.path)
|
||||
n = int(self.headers.get("Content-Length") or 0)'''
|
||||
new_post = ''' def do_POST(self):
|
||||
u = urllib.parse.urlsplit(self.path)
|
||||
if not _ui_auth(self):
|
||||
return self._json({"error": "unauthorized"}, 401)
|
||||
n = min(int(self.headers.get("Content-Length") or 0), 2_000_000)'''
|
||||
assert old_post in s, "do_POST шаблон не найден"
|
||||
s = s.replace(old_post, new_post)
|
||||
rep.append("POST: токен + лимит тела")
|
||||
|
||||
s = s.replace(''' print(f"omni ui: http://127.0.0.1:{port} (источников {len(sources.all_sources())})")''',
|
||||
''' 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")''')
|
||||
|
||||
p.write_text(s, encoding="utf-8", newline="\n")
|
||||
print("\n".join(rep))
|
||||
print("остались сырые int((q.get:", s.count("int((q.get"))
|
||||
Reference in New Issue
Block a user