settings.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. """
  2. Superadmin endpoints for system settings.
  3. """
  4. from datetime import datetime, timedelta, timezone
  5. from typing import Annotated
  6. from fastapi import APIRouter, Depends, HTTPException, status
  7. from pydantic import BaseModel
  8. from sqlalchemy import select
  9. from sqlalchemy.ext.asyncio import AsyncSession
  10. from app.api.deps import get_current_superadmin
  11. from app.core.database import get_db
  12. from app.models.settings import Settings
  13. from app.models.user import User
  14. router = APIRouter()
  15. class AutoRegistrationResponse(BaseModel):
  16. """Auto-registration status response."""
  17. enabled: bool
  18. last_device_at: str | None
  19. class AutoRegistrationToggleRequest(BaseModel):
  20. """Auto-registration toggle request."""
  21. enabled: bool
  22. @router.get("/auto-registration", response_model=AutoRegistrationResponse)
  23. async def get_auto_registration_status(
  24. db: Annotated[AsyncSession, Depends(get_db)],
  25. current_user: Annotated[User, Depends(get_current_superadmin)],
  26. ):
  27. """
  28. Get auto-registration status.
  29. Returns current state and last device registration timestamp.
  30. """
  31. result = await db.execute(
  32. select(Settings).where(Settings.key == "auto_registration")
  33. )
  34. setting = result.scalar_one_or_none()
  35. if not setting:
  36. # Create default setting if not exists
  37. setting = Settings(
  38. key="auto_registration",
  39. value={"enabled": False, "last_device_at": None},
  40. )
  41. db.add(setting)
  42. await db.commit()
  43. # Check if should auto-disable (1 hour since last device)
  44. enabled = setting.value.get("enabled", False)
  45. last_device_at_str = setting.value.get("last_device_at")
  46. if enabled and last_device_at_str:
  47. last_device_at = datetime.fromisoformat(last_device_at_str)
  48. if datetime.now(timezone.utc) - last_device_at > timedelta(hours=1):
  49. # Auto-disable
  50. setting.value["enabled"] = False
  51. await db.commit()
  52. enabled = False
  53. return AutoRegistrationResponse(
  54. enabled=enabled,
  55. last_device_at=last_device_at_str,
  56. )
  57. @router.post("/auto-registration", response_model=AutoRegistrationResponse)
  58. async def toggle_auto_registration(
  59. data: AutoRegistrationToggleRequest,
  60. db: Annotated[AsyncSession, Depends(get_db)],
  61. current_user: Annotated[User, Depends(get_current_superadmin)],
  62. ):
  63. """
  64. Enable or disable auto-registration of new devices.
  65. When enabled, devices that authenticate with unknown MAC addresses will be
  66. automatically registered in the system (unassigned to any organization).
  67. Auto-registration will automatically disable after 1 hour of the last registered device.
  68. """
  69. result = await db.execute(
  70. select(Settings).where(Settings.key == "auto_registration")
  71. )
  72. setting = result.scalar_one_or_none()
  73. if not setting:
  74. setting = Settings(
  75. key="auto_registration",
  76. value={"enabled": data.enabled, "last_device_at": None},
  77. )
  78. db.add(setting)
  79. else:
  80. setting.value["enabled"] = data.enabled
  81. if not data.enabled:
  82. # Reset last_device_at when disabling
  83. setting.value["last_device_at"] = None
  84. await db.commit()
  85. return AutoRegistrationResponse(
  86. enabled=setting.value["enabled"],
  87. last_device_at=setting.value.get("last_device_at"),
  88. )
  89. class SettingUpdate(BaseModel):
  90. """Generic setting update request."""
  91. value: dict
  92. class SettingResponse(BaseModel):
  93. """Generic setting response."""
  94. key: str
  95. value: dict
  96. updated_at: str | None
  97. @router.get("/setting/{key}", response_model=SettingResponse)
  98. async def get_setting(
  99. key: str,
  100. db: Annotated[AsyncSession, Depends(get_db)],
  101. current_user: Annotated[User, Depends(get_current_superadmin)],
  102. ):
  103. """Get a specific setting by key."""
  104. result = await db.execute(select(Settings).where(Settings.key == key))
  105. setting = result.scalar_one_or_none()
  106. if not setting:
  107. raise HTTPException(
  108. status_code=status.HTTP_404_NOT_FOUND,
  109. detail=f"setting_not_found:{key}",
  110. )
  111. return SettingResponse(
  112. key=setting.key,
  113. value=setting.value,
  114. updated_at=setting.updated_at.isoformat() if setting.updated_at else None,
  115. )
  116. @router.put("/setting/{key}", response_model=SettingResponse)
  117. async def update_setting(
  118. key: str,
  119. data: SettingUpdate,
  120. db: Annotated[AsyncSession, Depends(get_db)],
  121. current_user: Annotated[User, Depends(get_current_superadmin)],
  122. ):
  123. """Update a specific setting by key."""
  124. result = await db.execute(select(Settings).where(Settings.key == key))
  125. setting = result.scalar_one_or_none()
  126. if not setting:
  127. raise HTTPException(
  128. status_code=status.HTTP_404_NOT_FOUND,
  129. detail=f"setting_not_found:{key}",
  130. )
  131. setting.value = data.value
  132. setting.updated_at = datetime.now(timezone.utc)
  133. await db.commit()
  134. await db.refresh(setting)
  135. return SettingResponse(
  136. key=setting.key,
  137. value=setting.value,
  138. updated_at=setting.updated_at.isoformat(),
  139. )