403Webshell
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 :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /opt/ccp-ai-gateway/app.py.bak_openapi_20260118_074235
import os
import json
import time
import secrets
from datetime import datetime, timezone

from flask import Flask, request, jsonify, make_response
from flask_cors import CORS
import requests

APP = Flask(__name__)
CORS(APP)

# ================== ENV ==================
ADMIN_TOKEN = os.environ.get("CCP_AI_ADMIN_TOKEN", "")
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "")
GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY", "")

TOWER_PASSWORD = os.environ.get("CCP_TOWER_PASSWORD", "")
SESSION_SECRET = os.environ.get("CCP_SESSION_SECRET", "")

DATA_DIR = os.environ.get("CCP_AI_DATA_DIR", "/opt/ccp-ai-gateway/data")
USERS_FILE = os.path.join(DATA_DIR, "users.json")
USAGE_LOG = os.path.join(DATA_DIR, "ai_usage.jsonl")
CERT_USAGE_LOG = os.path.join(DATA_DIR, "cert_usage.jsonl")
BALANCES_FILE = os.path.join(DATA_DIR, "balances.json")
PRICING_FILE = os.path.join(DATA_DIR, "pricing.json")

# Stima costo EUR (solo reporting, non billing)
COST_OPENAI_EUR = float(os.environ.get("CCP_COST_OPENAI_EUR_PER_REQ", "0.01"))
COST_GEMINI_EUR = float(os.environ.get("CCP_COST_GEMINI_EUR_PER_REQ", "0.04"))

os.makedirs(DATA_DIR, exist_ok=True)

# ================== UTILS ==================
def _now_iso():
    return datetime.now(timezone.utc).isoformat()

def _json_load(path, default):
    try:
        with open(path, "r", encoding="utf-8") as f:
            return json.load(f)
    except Exception:
        return default

def _json_save(path, obj):
    tmp = path + ".tmp"
    with open(tmp, "w", encoding="utf-8") as f:
        json.dump(obj, f, ensure_ascii=False, indent=2)
    os.replace(tmp, path)

def _append_jsonl(path, obj):
    with open(path, "a", encoding="utf-8") as f:
        f.write(json.dumps(obj, ensure_ascii=False) + "\n")

# ================== AUTH (admin header token) ==================
def require_admin_token(req):
    if not ADMIN_TOKEN:
        return True
    return req.headers.get("X-CCP-Token", "") == ADMIN_TOKEN

# ================== TOWER COOKIE SIGN ==================
def _sign(payload: dict) -> str:
    raw = json.dumps(payload, separators=(",", ":"), sort_keys=True)
    salt = secrets.token_urlsafe(12)
    import hashlib
    h = hashlib.sha256()
    h.update(SESSION_SECRET.encode("utf-8"))
    h.update(salt.encode("utf-8"))
    h.update(raw.encode("utf-8"))
    return f"{salt}.{h.hexdigest()}.{raw}"

def _verify(token: str):
    try:
        salt, digest, raw = token.split(".", 2)
        import hashlib
        h = hashlib.sha256()
        h.update(SESSION_SECRET.encode("utf-8"))
        h.update(salt.encode("utf-8"))
        h.update(raw.encode("utf-8"))
        if h.hexdigest() != digest:
            return None
        payload = json.loads(raw)
        if payload.get("exp") and time.time() > float(payload["exp"]):
            return None
        return payload
    except Exception:
        return None

def tower_session():
    tok = request.cookies.get("ccp_tower", "")
    # PATCH: curl cookie-jar può salvare il valore tra doppi apici e con virgole escaped (\054)
    tok = (tok or "").strip('"').replace("\\054", ",")
    if not tok or not SESSION_SECRET:
        return None
    payload = _verify(tok)
    if not payload or payload.get("role") != "tower_admin":
        return None
    return payload

def _tower_guard():
    if not (TOWER_PASSWORD and SESSION_SECRET and tower_session()):
        return jsonify({"error": "unauthorized"}), 401
    return None

# ================== USERS ==================
def ensure_users_file():
    if not os.path.exists(USERS_FILE):
        _json_save(USERS_FILE, {"users": []})

def find_user_by_token(tok: str):
    tok = (tok or "").strip()
    if not tok:
        return None
    ensure_users_file()
    data = _json_load(USERS_FILE, {"users": []})
    for u in data.get("users", []):
        if (u.get("token") or "").strip() == tok:
            return u
    return None

def upsert_user(user: dict):
    ensure_users_file()
    data = _json_load(USERS_FILE, {"users": []})
    users = data.get("users", [])
    uid = str(user.get("id"))
    found = False
    for i, u in enumerate(users):
        if str(u.get("id")) == uid:
            users[i] = {**u, **user}
            found = True
            break
    if not found:
        users.append(user)
    data["users"] = users
    _json_save(USERS_FILE, data)

# ================== BALANCES (ABO) ==================
def ensure_balances_file():
    if not os.path.exists(BALANCES_FILE):
        _json_save(BALANCES_FILE, {"balances": {}})

def get_balance(uid: str) -> float:
    ensure_balances_file()
    data = _json_load(BALANCES_FILE, {"balances": {}})
    b = data.get("balances", {}).get(str(uid), 0.0)
    try:
        return float(b)
    except Exception:
        return 0.0

def set_balance(uid: str, amount: float):
    ensure_balances_file()
    data = _json_load(BALANCES_FILE, {"balances": {}})
    data.setdefault("balances", {})[str(uid)] = float(amount)
    _json_save(BALANCES_FILE, data)

def add_balance(uid: str, delta: float) -> float:
    cur = get_balance(uid)
    newv = float(cur) + float(delta)
    set_balance(uid, newv)
    return newv

# ================== PRICING (ABO per operazione) ==================
def ensure_pricing_file():
    if not os.path.exists(PRICING_FILE):
        _json_save(PRICING_FILE, {
            "default_op_cost_abo": 1.0,
            "operations": {"generic": 1.0}
        })

def get_pricing():
    ensure_pricing_file()
    data = _json_load(PRICING_FILE, {"default_op_cost_abo": 1.0, "operations": {"generic": 1.0}})
    data.setdefault("default_op_cost_abo", 1.0)
    data.setdefault("operations", {"generic": data["default_op_cost_abo"]})
    return data

def op_cost_abo(op: str) -> float:
    p = get_pricing()
    op = (op or "generic").strip().lower()
    ops = p.get("operations", {}) or {}
    if op in ops:
        try:
            return float(ops[op])
        except Exception:
            return float(p.get("default_op_cost_abo", 1.0))
    return float(p.get("default_op_cost_abo", 1.0))

def debit_or_reject(uid: str, op: str):
    cost = op_cost_abo(op)
    bal = get_balance(uid)
    if bal < cost:
        return False, bal, cost
    newb = add_balance(uid, -cost)
    return True, newb, cost

# ================== USAGE (log) ==================

def cert_seen_request(uid: str, request_id: str) -> bool:
    request_id = (request_id or "").strip()
    if not request_id:
        return False
    try:
        with open(CERT_USAGE_LOG, "r", encoding="utf-8") as f:
            for line in f:
                try:
                    row = json.loads(line)
                    if str(row.get("user_id")) == str(uid) and str(row.get("request_id")) == request_id:
                        return True
                except Exception:
                    continue
    except FileNotFoundError:
        return False
    except Exception:
        return False
    return False

def cert_log_request(**kw):
    _append_jsonl(CERT_USAGE_LOG, {"ts": _now_iso(), **kw})

def log_ai_usage(**kw):
    _append_jsonl(USAGE_LOG, {"ts": _now_iso(), **kw})

def summarize_usage(days=30):
    since = time.time() - int(days) * 86400
    res = {"by_user": {}, "total_requests": 0, "total_ok": 0, "total_cost_eur": 0.0}
    if not os.path.exists(USAGE_LOG):
        return {**res, "days": int(days), "from": datetime.fromtimestamp(since, timezone.utc).isoformat(), "to": _now_iso()}

    with open(USAGE_LOG, "r", encoding="utf-8") as f:
        for line in f:
            try:
                row = json.loads(line)
                ts = datetime.fromisoformat(row["ts"]).timestamp()
                if ts < since:
                    continue
                uid = str(row.get("user_id", "unknown"))
                u = res["by_user"].setdefault(uid, {"requests": 0, "ok": 0, "cost_eur": 0.0, "providers": {}})
                u["requests"] += 1
                res["total_requests"] += 1
                if row.get("ok"):
                    u["ok"] += 1
                    res["total_ok"] += 1
                cost = float(row.get("cost_eur", 0.0) or 0.0)
                u["cost_eur"] += cost
                res["total_cost_eur"] += cost
                p = row.get("provider", "unknown")
                u["providers"][p] = u["providers"].get(p, 0) + 1
            except Exception:
                pass

    return {
        **res,
        "days": int(days),
        "from": datetime.fromtimestamp(since, timezone.utc).isoformat(),
        "to": _now_iso()
    }

# ================== ROUTES ==================
@APP.get("/health")
def health():
    return jsonify({
        "ok": True,
        "has_openai_key": bool(OPENAI_API_KEY),
        "has_gemini_key": bool(GEMINI_API_KEY),
        "tower_configured": bool(TOWER_PASSWORD and SESSION_SECRET)
    })

@APP.post("/tower/login")
def tower_login():
    data = request.get_json(silent=True) or {}
    if data.get("password") != TOWER_PASSWORD:
        return jsonify({"error": "unauthorized"}), 401
    tok = _sign({"role": "tower_admin", "exp": time.time() + 86400})
    resp = make_response(jsonify({"ok": True}))
    resp.set_cookie("ccp_tower", tok, httponly=True, secure=True, samesite="Lax", path="/")
    return resp

@APP.post("/tower/users/upsert")
def tower_users_upsert():
    g = _tower_guard()
    if g:
        return g
    data = request.get_json(silent=True) or {}
    if not data.get("id"):
        return jsonify({"error": "missing_id"}), 400
    user = {
        "id": str(data["id"]),
        "token": data.get("token", ""),
        "email": data.get("email", ""),
        "name": data.get("name", ""),
        "wallet": data.get("wallet", ""),
        "plan": data.get("plan", "free"),
        # mantenuto solo per compatibilità UI, ma NON usato per bloccare
        "ai_limit_monthly": int(data.get("ai_limit_monthly", 0) or 0),
        "updated_at": _now_iso()
    }
    upsert_user(user)
    return jsonify({"ok": True})

@APP.get("/tower/users/list")
def tower_users_list():
    g = _tower_guard()
    if g:
        return g
    ensure_users_file()
    return jsonify(_json_load(USERS_FILE, {"users": []}))

@APP.get("/tower/ai/stats")
def tower_ai_stats():
    g = _tower_guard()
    if g:
        return g
    days = int(request.args.get("days", "30") or "30")
    days = max(1, min(days, 365))
    return jsonify(summarize_usage(days))

@APP.get("/tower/balances")
def tower_balances():
    g = _tower_guard()
    if g:
        return g
    ensure_balances_file()
    return jsonify(_json_load(BALANCES_FILE, {"balances": {}}))

@APP.post("/tower/balances/topup")
def tower_balances_topup():
    g = _tower_guard()
    if g:
        return g
    data = request.get_json(silent=True) or {}
    uid = str(data.get("user_id", "")).strip()
    amount = data.get("amount", None)
    if not uid or amount is None:
        return jsonify({"error": "missing_params"}), 400
    try:
        amount = float(amount)
    except Exception:
        return jsonify({"error": "invalid_amount"}), 400
    newb = add_balance(uid, amount)
    return jsonify({"ok": True, "user_id": uid, "balance_abo": newb})

@APP.post("/tower/balances/set")
def tower_balances_set():
    g = _tower_guard()
    if g:
        return g
    data = request.get_json(silent=True) or {}
    uid = str(data.get("user_id", "")).strip()
    balance = data.get("balance", None)
    if not uid or balance is None:
        return jsonify({"error": "missing_params"}), 400
    try:
        balance = float(balance)
    except Exception:
        return jsonify({"error": "invalid_balance"}), 400
    set_balance(uid, balance)
    return jsonify({"ok": True, "user_id": uid, "balance_abo": balance})

@APP.get("/tower/pricing")
def tower_pricing_get():
    g = _tower_guard()
    if g:
        return g
    return jsonify(get_pricing())

@APP.post("/tower/pricing/set")
def tower_pricing_set():
    g = _tower_guard()
    if g:
        return g
    data = request.get_json(silent=True) or {}
    # valida e salva
    default_cost = data.get("default_op_cost_abo", None)
    operations = data.get("operations", None)

    cur = get_pricing()
    if default_cost is not None:
        try:
            cur["default_op_cost_abo"] = float(default_cost)
        except Exception:
            return jsonify({"error": "invalid_default_cost"}), 400

    if operations is not None:
        if not isinstance(operations, dict):
            return jsonify({"error": "invalid_operations"}), 400
        cleaned = {}
        for k, v in operations.items():
            try:
                cleaned[str(k).strip().lower()] = float(v)
            except Exception:
                continue
        if cleaned:
            cur["operations"] = cleaned

    _json_save(PRICING_FILE, cur)
    return jsonify({"ok": True, "pricing": cur})

@APP.get("/me/balance")
def me_balance():
    tok = request.headers.get("X-CCP-Token", "")
    user = find_user_by_token(tok)
    if not user:
        return jsonify({"error": "unauthorized"}), 401
    uid = str(user.get("id"))
    return jsonify({"ok": True, "user_id": uid, "balance_abo": get_balance(uid), "pricing": get_pricing()})


@APP.post("/cert/debit")
def cert_debit():
    payload = request.get_json(silent=True) or {}
    tok = request.headers.get("X-CCP-Token", "")
    user = find_user_by_token(tok)
    if not user:
        return jsonify({"error": "unauthorized"}), 401

    cert_type = str(payload.get("cert_type", "base")).strip().lower()
    request_id = str(payload.get("request_id", "")).strip()

    if cert_type not in ("base", "blockchain"):
        return jsonify({"error": "invalid_cert_type", "allowed": ["base", "blockchain"]}), 400

    if not request_id:
        return jsonify({"error": "missing_request_id"}), 400

    op = "cert_blockchain" if cert_type == "blockchain" else "cert_base"
    uid = str(user.get("id"))

    # idempotenza: se già visto, non scalare
    if cert_seen_request(uid, request_id):
        return jsonify({
            "ok": True,
            "user_id": uid,
            "cert_type": cert_type,
            "op": op,
            "idempotent_replay": True,
            "request_id": request_id,
            "balance_abo": get_balance(uid)
        })

    # addebito
    ok_debit, bal_before, cost_abo = debit_or_reject(uid, op)
    if not ok_debit:
        return jsonify({
            "error": "insufficient_balance",
            "op": op,
            "cert_type": cert_type,
            "cost_abo": cost_abo,
            "balance_abo": bal_before
        }), 402

    # ledger certificati (idempotenza)
    cert_log_request(
        user_id=uid,
        request_id=request_id,
        op=op,
        cert_type=cert_type,
        abo_cost=cost_abo
    )

    # log generale
    log_ai_usage(
        user_id=uid,
        provider="gateway",
        ok=True,
        status=200,
        ms=0,
        cost_eur=0.0,
        op=op,
        abo_cost=cost_abo,
        cert_type=cert_type,
        request_id=request_id
    )

    return jsonify({
        "ok": True,
        "user_id": uid,
        "cert_type": cert_type,
        "op": op,
        "debited_abo": cost_abo,
        "idempotent_replay": False,
        "request_id": request_id,
        "balance_abo": get_balance(uid)
    })


@APP.post("/text")
def ai_text():
    payload = request.get_json(silent=True) or {}
    tok = request.headers.get("X-CCP-Token", "")
    user = find_user_by_token(tok)
    if not user:
        return jsonify({"error": "unauthorized"}), 401

    prompt = str(payload.get("prompt", "")).strip()
    if not prompt:
        return jsonify({"error": "missing_prompt"}), 400

    op = str(payload.get("op", "generic")).strip().lower()
    uid = str(user.get("id"))

    # 1) scala ABO prima della chiamata
    ok_debit, bal_before, cost_abo = debit_or_reject(uid, op)
    if not ok_debit:
        return jsonify({
            "error": "insufficient_balance",
            "op": op,
            "cost_abo": cost_abo,
            "balance_abo": bal_before
        }), 402

    t0 = time.time()
    r = requests.post(
        "https://api.openai.com/v1/responses",
        headers={"Authorization": f"Bearer {OPENAI_API_KEY}"},
        json={"model": "gpt-4.1-mini", "input": prompt},
        timeout=120
    )
    ms = int((time.time() - t0) * 1000)

    # 2) se OpenAI fallisce: rimborsa ABO
    if r.status_code >= 400:
        add_balance(uid, cost_abo)
        log_ai_usage(
            user_id=uid,
            provider="openai",
            ok=False,
            status=r.status_code,
            ms=ms,
            cost_eur=0.0,
            op=op,
            abo_cost=cost_abo,
            error="openai_error"
        )
        return jsonify({"error": "openai_error", "details": r.text}), r.status_code

    log_ai_usage(
        user_id=uid,
        provider="openai",
        ok=True,
        status=200,
        ms=ms,
        cost_eur=COST_OPENAI_EUR,
        op=op,
        abo_cost=cost_abo
    )

    # risposta testo
    try:
        j = r.json()
    except Exception:
        j = {}
    return jsonify({"ok": True, "text": (j.get("output_text") or "").strip()})

if __name__ == "__main__":
    APP.run("127.0.0.1", 5060)

Youez - 2016 - github.com/yon3zu
LinuXploit