mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
Add a Feature Flags card to the Settings page that toggles env-gated subsystems (external/internal PR review, web research, strategy engine, pitch provisioning, RAG auto-update, transcript pruning) directly from the panel instead of hand-editing environment variables. Flags persist in system_settings as 'true'/'false' and are overlaid onto the live config singleton at startup; an unset flag keeps its environment/config default. A toggle takes effect on the next backend restart — no per-consumer re-routing. Backend: FEATURE_FLAGS registry + bool validator + get_bool accessor on SettingsService; feature_flag_effective_values and apply_persisted_feature_flags; GET /settings/feature-flags; best-effort startup overlay in the app lifespan. Frontend: settingsApi.getFeatureFlags / setFeatureFlag and a FeatureFlagsCard rendered full-width below the settings grid.
60 lines
2.0 KiB
Python
60 lines
2.0 KiB
Python
"""System settings API — read and update runtime-editable settings.
|
|
|
|
Backs the panel settings page. Values persist in the ``system_settings`` table
|
|
and are read by the backend (e.g. the transcript-retention prune sweep) with a
|
|
``roboco.config`` default as the fallback when a key is unset.
|
|
"""
|
|
|
|
from fastapi import APIRouter, HTTPException, status
|
|
|
|
from roboco.api.deps import DbSession
|
|
from roboco.api.schemas.settings import (
|
|
FeatureFlag,
|
|
FeatureFlagsResponse,
|
|
SettingsResponse,
|
|
SettingUpdate,
|
|
)
|
|
from roboco.services.settings import (
|
|
FEATURE_FLAGS,
|
|
SettingValidationError,
|
|
feature_flag_effective_values,
|
|
get_settings_service,
|
|
)
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("", response_model=SettingsResponse)
|
|
async def list_settings(db: DbSession) -> SettingsResponse:
|
|
"""Return every stored runtime-editable setting."""
|
|
return SettingsResponse(settings=await get_settings_service(db).all())
|
|
|
|
|
|
@router.get("/feature-flags", response_model=FeatureFlagsResponse)
|
|
async def get_feature_flags(db: DbSession) -> FeatureFlagsResponse:
|
|
"""Effective feature-flag values (stored override, else the env default)."""
|
|
effective = await feature_flag_effective_values(db)
|
|
return FeatureFlagsResponse(
|
|
flags=[
|
|
FeatureFlag(key=key, label=label, enabled=effective.get(key, False))
|
|
for key, label in FEATURE_FLAGS
|
|
]
|
|
)
|
|
|
|
|
|
@router.put("/{key}", response_model=SettingsResponse)
|
|
async def update_setting(
|
|
key: str, data: SettingUpdate, db: DbSession
|
|
) -> SettingsResponse:
|
|
"""Validate and persist a single setting, returning the full updated map."""
|
|
service = get_settings_service(db)
|
|
try:
|
|
await service.set(key, data.value)
|
|
except SettingValidationError as exc:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, detail=str(exc)
|
|
) from exc
|
|
# Write route commits explicitly (get_db auto-commit is unreliable).
|
|
await db.commit()
|
|
return SettingsResponse(settings=await service.all())
|