from flask import Blueprint, request, redirect, flash from auth import require_level from config_utils import load_core, save_core, verify_core_hash, apply_msg import sanitize bp = Blueprint('action_apply_vlans', __name__) VIEW = '/view/view_vlans' def _row_index(): try: return int(request.form.get('row_index', '')) except (ValueError, TypeError): return None def _hash_ok(): if not verify_core_hash(request.form.get('config_hash', '')): flash('Configuration was modified by another session. Please refresh and try again.', 'error') return False return True @bp.route('/action/add_vlan', methods=['POST']) @require_level('administrator') def add_vlan(): vlan_id_raw = request.form.get('vlan_id', '').strip() name = sanitize.name(request.form.get('name', '')) interface = sanitize.interface_name(request.form.get('interface', '')) subnet = sanitize.ip_or_cidr(request.form.get('subnet', '')) radius_default = 'radius_default' in request.form mdns_reflection = 'mdns_reflection' in request.form if not vlan_id_raw or not name or not interface: flash('VLAN ID, name, and interface are required.', 'error') return redirect(VIEW) try: vlan_id = int(vlan_id_raw) if not (1 <= vlan_id <= 4094): raise ValueError except (ValueError, TypeError): flash('VLAN ID must be between 1 and 4094.', 'error') return redirect(VIEW) if not _hash_ok(): return redirect(VIEW) core = load_core() vlans = core.setdefault('vlans', []) if any(v.get('vlan_id') == vlan_id for v in vlans): flash(f'VLAN {vlan_id} already exists.', 'error') return redirect(VIEW) vlans.append({ 'vlan_id': vlan_id, 'name': name, 'interface': interface, 'dhcp': {'subnet': subnet}, 'use_blocklists': [], 'radius_default': radius_default, 'mdns_reflection': mdns_reflection, 'reservations': [], }) save_core(core) flash(apply_msg(), 'success') return redirect(VIEW) @bp.route('/action/edit_vlan', methods=['POST']) @require_level('administrator') def edit_vlan(): idx = _row_index() if idx is None: flash('Invalid request.', 'error') return redirect(VIEW) name = sanitize.name(request.form.get('name', '')) interface = sanitize.interface_name(request.form.get('interface', '')) subnet = sanitize.ip_or_cidr(request.form.get('subnet', '')) radius_default = 'radius_default' in request.form mdns_reflection = 'mdns_reflection' in request.form if not name or not interface: flash('Name and interface are required.', 'error') return redirect(VIEW) if not _hash_ok(): return redirect(VIEW) core = load_core() vlans = core.get('vlans', []) if idx < 0 or idx >= len(vlans): flash('VLAN not found.', 'error') return redirect(VIEW) vlans[idx].update({'name': name, 'interface': interface, 'radius_default': radius_default, 'mdns_reflection': mdns_reflection}) vlans[idx].setdefault('dhcp', {})['subnet'] = subnet save_core(core) flash(apply_msg(), 'success') return redirect(VIEW) @bp.route('/action/delete_vlan', methods=['POST']) @require_level('administrator') def delete_vlan(): idx = _row_index() if idx is None: flash('Invalid request.', 'error') return redirect(VIEW) if not _hash_ok(): return redirect(VIEW) core = load_core() vlans = core.get('vlans', []) if idx < 0 or idx >= len(vlans): flash('VLAN not found.', 'error') return redirect(VIEW) removed = vlans.pop(idx) save_core(core) flash(apply_msg(), 'success') return redirect(VIEW)