# app/api/v1/admin/settings.py """ Platform settings management endpoints. Provides endpoints for: - Viewing all platform settings - Creating/updating settings - Managing configuration by category """ import logging from fastapi import APIRouter, Depends, Query from sqlalchemy.orm import Session from app.api.deps import get_current_admin_api from app.core.database import get_db from app.exceptions import ConfirmationRequiredException, ResourceNotFoundException from app.services.admin_audit_service import admin_audit_service from app.services.admin_settings_service import admin_settings_service from models.database.user import User from models.schema.admin import ( AdminSettingCreate, AdminSettingListResponse, AdminSettingResponse, AdminSettingUpdate, PublicDisplaySettingsResponse, RowsPerPageResponse, RowsPerPageUpdateResponse, ) router = APIRouter(prefix="/settings") logger = logging.getLogger(__name__) @router.get("", response_model=AdminSettingListResponse) def get_all_settings( category: str | None = Query(None, description="Filter by category"), is_public: bool | None = Query(None, description="Filter by public flag"), db: Session = Depends(get_db), current_admin: User = Depends(get_current_admin_api), ): """ Get all platform settings. Can be filtered by category (system, security, marketplace, notifications) and by public flag (settings that can be exposed to frontend). """ settings = admin_settings_service.get_all_settings(db, category, is_public) return AdminSettingListResponse( settings=settings, total=len(settings), category=category ) @router.get("/categories") def get_setting_categories( db: Session = Depends(get_db), current_admin: User = Depends(get_current_admin_api), ): """Get list of all setting categories.""" # This could be enhanced to return counts per category return { "categories": [ "system", "security", "marketplace", "notifications", "integrations", "payments", ] } @router.get("/{key}", response_model=AdminSettingResponse) def get_setting( key: str, db: Session = Depends(get_db), current_admin: User = Depends(get_current_admin_api), ): """Get specific setting by key.""" setting = admin_settings_service.get_setting_by_key(db, key) if not setting: raise ResourceNotFoundException(resource_type="Setting", identifier=key) return AdminSettingResponse.model_validate(setting) @router.post("", response_model=AdminSettingResponse) def create_setting( setting_data: AdminSettingCreate, db: Session = Depends(get_db), current_admin: User = Depends(get_current_admin_api), ): """ Create new platform setting. Setting keys should be lowercase with underscores (e.g., max_vendors_allowed). """ result = admin_settings_service.create_setting( db=db, setting_data=setting_data, admin_user_id=current_admin.id ) # Log action admin_audit_service.log_action( db=db, admin_user_id=current_admin.id, action="create_setting", target_type="setting", target_id=setting_data.key, details={ "category": setting_data.category, "value_type": setting_data.value_type, }, ) db.commit() return result @router.put("/{key}", response_model=AdminSettingResponse) def update_setting( key: str, update_data: AdminSettingUpdate, db: Session = Depends(get_db), current_admin: User = Depends(get_current_admin_api), ): """Update existing setting value.""" old_value = admin_settings_service.get_setting_value(db, key) result = admin_settings_service.update_setting( db=db, key=key, update_data=update_data, admin_user_id=current_admin.id ) # Log action admin_audit_service.log_action( db=db, admin_user_id=current_admin.id, action="update_setting", target_type="setting", target_id=key, details={"old_value": str(old_value), "new_value": update_data.value}, ) db.commit() return result @router.post("/upsert", response_model=AdminSettingResponse) def upsert_setting( setting_data: AdminSettingCreate, db: Session = Depends(get_db), current_admin: User = Depends(get_current_admin_api), ): """ Create or update setting (upsert). If setting exists, updates its value. If not, creates new setting. """ result = admin_settings_service.upsert_setting( db=db, setting_data=setting_data, admin_user_id=current_admin.id ) # Log action admin_audit_service.log_action( db=db, admin_user_id=current_admin.id, action="upsert_setting", target_type="setting", target_id=setting_data.key, details={"category": setting_data.category}, ) db.commit() return result # ============================================================================ # CONVENIENCE ENDPOINTS FOR COMMON SETTINGS # ============================================================================ @router.get("/display/rows-per-page", response_model=RowsPerPageResponse) def get_rows_per_page( db: Session = Depends(get_db), current_admin: User = Depends(get_current_admin_api), ) -> RowsPerPageResponse: """Get the platform-wide rows per page setting.""" value = admin_settings_service.get_setting_value(db, "rows_per_page", default="20") return RowsPerPageResponse(rows_per_page=int(value)) @router.put("/display/rows-per-page", response_model=RowsPerPageUpdateResponse) def set_rows_per_page( rows: int = Query(..., ge=10, le=100, description="Rows per page (10-100)"), db: Session = Depends(get_db), current_admin: User = Depends(get_current_admin_api), ) -> RowsPerPageUpdateResponse: """ Set the platform-wide rows per page setting. Valid values: 10, 20, 50, 100 """ valid_values = [10, 20, 50, 100] if rows not in valid_values: # Round to nearest valid value rows = min(valid_values, key=lambda x: abs(x - rows)) setting_data = AdminSettingCreate( key="rows_per_page", value=str(rows), value_type="integer", category="display", description="Default number of rows per page in admin tables", is_public=True, ) admin_settings_service.upsert_setting( db=db, setting_data=setting_data, admin_user_id=current_admin.id ) admin_audit_service.log_action( db=db, admin_user_id=current_admin.id, action="update_setting", target_type="setting", target_id="rows_per_page", details={"value": rows}, ) db.commit() return RowsPerPageUpdateResponse( rows_per_page=rows, message="Rows per page setting updated" ) @router.get("/display/public", response_model=PublicDisplaySettingsResponse) def get_public_display_settings( db: Session = Depends(get_db), ) -> PublicDisplaySettingsResponse: """ Get public display settings (no auth required). Returns settings that can be used by frontend without admin auth. """ rows_per_page = admin_settings_service.get_setting_value( db, "rows_per_page", default="20" ) return PublicDisplaySettingsResponse(rows_per_page=int(rows_per_page)) @router.delete("/{key}") def delete_setting( key: str, confirm: bool = Query(False, description="Must be true to confirm deletion"), db: Session = Depends(get_db), current_admin: User = Depends(get_current_admin_api), ): """ Delete platform setting. Requires confirmation parameter. WARNING: Deleting settings may affect platform functionality. """ if not confirm: raise ConfirmationRequiredException( operation="delete_setting", message="Deletion requires confirmation parameter: confirm=true", ) message = admin_settings_service.delete_setting( db=db, key=key, admin_user_id=current_admin.id ) # Log action admin_audit_service.log_action( db=db, admin_user_id=current_admin.id, action="delete_setting", target_type="setting", target_id=key, details={}, ) db.commit() return {"message": message}