settings.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import shutil
  2. from pathlib import Path
  3. from fastapi import APIRouter, Depends
  4. from sqlalchemy.ext.asyncio import AsyncSession
  5. from sqlalchemy import select
  6. from backend.app.core.database import get_db
  7. from backend.app.models.settings import Settings
  8. from backend.app.schemas.settings import AppSettings, AppSettingsUpdate
  9. router = APIRouter(prefix="/settings", tags=["settings"])
  10. # Default settings
  11. DEFAULT_SETTINGS = AppSettings()
  12. async def get_setting(db: AsyncSession, key: str) -> str | None:
  13. """Get a single setting value by key."""
  14. result = await db.execute(select(Settings).where(Settings.key == key))
  15. setting = result.scalar_one_or_none()
  16. return setting.value if setting else None
  17. async def set_setting(db: AsyncSession, key: str, value: str) -> None:
  18. """Set a single setting value."""
  19. result = await db.execute(select(Settings).where(Settings.key == key))
  20. setting = result.scalar_one_or_none()
  21. if setting:
  22. setting.value = value
  23. else:
  24. setting = Settings(key=key, value=value)
  25. db.add(setting)
  26. @router.get("/", response_model=AppSettings)
  27. async def get_settings(db: AsyncSession = Depends(get_db)):
  28. """Get all application settings."""
  29. settings_dict = DEFAULT_SETTINGS.model_dump()
  30. # Load saved settings from database
  31. result = await db.execute(select(Settings))
  32. db_settings = result.scalars().all()
  33. for setting in db_settings:
  34. if setting.key in settings_dict:
  35. # Parse the value based on the expected type
  36. if setting.key in ["auto_archive", "save_thumbnails", "capture_finish_photo", "spoolman_enabled", "check_updates"]:
  37. settings_dict[setting.key] = setting.value.lower() == "true"
  38. elif setting.key in ["default_filament_cost", "energy_cost_per_kwh"]:
  39. settings_dict[setting.key] = float(setting.value)
  40. else:
  41. settings_dict[setting.key] = setting.value
  42. return AppSettings(**settings_dict)
  43. @router.put("/", response_model=AppSettings)
  44. async def update_settings(
  45. settings_update: AppSettingsUpdate,
  46. db: AsyncSession = Depends(get_db),
  47. ):
  48. """Update application settings."""
  49. update_data = settings_update.model_dump(exclude_unset=True)
  50. for key, value in update_data.items():
  51. # Convert value to string for storage
  52. if isinstance(value, bool):
  53. str_value = "true" if value else "false"
  54. else:
  55. str_value = str(value)
  56. await set_setting(db, key, str_value)
  57. await db.commit()
  58. # Return updated settings
  59. return await get_settings(db)
  60. @router.post("/reset", response_model=AppSettings)
  61. async def reset_settings(db: AsyncSession = Depends(get_db)):
  62. """Reset all settings to defaults."""
  63. # Delete all settings
  64. result = await db.execute(select(Settings))
  65. for setting in result.scalars().all():
  66. await db.delete(setting)
  67. await db.commit()
  68. return DEFAULT_SETTINGS
  69. @router.get("/check-ffmpeg")
  70. async def check_ffmpeg():
  71. """Check if ffmpeg is installed and available."""
  72. ffmpeg_path = shutil.which("ffmpeg")
  73. # If not found via PATH, check common installation locations
  74. # (systemd services often have limited PATH)
  75. if ffmpeg_path is None:
  76. common_paths = [
  77. "/usr/bin/ffmpeg",
  78. "/usr/local/bin/ffmpeg",
  79. "/opt/homebrew/bin/ffmpeg",
  80. "/snap/bin/ffmpeg",
  81. ]
  82. for path in common_paths:
  83. if Path(path).exists():
  84. ffmpeg_path = path
  85. break
  86. return {
  87. "installed": ffmpeg_path is not None,
  88. "path": ffmpeg_path,
  89. }
  90. @router.get("/spoolman")
  91. async def get_spoolman_settings(db: AsyncSession = Depends(get_db)):
  92. """Get Spoolman integration settings."""
  93. spoolman_enabled = await get_setting(db, "spoolman_enabled") or "false"
  94. spoolman_url = await get_setting(db, "spoolman_url") or ""
  95. spoolman_sync_mode = await get_setting(db, "spoolman_sync_mode") or "auto"
  96. return {
  97. "spoolman_enabled": spoolman_enabled,
  98. "spoolman_url": spoolman_url,
  99. "spoolman_sync_mode": spoolman_sync_mode,
  100. }
  101. @router.put("/spoolman")
  102. async def update_spoolman_settings(
  103. settings: dict,
  104. db: AsyncSession = Depends(get_db),
  105. ):
  106. """Update Spoolman integration settings."""
  107. if "spoolman_enabled" in settings:
  108. await set_setting(db, "spoolman_enabled", settings["spoolman_enabled"])
  109. if "spoolman_url" in settings:
  110. await set_setting(db, "spoolman_url", settings["spoolman_url"])
  111. if "spoolman_sync_mode" in settings:
  112. await set_setting(db, "spoolman_sync_mode", settings["spoolman_sync_mode"])
  113. await db.commit()
  114. # Return updated settings
  115. return await get_spoolman_settings(db)