From abf8b89d7c6d9474c32badcf16a19e380957bff2 Mon Sep 17 00:00:00 2001 From: Ivo Oskamp Date: Wed, 4 Feb 2026 22:03:11 +0100 Subject: [PATCH 1/2] Add setting to enable/disable daily dashboard redirect Added a new setting in Settings > General > Navigation that allows administrators to control whether users are redirected to the dashboard on their first page view each day. Changes: - Added require_daily_dashboard_visit column to SystemSettings model - Added migration for the new column (default: FALSE) - Modified before_request hook to check the setting before redirecting - Added Navigation card with toggle in Settings General page - Restored changelog-claude.md with performance and feature updates Default is OFF - users can navigate directly to any page without being forced to visit the dashboard first. Co-Authored-By: Claude Opus 4.5 --- .../backupchecks/src/backend/app/__init__.py | 15 ++++ .../src/backend/app/main/routes_settings.py | 4 + .../src/backend/app/migrations.py | 74 +++++++++++++++++++ .../backupchecks/src/backend/app/models.py | 5 ++ .../src/templates/main/settings.html | 11 +++ docs/changelog-claude.md | 32 ++++++++ 6 files changed, 141 insertions(+) create mode 100644 docs/changelog-claude.md diff --git a/containers/backupchecks/src/backend/app/__init__.py b/containers/backupchecks/src/backend/app/__init__.py index 2b54a76..43857f0 100644 --- a/containers/backupchecks/src/backend/app/__init__.py +++ b/containers/backupchecks/src/backend/app/__init__.py @@ -69,6 +69,9 @@ def create_app(): This ensures that when a user opens the site for the first time each day, they land on the dashboard regardless of the bookmarked/deeplinked URL. + + This behavior is controlled by the system setting `require_daily_dashboard_visit`. + When disabled (the default), users can navigate directly to any page. """ # Only for normal page loads. @@ -99,6 +102,18 @@ def create_app(): session["daily_dashboard_seen"] = _get_today_ui_date() return None + # Check if the feature is enabled in system settings. + try: + from .models import SystemSettings + + settings = SystemSettings.query.first() + if not settings or not getattr(settings, "require_daily_dashboard_visit", False): + # Feature is disabled; skip redirect. + return None + except Exception: + # On any error (e.g. column doesn't exist yet), skip redirect. + return None + today = _get_today_ui_date() seen = (session.get("daily_dashboard_seen") or "").strip() if seen != today: diff --git a/containers/backupchecks/src/backend/app/main/routes_settings.py b/containers/backupchecks/src/backend/app/main/routes_settings.py index 7018135..905f61e 100644 --- a/containers/backupchecks/src/backend/app/main/routes_settings.py +++ b/containers/backupchecks/src/backend/app/main/routes_settings.py @@ -430,6 +430,10 @@ def settings(): if "ui_timezone" in request.form: settings.ui_timezone = (request.form.get("ui_timezone") or "").strip() or "Europe/Amsterdam" + # Navigation setting is in the same form (General tab), so process it here. + # Checkbox: present in form = checked, absent = unchecked. + settings.require_daily_dashboard_visit = bool(request.form.get("require_daily_dashboard_visit")) + # Daily Jobs if "daily_jobs_start_date" in request.form: daily_jobs_start_date_str = (request.form.get("daily_jobs_start_date") or "").strip() diff --git a/containers/backupchecks/src/backend/app/migrations.py b/containers/backupchecks/src/backend/app/migrations.py index 334be39..a8e9976 100644 --- a/containers/backupchecks/src/backend/app/migrations.py +++ b/containers/backupchecks/src/backend/app/migrations.py @@ -801,6 +801,8 @@ def run_migrations() -> None: migrate_news_tables() migrate_reporting_tables() migrate_reporting_report_config() + migrate_performance_indexes() + migrate_system_settings_require_daily_dashboard_visit() print("[migrations] All migrations completed.") @@ -844,6 +846,78 @@ def migrate_jobs_archiving() -> None: print("[migrations] migrate_jobs_archiving completed.") +def migrate_system_settings_require_daily_dashboard_visit() -> None: + """Add require_daily_dashboard_visit column to system_settings if missing. + + When enabled, authenticated users are redirected to the dashboard on + their first page view each day. + """ + table = "system_settings" + column = "require_daily_dashboard_visit" + + try: + engine = db.get_engine() + except Exception as exc: + print(f"[migrations] Could not get engine for system_settings require_daily_dashboard_visit migration: {exc}") + return + + try: + if _column_exists(table, column): + print("[migrations] system_settings.require_daily_dashboard_visit already exists.") + return + + with engine.begin() as conn: + conn.execute( + text( + f'ALTER TABLE "{table}" ADD COLUMN {column} BOOLEAN NOT NULL DEFAULT FALSE' + ) + ) + + print("[migrations] migrate_system_settings_require_daily_dashboard_visit completed.") + except Exception as exc: + print(f"[migrations] Failed to migrate system_settings.require_daily_dashboard_visit: {exc}") + + +def migrate_performance_indexes() -> None: + """Add performance indexes for frequently queried foreign key columns. + + These indexes significantly improve query performance on slow storage, + especially for Daily Jobs and Run Checks pages. + + This migration is idempotent. + """ + try: + engine = db.get_engine() + except Exception as exc: + print(f"[migrations] Could not get engine for performance indexes migration: {exc}") + return + + with engine.begin() as conn: + # JobRun indexes + conn.execute(text('CREATE INDEX IF NOT EXISTS idx_job_run_job_id ON job_runs (job_id)')) + conn.execute(text('CREATE INDEX IF NOT EXISTS idx_job_run_job_id_run_at ON job_runs (job_id, run_at)')) + conn.execute(text('CREATE INDEX IF NOT EXISTS idx_job_run_job_id_reviewed_at ON job_runs (job_id, reviewed_at)')) + conn.execute(text('CREATE INDEX IF NOT EXISTS idx_job_run_mail_message_id ON job_runs (mail_message_id)')) + + # MailMessage indexes + conn.execute(text('CREATE INDEX IF NOT EXISTS idx_mail_message_job_id ON mail_messages (job_id)')) + conn.execute(text('CREATE INDEX IF NOT EXISTS idx_mail_message_location ON mail_messages (location)')) + conn.execute(text('CREATE INDEX IF NOT EXISTS idx_mail_message_job_id_location ON mail_messages (job_id, location)')) + + # MailObject indexes + conn.execute(text('CREATE INDEX IF NOT EXISTS idx_mail_object_mail_message_id ON mail_objects (mail_message_id)')) + + # TicketScope indexes + conn.execute(text('CREATE INDEX IF NOT EXISTS idx_ticket_scope_ticket_id ON ticket_scopes (ticket_id)')) + conn.execute(text('CREATE INDEX IF NOT EXISTS idx_ticket_scope_job_id ON ticket_scopes (job_id)')) + + # RemarkScope indexes + conn.execute(text('CREATE INDEX IF NOT EXISTS idx_remark_scope_remark_id ON remark_scopes (remark_id)')) + conn.execute(text('CREATE INDEX IF NOT EXISTS idx_remark_scope_job_id ON remark_scopes (job_id)')) + + print("[migrations] migrate_performance_indexes completed.") + + def migrate_reporting_report_config() -> None: """Add report_definitions.report_config column if missing. diff --git a/containers/backupchecks/src/backend/app/models.py b/containers/backupchecks/src/backend/app/models.py index 3d23da6..fb9329f 100644 --- a/containers/backupchecks/src/backend/app/models.py +++ b/containers/backupchecks/src/backend/app/models.py @@ -107,6 +107,11 @@ class SystemSettings(db.Model): # UI display timezone (IANA name). Used for rendering times in the web interface. ui_timezone = db.Column(db.String(64), nullable=False, default="Europe/Amsterdam") + # Navigation behavior: require visiting dashboard first each day. + # When enabled, authenticated users are redirected to the dashboard on + # their first page view each day before they can navigate elsewhere. + require_daily_dashboard_visit = db.Column(db.Boolean, nullable=False, default=False) + created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) updated_at = db.Column( db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False diff --git a/containers/backupchecks/src/templates/main/settings.html b/containers/backupchecks/src/templates/main/settings.html index bdc5fbe..7bb609c 100644 --- a/containers/backupchecks/src/templates/main/settings.html +++ b/containers/backupchecks/src/templates/main/settings.html @@ -136,6 +136,17 @@ +
+
Navigation
+
+
+ + +
+
When enabled, users are redirected to the dashboard on their first page view each day, regardless of which page they try to access.
+
+
+
diff --git a/docs/changelog-claude.md b/docs/changelog-claude.md new file mode 100644 index 0000000..1c12ed3 --- /dev/null +++ b/docs/changelog-claude.md @@ -0,0 +1,32 @@ +# Changelog - Claude Code + +This file documents all changes made to this project via Claude Code. + +## [2026-02-04] + +### Added +- `docs/changelog-claude.md` - Changelog file for tracking changes made via Claude Code +- Setting to enable/disable daily dashboard redirect requirement (Settings > General > Navigation) + - New `require_daily_dashboard_visit` column in `SystemSettings` model + - Migration in `migrations.py` to add the column + - Toggle in Settings General page under new "Navigation" card + - Default value is OFF (disabled) - users can navigate directly to any page + +### Changed +- Converted changelog to English (all project documentation must be in English) +- Documented branch naming convention and build workflow in Claude memory +- Filled README.md with comprehensive project documentation based on source code analysis + +### Performance +- Added database indexes migration (`migrations.py`) for frequently queried foreign key columns: + - `JobRun`: indexes on `job_id`, `job_id+run_at`, `job_id+reviewed_at`, `mail_message_id` + - `MailMessage`: indexes on `job_id`, `location`, `job_id+location` + - `MailObject`: index on `mail_message_id` + - `TicketScope`: indexes on `ticket_id`, `job_id` + - `RemarkScope`: indexes on `remark_id`, `job_id` +- Fixed N+1 query in `_recompute_override_flags_for_runs()` - batch loads all jobs instead of per-run queries +- Optimized Daily Jobs page with batch queries: + - Batch load all today's runs for all jobs in single query + - Batch infer weekly schedules for all jobs (was per-job query) + - Batch infer monthly schedules for jobs without weekly schedule + - Batch load ticket/remark indicators for all jobs From 4b66ec1c6a3be0c08ee004165d782dd86654d4e6 Mon Sep 17 00:00:00 2001 From: Ivo Oskamp Date: Wed, 4 Feb 2026 22:03:24 +0100 Subject: [PATCH 2/2] Auto-commit local changes before build (2026-02-04 22:03:24) --- .last-branch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.last-branch b/.last-branch index 2da1383..f35a538 100644 --- a/.last-branch +++ b/.last-branch @@ -1 +1 @@ -v20260113-08-vspc-object-linking-normalize +v20260204-03-dashboard-redirect-setting