Autarch/web/routes/upnp.py

131 lines
3.8 KiB
Python
Raw Normal View History

"""UPnP management route"""
import json
from flask import Blueprint, render_template, request, redirect, url_for, flash, current_app
from web.auth import login_required
upnp_bp = Blueprint('upnp', __name__, url_prefix='/upnp')
@upnp_bp.route('/')
@login_required
def index():
from core.upnp import get_upnp_manager
config = current_app.autarch_config
upnp = get_upnp_manager(config)
available = upnp.is_available()
mappings = upnp.load_mappings_from_config()
cron = upnp.get_cron_status()
current_mappings = ''
external_ip = ''
if available:
success, output = upnp.list_mappings()
current_mappings = output if success else f'Error: {output}'
success, ip = upnp.get_external_ip()
external_ip = ip if success else 'N/A'
return render_template('upnp.html',
available=available,
mappings=mappings,
cron=cron,
current_mappings=current_mappings,
external_ip=external_ip,
internal_ip=upnp._get_internal_ip(),
)
@upnp_bp.route('/refresh', methods=['POST'])
@login_required
def refresh():
from core.upnp import get_upnp_manager
config = current_app.autarch_config
upnp = get_upnp_manager(config)
results = upnp.refresh_all()
ok = sum(1 for r in results if r['success'])
fail = sum(1 for r in results if not r['success'])
if fail == 0:
flash(f'Refreshed {ok} mapping(s) successfully.', 'success')
else:
flash(f'{ok} OK, {fail} failed.', 'warning')
return redirect(url_for('upnp.index'))
@upnp_bp.route('/add', methods=['POST'])
@login_required
def add():
from core.upnp import get_upnp_manager
config = current_app.autarch_config
upnp = get_upnp_manager(config)
port = request.form.get('port', '')
proto = request.form.get('protocol', 'TCP').upper()
try:
port = int(port)
except ValueError:
flash('Invalid port number.', 'error')
return redirect(url_for('upnp.index'))
internal_ip = upnp._get_internal_ip()
success, msg = upnp.add_mapping(internal_ip, port, port, proto)
if success:
# Save to config
mappings = upnp.load_mappings_from_config()
if not any(m['port'] == port and m['protocol'] == proto for m in mappings):
mappings.append({'port': port, 'protocol': proto})
upnp.save_mappings_to_config(mappings)
flash(f'Added {port}/{proto}', 'success')
else:
flash(f'Failed: {msg}', 'error')
return redirect(url_for('upnp.index'))
@upnp_bp.route('/remove', methods=['POST'])
@login_required
def remove():
from core.upnp import get_upnp_manager
config = current_app.autarch_config
upnp = get_upnp_manager(config)
port = int(request.form.get('port', 0))
proto = request.form.get('protocol', 'TCP')
success, msg = upnp.remove_mapping(port, proto)
if success:
mappings = upnp.load_mappings_from_config()
mappings = [m for m in mappings if not (m['port'] == port and m['protocol'] == proto)]
upnp.save_mappings_to_config(mappings)
flash(f'Removed {port}/{proto}', 'success')
else:
flash(f'Failed: {msg}', 'error')
return redirect(url_for('upnp.index'))
@upnp_bp.route('/cron', methods=['POST'])
@login_required
def cron():
from core.upnp import get_upnp_manager
config = current_app.autarch_config
upnp = get_upnp_manager(config)
action = request.form.get('action', '')
if action == 'install':
hours = int(request.form.get('hours', 12))
success, msg = upnp.install_cron(hours)
elif action == 'uninstall':
success, msg = upnp.uninstall_cron()
else:
flash('Invalid action.', 'error')
return redirect(url_for('upnp.index'))
flash(msg, 'success' if success else 'error')
return redirect(url_for('upnp.index'))