| Server IP : 54.37.205.81 / Your IP : 216.73.216.76 Web Server : nginx/1.22.1 System : Linux vps-249481fa 6.1.0-50-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.176-1 (2026-07-02) x86_64 User : debian ( 1000) PHP Version : 8.2.32 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /opt/ccp-ai-gateway/ |
Upload File : |
#!/usr/bin/env python3
import json
import os
import re
import smtplib
import subprocess
import tempfile
from datetime import datetime, timezone
from email.message import EmailMessage
from pathlib import Path
BASE = Path("/opt/ccp-ai-gateway")
DATA = BASE / "data"
CERT_LOG = DATA / "cert_usage.jsonl"
USERS_FILE = DATA / "users.json"
WORKER_LOG = DATA / "ots_notify_worker.log"
OTS_BIN = os.environ.get("OTS_BIN", "/home/debian/.local/bin/ots")
ENV_FILES = [BASE / ".env", Path("/etc/ccp-ai-gateway/ccp-ai-gateway.env")]
def now_iso():
return datetime.now(timezone.utc).isoformat()
def log(msg):
line = f"[{now_iso()}] {msg}"
print(line)
try:
with open(WORKER_LOG, "a", encoding="utf-8") as f:
f.write(line + "\n")
except Exception:
pass
def load_env():
for path in ENV_FILES:
if not path.exists():
continue
try:
for line in path.read_text(encoding="utf-8").splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
k, v = line.split("=", 1)
if k.strip() and k.strip() not in os.environ:
os.environ[k.strip()] = v.strip().strip('"').strip("'")
except Exception as e:
log(f"env read failed {path}: {e}")
def load_users():
try:
data = json.loads(USERS_FILE.read_text(encoding="utf-8"))
return {str(u.get("id", "")): u for u in data.get("users", []) if u.get("id")}
except Exception:
return {}
def load_rows():
rows = []
if not CERT_LOG.exists():
return rows
with open(CERT_LOG, "r", encoding="utf-8") as f:
for line in f:
try:
rows.append(json.loads(line))
except Exception:
continue
return rows
def save_rows(rows):
fd, tmp = tempfile.mkstemp(prefix="cert_usage.", suffix=".jsonl", dir=str(DATA))
os.close(fd)
with open(tmp, "w", encoding="utf-8") as f:
for row in rows:
f.write(json.dumps(row, ensure_ascii=False) + "\n")
os.replace(tmp, CERT_LOG)
def run_cmd(args, timeout=180):
try:
p = subprocess.run(args, capture_output=True, text=True, timeout=timeout)
return p.returncode, (p.stdout or "") + (p.stderr or "")
except Exception as e:
return 1, str(e)
def parse_confirmed(output):
txt = output or ""
low = txt.lower()
if "pending confirmation in bitcoin blockchain" in low:
return False, "", None
if "bitcoin block" not in low and "success" not in low:
return False, "", None
block_height = None
for pat in [r"bitcoin block\s+(\d+)", r"block height[:\s]+(\d+)", r"block[:\s]+(\d+)"]:
m = re.search(pat, txt, re.I)
if m:
try:
block_height = int(m.group(1))
break
except Exception:
pass
txid = ""
m = re.search(r"\b([a-fA-F0-9]{64})\b", txt)
if m:
txid = m.group(1)
return True, txid, block_height
def smtp_ready():
return all(os.environ.get(k) for k in ["SMTP_HOST", "SMTP_PORT", "SMTP_USER", "SMTP_PASSWORD", "SMTP_FROM"])
def send_email(row, user):
if not smtp_ready():
log(f"SMTP not configured; email skipped for {row.get('cert_id')}")
return False
to_email = str(user.get("email", "") or "").strip()
if not to_email or "@" not in to_email:
log(f"missing user email for {row.get('cert_id')}")
return False
cert_id = str(row.get("cert_id", "") or "").strip()
title = str(row.get("original_filename", "") or "").strip() or cert_id
verify_url = str(row.get("verify_url", "") or "").strip()
confirmed_at = str(row.get("confirmed_at", "") or "").strip() or now_iso()
msg = EmailMessage()
msg["Subject"] = f"Aggiornamento certificato CopyrightChain {cert_id}"
msg["From"] = os.environ["SMTP_FROM"]
msg["To"] = to_email
msg.set_content(f"""Gentile cliente,
il tuo certificato CopyrightChain รจ stato aggiornato.
ID certificato: {cert_id}
Opera / file: {title}
Stato: confermato
Data aggiornamento: {confirmed_at}
Verifica pubblica:
{verify_url}
La pagina di verifica pubblica mostra i riferimenti tecnici aggiornati del certificato.
CopyrightChain
Certificazione digitale verificabile
""")
host = os.environ["SMTP_HOST"]
port = int(os.environ.get("SMTP_PORT", "465"))
smtp_user = os.environ["SMTP_USER"]
smtp_pass = os.environ["SMTP_PASSWORD"]
use_ssl = str(os.environ.get("SMTP_SSL", "")).lower() in ("1", "true", "yes", "on")
if use_ssl:
with smtplib.SMTP_SSL(host, port, timeout=30) as s:
s.login(smtp_user, smtp_pass)
s.send_message(msg)
else:
with smtplib.SMTP(host, port, timeout=30) as s:
s.starttls()
s.login(smtp_user, smtp_pass)
s.send_message(msg)
log(f"email sent to {to_email} for {cert_id}")
return True
def main():
load_env()
users = load_users()
rows = load_rows()
changed = False
for row in rows:
if str(row.get("cert_type", "")).lower() != "blockchain":
continue
cert_id = str(row.get("cert_id", "") or "").strip()
status = str(row.get("blockchain_status", "") or "").strip().lower()
if status == "confirmed" and row.get("blockchain_email_sent_at"):
continue
proof = str(row.get("ots_path", "") or "").strip()
manifest = str(row.get("ots_manifest_path", "") or "").strip()
if not proof or not manifest or not os.path.exists(proof) or not os.path.exists(manifest):
continue
if status == "confirmed" and not row.get("blockchain_email_sent_at"):
user = users.get(str(row.get("user_id", "")), {})
if send_email(row, user):
row["blockchain_email_sent_at"] = now_iso()
changed = True
continue
log(f"checking {cert_id}")
_, out_up = run_cmd([OTS_BIN, "upgrade", proof])
row["ots_last_upgrade_at"] = now_iso()
_, out_verify = run_cmd([OTS_BIN, "verify", proof, "-f", manifest])
confirmed, txid, block_height = parse_confirmed(out_up + "\n" + out_verify)
if not confirmed:
log(f"{cert_id}: still pending")
changed = True
continue
row["blockchain_status"] = "confirmed"
row["confirmed_at"] = row.get("confirmed_at") or now_iso()
row["anchored_at"] = row.get("anchored_at") or row["confirmed_at"]
if txid:
row["btc_txid"] = txid
if block_height is not None:
row["btc_block_height"] = block_height
user = users.get(str(row.get("user_id", "")), {})
if not row.get("blockchain_email_sent_at"):
if send_email(row, user):
row["blockchain_email_sent_at"] = now_iso()
else:
row["blockchain_email_pending"] = True
log(f"{cert_id}: confirmed")
changed = True
if changed:
save_rows(rows)
log("cert_usage.jsonl updated")
else:
log("no changes")
if __name__ == "__main__":
main()