api_keys.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import logging
  2. from fastapi import APIRouter, Depends, HTTPException
  3. from sqlalchemy.ext.asyncio import AsyncSession
  4. from sqlalchemy import select
  5. from backend.app.core.database import get_db
  6. from backend.app.core.auth import generate_api_key
  7. from backend.app.models.api_key import APIKey
  8. from backend.app.schemas.api_key import (
  9. APIKeyCreate,
  10. APIKeyUpdate,
  11. APIKeyResponse,
  12. APIKeyCreateResponse,
  13. )
  14. logger = logging.getLogger(__name__)
  15. router = APIRouter(prefix="/api-keys", tags=["api-keys"])
  16. @router.get("/", response_model=list[APIKeyResponse])
  17. async def list_api_keys(db: AsyncSession = Depends(get_db)):
  18. """List all API keys (without full key values)."""
  19. result = await db.execute(
  20. select(APIKey).order_by(APIKey.created_at.desc())
  21. )
  22. return list(result.scalars().all())
  23. @router.post("/", response_model=APIKeyCreateResponse)
  24. async def create_api_key(
  25. data: APIKeyCreate,
  26. db: AsyncSession = Depends(get_db),
  27. ):
  28. """Create a new API key.
  29. IMPORTANT: The full API key is only returned in this response.
  30. Store it securely - it cannot be retrieved again.
  31. """
  32. # Generate the key
  33. full_key, key_hash, key_prefix = generate_api_key()
  34. api_key = APIKey(
  35. name=data.name,
  36. key_hash=key_hash,
  37. key_prefix=key_prefix,
  38. can_queue=data.can_queue,
  39. can_control_printer=data.can_control_printer,
  40. can_read_status=data.can_read_status,
  41. printer_ids=data.printer_ids,
  42. expires_at=data.expires_at,
  43. )
  44. db.add(api_key)
  45. await db.flush()
  46. await db.refresh(api_key)
  47. # Return with full key (only time it's shown)
  48. return APIKeyCreateResponse(
  49. id=api_key.id,
  50. name=api_key.name,
  51. key_prefix=api_key.key_prefix,
  52. key=full_key, # Only returned on creation
  53. can_queue=api_key.can_queue,
  54. can_control_printer=api_key.can_control_printer,
  55. can_read_status=api_key.can_read_status,
  56. printer_ids=api_key.printer_ids,
  57. enabled=api_key.enabled,
  58. last_used=api_key.last_used,
  59. created_at=api_key.created_at,
  60. expires_at=api_key.expires_at,
  61. )
  62. @router.get("/{key_id}", response_model=APIKeyResponse)
  63. async def get_api_key(
  64. key_id: int,
  65. db: AsyncSession = Depends(get_db),
  66. ):
  67. """Get an API key by ID."""
  68. result = await db.execute(select(APIKey).where(APIKey.id == key_id))
  69. api_key = result.scalar_one_or_none()
  70. if not api_key:
  71. raise HTTPException(status_code=404, detail="API key not found")
  72. return api_key
  73. @router.patch("/{key_id}", response_model=APIKeyResponse)
  74. async def update_api_key(
  75. key_id: int,
  76. data: APIKeyUpdate,
  77. db: AsyncSession = Depends(get_db),
  78. ):
  79. """Update an API key."""
  80. result = await db.execute(select(APIKey).where(APIKey.id == key_id))
  81. api_key = result.scalar_one_or_none()
  82. if not api_key:
  83. raise HTTPException(status_code=404, detail="API key not found")
  84. # Update fields if provided
  85. if data.name is not None:
  86. api_key.name = data.name
  87. if data.can_queue is not None:
  88. api_key.can_queue = data.can_queue
  89. if data.can_control_printer is not None:
  90. api_key.can_control_printer = data.can_control_printer
  91. if data.can_read_status is not None:
  92. api_key.can_read_status = data.can_read_status
  93. if data.printer_ids is not None:
  94. api_key.printer_ids = data.printer_ids
  95. if data.enabled is not None:
  96. api_key.enabled = data.enabled
  97. if data.expires_at is not None:
  98. api_key.expires_at = data.expires_at
  99. await db.flush()
  100. await db.refresh(api_key)
  101. return api_key
  102. @router.delete("/{key_id}")
  103. async def delete_api_key(
  104. key_id: int,
  105. db: AsyncSession = Depends(get_db),
  106. ):
  107. """Delete (revoke) an API key."""
  108. result = await db.execute(select(APIKey).where(APIKey.id == key_id))
  109. api_key = result.scalar_one_or_none()
  110. if not api_key:
  111. raise HTTPException(status_code=404, detail="API key not found")
  112. await db.delete(api_key)
  113. return {"message": "API key deleted"}