Files
gh-christianlouis-docuelevate/app/views/wizard.py
2026-02-23 10:01:07 +00:00

175 lines
5.9 KiB
Python

"""
Setup wizard views for initial system configuration.
"""
import logging
from secrets import token_hex
from fastapi import Depends, Form, Request
from fastapi.responses import RedirectResponse
from sqlalchemy.orm import Session
from app.utils.settings_service import save_setting_to_db
from app.utils.settings_sync import notify_settings_updated
from app.utils.setup_wizard import get_wizard_steps
from app.views.base import APIRouter, get_db, templates
logger = logging.getLogger(__name__)
router = APIRouter()
@router.get("/setup")
async def setup_wizard(request: Request, step: int = 1, db: Session = Depends(get_db)):
"""
Setup wizard for first-time configuration.
This wizard guides users through configuring essential settings
needed for the system to operate properly.
"""
# Get wizard steps
wizard_steps = get_wizard_steps()
max_step = max(wizard_steps.keys())
# Validate step number
if step < 1:
step = 1
elif step > max_step:
step = max_step
# Get settings for current step
current_settings = wizard_steps.get(step, [])
# Get step category (all settings in a step should have same category)
step_category = current_settings[0].get("wizard_category", "Configuration") if current_settings else "Configuration"
# Enrich settings with current live values
from app.config import settings as app_settings
from app.utils.settings_service import get_setting_from_db
enriched_settings = []
for s in current_settings:
key = s["key"]
db_val = get_setting_from_db(db, key)
env_val = getattr(app_settings, key, None)
# Determine current_value and source
if db_val is not None:
current_value = db_val
value_source = "db"
elif env_val is not None and str(env_val).strip():
current_value = str(env_val)
value_source = "env"
elif s.get("default") is not None:
current_value = s["default"]
value_source = "default"
else:
current_value = ""
value_source = "none"
enriched_settings.append({**s, "current_value": current_value, "value_source": value_source})
current_settings = enriched_settings
return templates.TemplateResponse(
"setup_wizard.html",
{
"request": request,
"current_step": step,
"max_step": max_step,
"settings": current_settings,
"step_category": step_category,
"progress_percent": int((step / max_step) * 100),
"setup_skipped": bool(get_setting_from_db(db, "_setup_wizard_skipped")),
},
)
@router.post("/setup")
async def setup_wizard_save(request: Request, step: int = Form(...), db: Session = Depends(get_db)):
"""
Save settings from the current wizard step.
"""
try:
# Get form data
form_data = await request.form()
# Get settings for current step
wizard_steps = get_wizard_steps()
current_settings = wizard_steps.get(step, [])
# Save each setting from the form
saved_count = 0
for setting in current_settings:
key = setting["key"]
value = form_data.get(key)
# Skip empty values unless it's explicitly allowed
if value and value.strip():
# Auto-generate session_secret if needed
if key == "session_secret" and value == "auto-generate":
value = token_hex(32)
logger.info("Auto-generated session secret")
# Save to database
if save_setting_to_db(db, key, value):
saved_count += 1
logger.info(f"Setup wizard: Saved {key}")
logger.info(f"Setup wizard step {step}: Saved {saved_count} settings")
if saved_count > 0:
notify_settings_updated()
# Determine next step
max_step = max(wizard_steps.keys())
next_step = step + 1
if next_step > max_step:
# Setup complete, redirect to home
return RedirectResponse(url="/?setup=complete", status_code=303)
else:
# Go to next step
return RedirectResponse(url=f"/setup?step={next_step}", status_code=303)
except Exception as e:
logger.error(f"Error saving wizard settings: {e}")
return RedirectResponse(url=f"/setup?step={step}&error=save_failed", status_code=303)
@router.get("/setup/skip")
async def setup_wizard_skip(request: Request):
"""
Skip the setup wizard (for advanced users).
Creates a marker to indicate setup was skipped.
"""
try:
db = next(get_db())
try:
# Save a marker to indicate setup was skipped
save_setting_to_db(db, "_setup_wizard_skipped", "true")
logger.info("Setup wizard skipped by user")
return RedirectResponse(url="/", status_code=303)
finally:
db.close()
except Exception as e:
logger.error(f"Error skipping setup wizard: {e}")
return RedirectResponse(url="/", status_code=303)
@router.get("/setup/undo-skip")
async def setup_wizard_undo_skip(request: Request, db: Session = Depends(get_db)):
"""
Undo a previously skipped setup wizard.
Removes the skip marker from the database so the wizard will be
presented again on next visit to the home page. Redirects to
step 1 of the wizard immediately.
"""
try:
from app.utils.settings_service import delete_setting_from_db
delete_setting_from_db(db, "_setup_wizard_skipped", changed_by="wizard_undo_skip")
logger.info("Setup wizard skip marker removed; redirecting to wizard")
return RedirectResponse(url="/setup?step=1", status_code=303)
except Exception as e:
logger.error(f"Error undoing setup wizard skip: {e}")
return RedirectResponse(url="/settings", status_code=303)