From a78df488045289f6c9f91eab0b1c183cbb498a7d Mon Sep 17 00:00:00 2001 From: Matthew Grotke Date: Mon, 25 May 2026 13:49:23 -0400 Subject: [PATCH] Development --- .../{action_general.py => action_actions.py} | 21 +++++------------ docker/routlin-dash/app/action_log_in.py | 1 + docker/routlin-dash/app/config_utils.py | 4 ++-- docker/routlin-dash/app/main.py | 4 ++-- docker/routlin-dash/app/view_page.py | 2 +- docker/routlin-dash/data/navbar_content.json | 7 +++++- docker/routlin-dash/data/page_content.json | 23 ++++++++++++------- 7 files changed, 33 insertions(+), 29 deletions(-) rename docker/routlin-dash/app/{action_general.py => action_actions.py} (64%) diff --git a/docker/routlin-dash/app/action_general.py b/docker/routlin-dash/app/action_actions.py similarity index 64% rename from docker/routlin-dash/app/action_general.py rename to docker/routlin-dash/app/action_actions.py index 7a10d1c..93fffa2 100644 --- a/docker/routlin-dash/app/action_general.py +++ b/docker/routlin-dash/app/action_actions.py @@ -1,27 +1,18 @@ -from flask import Blueprint, request, redirect, flash +from flask import Blueprint, request, redirect, flash, session from auth import require_level -from config_utils import (load_core, save_core, verify_core_hash, queued_msg, - flush_pending_to_queue, get_dashboard_pending, +from config_utils import (flush_pending_to_queue, get_dashboard_pending, _is_locked, _format_timing, _seconds_until_next_run) -import validation as validate -bp = Blueprint('action_general', __name__) +bp = Blueprint('action_actions', __name__) -_VIEW = '/view/view_general' +_VIEW = '/view/view_actions' @bp.route('/action/general_cardpendingchanges_save', methods=['POST']) @require_level('administrator') def general_cardpendingchanges_save(): - if not verify_core_hash(request.form.get('config_hash', '')): - flash('Configuration was modified by another session. Please refresh and try again.', 'error') - return redirect(_VIEW) - - core = load_core() - core.setdefault('network_interfaces', {})['apply_on_save'] = 'apply_on_save' in request.form - save_core(core) - - flash(queued_msg('core apply'), 'success') + session['apply_changes_immediately'] = 'apply_changes_immediately' in request.form + flash('Preference saved.', 'success') return redirect(_VIEW) diff --git a/docker/routlin-dash/app/action_log_in.py b/docker/routlin-dash/app/action_log_in.py index 11e1dec..aec1d07 100644 --- a/docker/routlin-dash/app/action_log_in.py +++ b/docker/routlin-dash/app/action_log_in.py @@ -50,6 +50,7 @@ def log_in(): session['email_address'] = account['email_address'] session['access_level'] = account.get('access_level', 'viewer') session['timezone'] = account.get('timezone', '') + session['apply_changes_immediately'] = False session.permanent = True return redirect('/view/view_overview') diff --git a/docker/routlin-dash/app/config_utils.py b/docker/routlin-dash/app/config_utils.py index 54af752..6fa8ead 100644 --- a/docker/routlin-dash/app/config_utils.py +++ b/docker/routlin-dash/app/config_utils.py @@ -111,9 +111,9 @@ def _trim_if_needed(): def _apply_on_save(): try: - return load_core().get('network_interfaces', {}).get('apply_on_save', True) + return session.get('apply_changes_immediately', False) except Exception: - return True + return False def _read_dashboard_pending(): diff --git a/docker/routlin-dash/app/main.py b/docker/routlin-dash/app/main.py index 8606f15..f9bb026 100644 --- a/docker/routlin-dash/app/main.py +++ b/docker/routlin-dash/app/main.py @@ -1,7 +1,7 @@ import os, json, sys from flask import Flask from view_page import bp as view_page_bp -from action_general import bp as action_general_bp +from action_actions import bp as action_actions_bp from action_networkinterfaces import bp as action_networkinterfaces_bp from action_upstreamdns import bp as action_upstreamdns_bp from action_apply_mdns import bp as action_apply_mdns_bp @@ -27,7 +27,7 @@ from api_apply_health import bp as api_apply_health_bp app = Flask(__name__) app.secret_key = os.environ.get('SECRET_KEY', os.urandom(24)) app.register_blueprint(view_page_bp) -app.register_blueprint(action_general_bp) +app.register_blueprint(action_actions_bp) app.register_blueprint(action_networkinterfaces_bp) app.register_blueprint(action_upstreamdns_bp) app.register_blueprint(action_apply_mdns_bp) diff --git a/docker/routlin-dash/app/view_page.py b/docker/routlin-dash/app/view_page.py index 434c0f5..7596524 100644 --- a/docker/routlin-dash/app/view_page.py +++ b/docker/routlin-dash/app/view_page.py @@ -589,7 +589,7 @@ def collect_tokens(): tokens['GENERAL_LOG_ERRORS_ONLY'] = 'true' if dns_blk_gen.get('log_errors_only') else 'false' tokens['GENERAL_DAILY_EXECUTE_TIME'] = str(dns_blk_gen.get('daily_execute_time_24hr_local', '-')) - tokens['GENERAL_APPLY_ON_SAVE'] = 'true' if net.get('apply_on_save', True) else 'false' + tokens['GENERAL_APPLY_ON_SAVE'] = 'true' if session.get('apply_changes_immediately', False) else 'false' pending_items = get_dashboard_pending() if pending_items: diff --git a/docker/routlin-dash/data/navbar_content.json b/docker/routlin-dash/data/navbar_content.json index 1844a62..7a19a93 100644 --- a/docker/routlin-dash/data/navbar_content.json +++ b/docker/routlin-dash/data/navbar_content.json @@ -6,12 +6,17 @@ "map_to": "view_overview", "client_requirement": "client_is_nothing+" }, + { + "type": "nav_item", + "label": "Actions", + "map_to": "view_actions", + "client_requirement": "client_is_administrator+" + }, { "type": "nav_menu", "label": "%MENU_LABEL%", "client_requirement": "client_is_viewer+", "items": [ - { "type": "nav_item", "label": "General", "map_to": "view_general", "client_requirement": "client_is_administrator+" }, { "type": "nav_item", "label": "Network Interfaces", "map_to": "view_network_interfaces", "client_requirement": "client_is_administrator+" }, { "type": "nav_item", "label": "DNS", "map_to": "view_upstream_dns", "client_requirement": "client_is_administrator+" }, { "type": "nav_item", "label": "DNS Blocking", "map_to": "view_dns_blocking", "client_requirement": "client_is_administrator+" }, diff --git a/docker/routlin-dash/data/page_content.json b/docker/routlin-dash/data/page_content.json index 8a6ad5d..5038ac7 100644 --- a/docker/routlin-dash/data/page_content.json +++ b/docker/routlin-dash/data/page_content.json @@ -585,7 +585,7 @@ ] }, { - "id": "view_general", + "id": "view_actions", "client_requirement": "client_is_viewer+", "items": [ { @@ -593,17 +593,17 @@ "items": [ { "type": "h1", - "text": "General Settings" + "text": "Actions" }, { "type": "p", - "text": "Core network and logging configuration from core.json." + "text": "Apply or stage pending configuration changes." } ] }, { "type": "card", - "label": "Pending Changes", + "label": "Options", "client_requirement": "client_is_administrator+", "items": [ { @@ -613,11 +613,11 @@ "items": [ { "type": "field", - "label": "Apply on Save", - "name": "apply_on_save", + "label": "Apply Changes Immediately", + "name": "apply_changes_immediately", "input_type": "checkbox", "value": "%GENERAL_APPLY_ON_SAVE%", - "hint": "When enabled, saved changes are queued immediately. When disabled, changes accumulate here until you click Apply Now." + "hint": "When enabled, saved changes are queued immediately. When disabled, changes accumulate in Pending Changes until you click Apply Now." }, { "type": "button_row", @@ -635,7 +635,14 @@ ] } ] - }, + } + ] + }, + { + "type": "card", + "label": "Pending Changes", + "client_requirement": "client_is_administrator+", + "items": [ { "type": "raw_html", "html": "%PENDING_CHANGES_HTML%"