Files
wehub-resource-sync 555e282cc4
pi-agent-plugin checks / lint (push) Has been cancelled
pi-agent-plugin checks / test (20) (push) Has been cancelled
pi-agent-plugin checks / test (22) (push) Has been cancelled
pi-agent-plugin checks / build (push) Has been cancelled
TypeScript SDK CI / check_changes (push) Has been cancelled
TypeScript SDK CI / changelog_check (push) Has been cancelled
ci / changelog_check (push) Has been cancelled
ci / check_changes (push) Has been cancelled
ci / build_mem0 (3.10) (push) Has been cancelled
ci / build_mem0 (3.11) (push) Has been cancelled
ci / build_mem0 (3.12) (push) Has been cancelled
CLI Node CI / lint (push) Has been cancelled
CLI Node CI / test (20) (push) Has been cancelled
CLI Node CI / test (22) (push) Has been cancelled
CLI Node CI / build (push) Has been cancelled
CLI Python CI / lint (push) Has been cancelled
CLI Python CI / test (3.10) (push) Has been cancelled
CLI Python CI / test (3.11) (push) Has been cancelled
CLI Python CI / test (3.12) (push) Has been cancelled
CLI Python CI / build (push) Has been cancelled
openclaw checks / lint (push) Has been cancelled
openclaw checks / test (20) (push) Has been cancelled
openclaw checks / test (22) (push) Has been cancelled
openclaw checks / build (push) Has been cancelled
opencode-plugin checks / build (push) Has been cancelled
TypeScript SDK CI / build_ts_sdk (20) (push) Has been cancelled
TypeScript SDK CI / build_ts_sdk (22) (push) Has been cancelled
TypeScript SDK CI / integration_ts_sdk (20) (push) Has been cancelled
TypeScript SDK CI / integration_ts_sdk (22) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:03:45 +08:00

94 lines
2.6 KiB
Python

import uuid
from datetime import datetime, timezone
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy import select
from sqlalchemy.orm import Session
from auth import generate_api_key, require_auth
from db import get_db
from models import APIKey, User
from schemas import MessageResponse
router = APIRouter(prefix="/api-keys", tags=["api-keys"])
class CreateKeyRequest(BaseModel):
label: str
class CreateKeyResponse(BaseModel):
id: str
key: str
label: str
key_prefix: str
created_at: datetime
class KeyListItem(BaseModel):
id: str
label: str
key_prefix: str
created_at: datetime
last_used_at: datetime | None
model_config = {"from_attributes": True}
@router.get("", response_model=list[KeyListItem])
def list_keys(user: User = Depends(require_auth), db: Session = Depends(get_db)):
keys = (
db.execute(
select(APIKey)
.where(APIKey.created_by == user.id, APIKey.revoked_at.is_(None))
.order_by(APIKey.created_at.desc())
)
.scalars()
.all()
)
return [
KeyListItem(
id=str(k.id),
label=k.label,
key_prefix=k.key_prefix,
created_at=k.created_at,
last_used_at=k.last_used_at,
)
for k in keys
]
@router.post("", response_model=CreateKeyResponse, status_code=201)
def create_key(body: CreateKeyRequest, user: User = Depends(require_auth), db: Session = Depends(get_db)):
full_key, prefix, key_hash = generate_api_key()
api_key = APIKey(key_prefix=prefix, key_hash=key_hash, label=body.label, created_by=user.id)
db.add(api_key)
db.commit()
db.refresh(api_key)
return CreateKeyResponse(
id=str(api_key.id),
key=full_key,
label=api_key.label,
key_prefix=prefix,
created_at=api_key.created_at,
)
@router.delete("/{key_id}", response_model=MessageResponse)
def revoke_key(key_id: str, user: User = Depends(require_auth), db: Session = Depends(get_db)):
try:
key_uuid = uuid.UUID(key_id)
except (TypeError, ValueError):
raise HTTPException(status_code=404, detail="API key not found.")
api_key = db.get(APIKey, key_uuid)
if api_key is None or api_key.created_by != user.id:
raise HTTPException(status_code=404, detail="API key not found.")
if api_key.revoked_at is not None:
raise HTTPException(status_code=400, detail="API key is already revoked.")
api_key.revoked_at = datetime.now(timezone.utc)
db.commit()
return MessageResponse(message="API key revoked.")