Development

This commit is contained in:
Matthew Grotke 2026-06-06 14:55:29 -04:00
parent 33ec9e7f1c
commit 0cec7d69c9
12 changed files with 124 additions and 92 deletions

View file

@ -839,10 +839,10 @@ def load_datasource(spec):
def collect_layout_tokens(cfg):
import license
import settings as settings
net = cfg.get('network_interfaces', {})
return {
'GENERAL_LAN_INTERFACE': str(net.get('lan_interface', '-')),
'VPN_VLAN_COUNT': str(sum(1 for v in cfg.get('vlans', []) if v.get('is_vpn'))),
'PRO_LICENSE_JS': 'true' if license.is_pro() else 'false',
'PRO_LICENSE_JS': 'true' if settings.is_pro() else 'false',
}

View file

@ -15,10 +15,19 @@ from config_utils import (
_is_locked, _lock_mtime, _entry_ts_from_queue,
)
PAGES_DIR = os.path.join(APP_DIR, 'pages')
NAVBAR_FILE = os.path.join(APP_DIR, 'navbar.json')
CSS_FILE = os.path.join(DATA_DIR, 'styles.css')
COMMON_JS_FILE = os.path.join(DATA_DIR, 'common.js')
import settings as settings
PAGES_DIR = os.path.join(APP_DIR, 'pages')
NAVBAR_FILE = os.path.join(APP_DIR, 'navbar.json')
CSS_FILE = os.path.join(WWW_DIR, 'styles.css')
COMMON_JS_FILE = os.path.join(WWW_DIR, 'common.js')
def _file_version(path):
try:
return int(os.path.getmtime(path))
except OSError:
return 0
# Constants ===========================================================
@ -91,21 +100,20 @@ def load_icon(name):
return ''
def inline_js(page_name=None):
big_validate_js = build_big_validate()
try:
with open(COMMON_JS_FILE) as f:
app_js = f.read()
except Exception:
app_js = ''
page_js = ''
if page_name:
page_js_path = os.path.join(PAGES_DIR, page_name, 'page.js')
parts = [build_big_validate()]
if not settings.is_production():
try:
with open(page_js_path) as f:
page_js = f.read()
with open(COMMON_JS_FILE) as f:
parts.append(f.read())
except Exception:
pass
return big_validate_js + '\n' + app_js + ('\n' + page_js if page_js else '')
if page_name:
try:
with open(os.path.join(PAGES_DIR, page_name, 'page.js')) as f:
parts.append(f.read())
except Exception:
pass
return '\n'.join(parts)
# Utilities ===========================================================
@ -1512,7 +1520,6 @@ def build_item(item, tokens, inherited_req=None):
# Layout renderer =====================================================
def render_layout(view_id, content_html, tokens, page_name=None):
css = load_css()
level = client_level()
has_pending_alert = not _apply_changes_immediately() and bool(get_dashboard_pending())
titlebar_html = f'<div class="titlebar"><span class="titlebar-brand">{WEB_APP_DISPLAY_NAME}</span></div>'
@ -1636,17 +1643,27 @@ def render_layout(view_id, content_html, tokens, page_name=None):
'</div>\n'
)
if settings.is_production():
css_ver = _file_version(CSS_FILE)
js_ver = _file_version(COMMON_JS_FILE)
css_tag = f' <link rel="stylesheet" href="/www/styles.css?v={css_ver}">\n'
common_js = f'<script src="/www/common.js?v={js_ver}"></script>\n'
else:
css_tag = f' <style>{load_css()}</style>\n'
common_js = ''
return (
'<!DOCTYPE html>\n<html lang="en">\n<head>\n'
' <meta charset="UTF-8"/>\n'
' <meta name="viewport" content="width=device-width, initial-scale=1.0"/>\n'
f' <title>{WEB_APP_DISPLAY_NAME}</title>\n'
f' <style>{css}</style>\n'
f'{css_tag}'
'</head>\n<body>\n'
f'{titlebar_html}\n'
f'{navbar_html}\n'
f'<main class="main-content">\n{pending_bar}{problem_bars}{other_bars}{content_html}\n</main>\n'
f'{footer_html}\n'
f'{common_js}'
f'<script>var CONFIG_HASH="{page_hash}";var LAN_IFACE="{lan_iface}";var VPN_VLAN_COUNT={vpn_count};var APPLY_UUID={json.dumps(my_uuid)};</script>\n'
f'<script>{inline_js(page_name)}</script>\n'
'</body>\n</html>'

View file

@ -1,2 +0,0 @@
def is_pro():
return True

View file

@ -1,14 +1,15 @@
import os, json, sys, importlib.util as _importlib_util
from flask import Flask, Blueprint, session, redirect, get_flashed_messages
from flask import Flask, Blueprint, session, redirect, get_flashed_messages, send_from_directory
from markupsafe import Markup
from config_utils import (
ACCOUNTS_FILE, APP_DIR, CONFIGS_DIR, HEALTH_FILE,
ACCOUNTS_FILE, APP_DIR, CONFIGS_DIR, HEALTH_FILE, WWW_DIR,
load_config, queue_command, _find_cmd_in_queues,
)
from factory import (
LEVEL_RANK, PAGES_DIR, e, client_level, passes, build_items,
load_json, render_layout,
)
import settings as settings
from pages.actions.action import bp as actions_bp
from pages.bannedips.action import bp as bannedips_bp
from pages.ddns.action import bp as ddns_bp
@ -35,6 +36,16 @@ 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))
# Static www/ serving =================================================
@app.route('/www/<path:filename>')
def serve_www(filename):
response = send_from_directory(WWW_DIR, filename)
if settings.is_production():
response.cache_control.max_age = 86400
response.cache_control.public = True
return response
# View blueprint ======================================================
bp = Blueprint('view_page', __name__)

View file

@ -8,11 +8,11 @@ from auth import require_level
from config_utils import load_config, record_group, diff_fields, verify_config_hash
import sanitize
import mod_validation as validate
import license
import settings as settings
_PAGE = Path(__file__).parent.name
PRO_LICENSE = license.is_pro()
PRO_LICENSE = settings.is_pro()
bp = Blueprint(_PAGE, __name__)

View file

@ -1,9 +1,9 @@
import json
from config_utils import collect_layout_tokens, load_datasource
from factory import load_json, build_table, table_token_key, iter_table_items, PAGES_DIR
import license
import settings as settings
PRO_LICENSE = license.is_pro()
PRO_LICENSE = settings.is_pro()
def collect_tokens(cfg):

View file

@ -8,11 +8,11 @@ from flask import Blueprint, request, redirect, flash, send_file, abort, jsonify
from auth import require_level
from config_utils import CONFIGS_DIR, load_config, record_group, diff_fields
import mod_validation as validate
import license
import settings as settings
_PAGE = Path(__file__).parent.name
PRO_LICENSE = license.is_pro()
PRO_LICENSE = settings.is_pro()
bp = Blueprint(_PAGE, __name__)

View file

@ -1,9 +1,9 @@
import json
import os
from config_utils import collect_layout_tokens, CONFIGS_DIR
import license
import settings as settings
PRO_LICENSE = license.is_pro()
PRO_LICENSE = settings.is_pro()
RADIUS_LOG_MAX = 50
RADIUS_LOG_FILE = '/var/log/freeradius/radius.log'

View file

@ -0,0 +1,9 @@
import os
def is_production():
return os.environ.get('PRODUCTION_MODE', '').lower() in ('1', 'true', 'yes')
def is_pro():
return bool(os.environ.get('LICENSE', '').strip())