feat: dapp registry + /api/getDapps, /img/dapps, DappAdmin; dapp-link verifier default-on (IDRS_VERIFY_DAPPS); is_live sticky override; fix edit-without-upload (Dapp+Token)
This commit is contained in:
1 parent
77d6cd5b92
commit
3b68820beb
9 files changed
+447
-51
No files matched your search
+87
-31
@@ -1,17 +1,16 @@
|
||||
import logging
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from app.models import ICON_DIR, SessionLocal, Token
|
||||
from app.models import ICON_DIR, SessionLocal, Dapp, Token
|
||||
|
||||
logger = logging.getLogger("idrs.verify")
|
||||
|
||||
ENABLED = os.environ.get("IDRS_VERIFY_ENABLED", "").strip().lower() in ("1", "true", "yes", "on")
|
||||
TOKENS_ENABLED = os.environ.get("IDRS_VERIFY_TOKENS", "").strip().lower() in ("1", "true", "yes", "on")
|
||||
DAPPS_ENABLED = os.environ.get("IDRS_VERIFY_DAPPS", "1").strip().lower() not in ("0", "false", "off", "no")
|
||||
PUBLIC_BASE_URL = os.environ.get("IDRS_PUBLIC_URL", "https://idrs.databisnis.id").rstrip("/")
|
||||
PUBLIC_HOST = urlparse(PUBLIC_BASE_URL).hostname or "idrs.databisnis.id"
|
||||
INTERVAL = int(os.environ.get("IDRS_VERIFY_INTERVAL", "600"))
|
||||
@@ -26,31 +25,25 @@ def _local_reachable(filename: str) -> bool:
|
||||
return path.is_file() and token_dir.resolve() in path.parents
|
||||
|
||||
|
||||
def _http_reachable(url: str) -> bool:
|
||||
req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT, "Accept": "image/*,*/*"})
|
||||
def _http_reachable(url: str, accept: str = "image/*,*/*") -> bool:
|
||||
req = urllib.request.Request(
|
||||
url, headers={"User-Agent": USER_AGENT, "Accept": accept}
|
||||
)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=HTTP_TIMEOUT) as resp:
|
||||
return 200 <= resp.status < 300
|
||||
except urllib.error.HTTPError as e:
|
||||
logger.warning("icon http %s for %s", e.code, url)
|
||||
logger.warning("http %s for %s", e.code, url)
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.warning("icon network error %r for %s", e, url)
|
||||
logger.warning("network error %r for %s", e, url)
|
||||
return False
|
||||
|
||||
|
||||
def reachable(token) -> bool:
|
||||
url = (token.iconUrl or "").strip()
|
||||
if not url:
|
||||
return False
|
||||
if urlparse(url).hostname == PUBLIC_HOST:
|
||||
return _local_reachable(token.filename or "")
|
||||
return _http_reachable(url)
|
||||
|
||||
|
||||
class Verifier(threading.Thread):
|
||||
def __init__(self):
|
||||
super().__init__(daemon=True, name="idrs-verify")
|
||||
def __init__(self, name: str, enabled: bool):
|
||||
super().__init__(daemon=True, name=name)
|
||||
self._enabled = enabled
|
||||
self._stop = threading.Event()
|
||||
self._fail_counts = {}
|
||||
|
||||
@@ -58,8 +51,8 @@ class Verifier(threading.Thread):
|
||||
self._stop.set()
|
||||
|
||||
def start(self):
|
||||
if not ENABLED:
|
||||
logger.info("verifier disabled (IDRS_VERIFY_ENABLED not set)")
|
||||
if not self._enabled:
|
||||
logger.info("%s disabled", self.name)
|
||||
return
|
||||
super().start()
|
||||
|
||||
@@ -68,24 +61,87 @@ class Verifier(threading.Thread):
|
||||
try:
|
||||
self._check_once()
|
||||
except Exception:
|
||||
logger.exception("verify pass failed")
|
||||
logger.exception("%s pass failed", self.name)
|
||||
self._stop.wait(INTERVAL)
|
||||
|
||||
def _check_once(self):
|
||||
session = SessionLocal()
|
||||
try:
|
||||
for token in session.query(Token).all():
|
||||
ok = reachable(token)
|
||||
for item in self._items(session):
|
||||
if self._skip(item):
|
||||
continue
|
||||
ok = self._reachable(item)
|
||||
key = self._key(item)
|
||||
if ok:
|
||||
self._fail_counts[token.idfToken] = 0
|
||||
self._fail_counts[key] = 0
|
||||
live = True
|
||||
else:
|
||||
fails = self._fail_counts.get(token.idfToken, 0) + 1
|
||||
self._fail_counts[token.idfToken] = fails
|
||||
fails = self._fail_counts.get(key, 0) + 1
|
||||
self._fail_counts[key] = fails
|
||||
live = fails < GRACE_PASSES
|
||||
if bool(token.is_live) != live:
|
||||
token.is_live = live
|
||||
logger.info("token %s is_live -> %s (%s)", token.idfToken, live, token.iconUrl)
|
||||
if bool(item.is_live) != live:
|
||||
item.is_live = live
|
||||
logger.info("%s is_live -> %s (%s)", key, live, self._label(item))
|
||||
session.commit()
|
||||
finally:
|
||||
session.close()
|
||||
session.close()
|
||||
|
||||
def _items(self, session):
|
||||
raise NotImplementedError
|
||||
|
||||
def _key(self, item):
|
||||
raise NotImplementedError
|
||||
|
||||
def _label(self, item):
|
||||
raise NotImplementedError
|
||||
|
||||
def _reachable(self, item) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
def _skip(self, item) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
class TokenVerifier(Verifier):
|
||||
def __init__(self):
|
||||
super().__init__(name="idrs-verify-tokens", enabled=TOKENS_ENABLED)
|
||||
|
||||
def _items(self, session):
|
||||
return session.query(Token).all()
|
||||
|
||||
def _key(self, item):
|
||||
return item.idfToken
|
||||
|
||||
def _label(self, item):
|
||||
return item.iconUrl or ""
|
||||
|
||||
def _reachable(self, item) -> bool:
|
||||
url = (item.iconUrl or "").strip()
|
||||
if not url:
|
||||
return False
|
||||
if urlparse(url).hostname == PUBLIC_HOST:
|
||||
return _local_reachable(item.filename or "")
|
||||
return _http_reachable(url, "image/*,*/*")
|
||||
|
||||
|
||||
class DappVerifier(Verifier):
|
||||
def __init__(self):
|
||||
super().__init__(name="idrs-verify-dapps", enabled=DAPPS_ENABLED)
|
||||
|
||||
def _items(self, session):
|
||||
return session.query(Dapp).all()
|
||||
|
||||
def _key(self, item):
|
||||
return item.id
|
||||
|
||||
def _label(self, item):
|
||||
return item.link or ""
|
||||
|
||||
def _reachable(self, item) -> bool:
|
||||
url = (item.link or "").strip()
|
||||
if not url:
|
||||
return False
|
||||
return _http_reachable(url, "text/html,application/xhtml+xml,*/*")
|
||||
|
||||
def _skip(self, item) -> bool:
|
||||
return bool(getattr(item, "is_live_override", False))
|
||||
Reference in new issue
Block a user