Spaces:
Sleeping
Sleeping
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
|