File size: 2,389 Bytes
ce8f04a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
"""Developer API key management."""
from __future__ import annotations

import hashlib
import secrets
import uuid
from datetime import datetime, timezone
from typing import Any

from sqlalchemy.orm import Session

from core.subscription.models import DeveloperApiKey


def _hash_key(raw: str) -> str:
    return hashlib.sha256(raw.encode("utf-8")).hexdigest()


def _prefix(raw: str) -> str:
    return raw[:8]


def create_api_key(db: Session, user_id: str, label: str) -> tuple[str, dict[str, Any]]:
    raw = f"gf_{secrets.token_urlsafe(32)}"
    row = DeveloperApiKey(
        id=str(uuid.uuid4()),
        user_id=user_id,
        label=label.strip(),
        key_prefix=_prefix(raw),
        key_hash=_hash_key(raw),
    )
    db.add(row)
    db.commit()
    db.refresh(row)
    meta = {
        "id": row.id,
        "label": row.label,
        "key_prefix": row.key_prefix,
        "created_at": row.created_at.isoformat() if row.created_at else None,
    }
    return raw, meta


def list_api_keys(db: Session, user_id: str) -> list[dict[str, Any]]:
    rows = (
        db.query(DeveloperApiKey)
        .filter(DeveloperApiKey.user_id == user_id, DeveloperApiKey.revoked_at.is_(None))
        .order_by(DeveloperApiKey.created_at.desc())
        .all()
    )
    return [
        {
            "id": r.id,
            "label": r.label,
            "key_prefix": r.key_prefix,
            "created_at": r.created_at.isoformat() if r.created_at else None,
            "last_used_at": r.last_used_at.isoformat() if r.last_used_at else None,
        }
        for r in rows
    ]


def verify_api_key_hash(db: Session, raw_key: str) -> dict[str, Any] | None:
    digest = _hash_key(raw_key)
    row = (
        db.query(DeveloperApiKey)
        .filter(DeveloperApiKey.key_hash == digest, DeveloperApiKey.revoked_at.is_(None))
        .first()
    )
    if not row:
        return None
    row.last_used_at = datetime.now(timezone.utc)
    db.commit()
    return {"user_id": row.user_id, "key_id": row.id, "label": row.label}


def revoke_api_key(db: Session, key_id: str, user_id: str) -> bool:
    row = (
        db.query(DeveloperApiKey)
        .filter(DeveloperApiKey.id == key_id, DeveloperApiKey.user_id == user_id)
        .first()
    )
    if not row or row.revoked_at:
        return False
    row.revoked_at = datetime.now(timezone.utc)
    db.commit()
    return True