Files
microdao-daarion/services/auth-service/routes_api_keys.py
Apple fca48b3eb0 feat(node2): Complete NODE2 setup - guardian, agents, swapper models
- Node-guardian running on MacBook and updating metrics
- NODE2 agents (Atlas, Greeter, Oracle, Builder Bot) assigned to node-2-macbook-m4max
- Swapper models displaying correctly (8 models)
- DAGI Router agents showing with correct status (3 active, 1 stale)
- Router health check using node_cache for remote nodes
2025-12-02 07:07:58 -08:00

131 lines
3.5 KiB
Python

"""
API Key management routes
"""
from fastapi import APIRouter, HTTPException, Depends
import asyncpg
from datetime import datetime, timedelta, timezone
import secrets
from models import ApiKeyCreateRequest, ApiKey, ApiKeyResponse, ActorIdentity
from actor_context import require_actor
import json
router = APIRouter(prefix="/auth/api-keys", tags=["api_keys"])
def get_db_pool(request) -> asyncpg.Pool:
"""Get database pool from app state"""
return request.app.state.db_pool
@router.post("", response_model=ApiKey)
async def create_api_key(
request: ApiKeyCreateRequest,
actor: ActorIdentity = Depends(require_actor),
db_pool: asyncpg.Pool = Depends(get_db_pool)
):
"""
Create new API key for current actor
Returns full key only once (on creation)
"""
# Generate key
key = f"dk_{secrets.token_urlsafe(32)}"
key_id = secrets.token_urlsafe(16)
# Calculate expiration
expires_at = None
if request.expires_days:
expires_at = datetime.now(timezone.utc) + timedelta(days=request.expires_days)
# Store in database
async with db_pool.acquire() as conn:
await conn.execute(
"""
INSERT INTO api_keys (id, key, actor_id, actor_data, description, expires_at)
VALUES ($1, $2, $3, $4, $5, $6)
""",
key_id,
key,
actor.actor_id,
json.dumps(actor.model_dump()),
request.description,
expires_at
)
return ApiKey(
id=key_id,
key=key, # Full key shown only once
actor_id=actor.actor_id,
actor=actor,
description=request.description,
created_at=datetime.now(timezone.utc),
expires_at=expires_at,
last_used=None,
is_active=True
)
@router.get("", response_model=list[ApiKeyResponse])
async def list_api_keys(
actor: ActorIdentity = Depends(require_actor),
db_pool: asyncpg.Pool = Depends(get_db_pool)
):
"""List all API keys for current actor"""
async with db_pool.acquire() as conn:
rows = await conn.fetch(
"""
SELECT id, key, description, created_at, expires_at, last_used, is_active
FROM api_keys
WHERE actor_id = $1
ORDER BY created_at DESC
""",
actor.actor_id
)
keys = []
for row in rows:
# Show only preview of key
key_preview = row['key'][:8] + "..." if len(row['key']) > 8 else row['key']
keys.append(ApiKeyResponse(
id=row['id'],
key_preview=key_preview,
description=row['description'],
created_at=row['created_at'],
expires_at=row['expires_at'],
last_used=row['last_used'],
is_active=row['is_active']
))
return keys
@router.delete("/{key_id}")
async def delete_api_key(
key_id: str,
actor: ActorIdentity = Depends(require_actor),
db_pool: asyncpg.Pool = Depends(get_db_pool)
):
"""Delete (deactivate) API key"""
async with db_pool.acquire() as conn:
result = await conn.execute(
"""
UPDATE api_keys
SET is_active = false
WHERE id = $1 AND actor_id = $2
""",
key_id,
actor.actor_id
)
if result == "UPDATE 0":
raise HTTPException(404, "API key not found")
return {"status": "deleted", "key_id": key_id}