A5: Extract all route groups into Flask blueprints (app.py -1735 lines)

Extract 9 route groups out of app.py into routes/ blueprints:
- routes/network.py  — DNS, DHCP, NTP, network info/test (10 routes)
- routes/wireguard.py — WireGuard keys, peers, config, enforcement (18 routes)
- routes/cells.py    — cell-to-cell connections (5 routes)
- routes/peers.py    — peer CRUD + IP update + _next_peer_ip helper (10 routes)
- routes/routing.py  — NAT, peer routes, firewall, iptables (17 routes)
- routes/vault.py    — certs, trust, secrets (19 routes)
- routes/containers.py — containers, images, volumes (14 routes)
- routes/services.py — service bus, logs, services status/connectivity (18 routes)
- routes/peer_dashboard.py — peer-scoped dashboard/services (2 routes)

All blueprints use lazy `from app import X` inside route bodies to preserve
test patch compatibility (patch('app.email_manager', mock) still works).

Also included in this commit:
- A1 fix: backup/restore now includes email/calendar user files
- A2 fix: apply_config sets applying=True flag via helper container
- A3 fix: add_peer rolls back firewall on DNS failure

app.py reduced: 3011 → 1294 lines. 1021 tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-01 06:11:21 -04:00
parent d54844cd44
commit 09138fbc18
16 changed files with 2108 additions and 2072 deletions
+115
View File
@@ -0,0 +1,115 @@
import logging
from flask import Blueprint, jsonify, session
logger = logging.getLogger('picell')
bp = Blueprint('peer_dashboard', __name__)
@bp.route('/api/peer/dashboard', methods=['GET'])
def peer_dashboard():
try:
from app import peer_registry, wireguard_manager, _configured_domain
peer_name = session.get('peer_name')
peer = peer_registry.get_peer(peer_name) if peer_name else None
if not peer:
return jsonify({'error': 'Peer not found'}), 404
wg_stats = {'online': None, 'transfer_rx': 0, 'transfer_tx': 0, 'last_handshake': None}
public_key = peer.get('public_key')
if public_key:
try:
wg_stats = wireguard_manager.get_peer_status(public_key)
except Exception:
pass
peer_ip = peer.get('ip', '')
allowed_ips = f"{peer_ip.split('/')[0]}/32" if peer_ip else ''
domain = _configured_domain()
_svc_url_map = {
'calendar': f'http://calendar.{domain}',
'files': f'http://files.{domain}',
'mail': f'http://mail.{domain}',
'webdav': f'http://webdav.{domain}',
}
service_urls = {
svc: _svc_url_map[svc]
for svc in peer.get('service_access', [])
if svc in _svc_url_map
}
return jsonify({
'name': peer_name,
'ip': peer_ip,
'service_access': peer.get('service_access', []),
'service_urls': service_urls,
'online': wg_stats.get('online'),
'transfer_rx': wg_stats.get('transfer_rx', 0),
'transfer_tx': wg_stats.get('transfer_tx', 0),
'last_handshake': wg_stats.get('last_handshake'),
'allowed_ips': peer.get('allowed_ips', allowed_ips),
})
except Exception as e:
return jsonify({'error': str(e)}), 500
@bp.route('/api/peer/services', methods=['GET'])
def peer_services():
try:
from app import peer_registry, wireguard_manager, config_manager, _configured_domain, _resolve_peer_dns
peer_name = session.get('peer_name')
peer = peer_registry.get_peer(peer_name) if peer_name else None
if not peer:
return jsonify({'error': 'Peer not found'}), 404
domain = _configured_domain()
peer_ip = peer.get('ip', '')
server_public_key = ''
wg_port = 51820
server_endpoint = ''
try:
server_public_key = wireguard_manager.get_keys().get('public_key', '')
wg_port = config_manager.configs.get('_identity', {}).get('wireguard_port', 51820)
srv = wireguard_manager.get_server_config()
server_endpoint = srv.get('endpoint') or '<SERVER_IP>'
except Exception:
pass
wg_config = ''
peer_private_key = peer.get('private_key', '')
if peer_private_key:
try:
internet_access = peer.get('internet_access', True)
allowed_ips = wireguard_manager.FULL_TUNNEL_IPS if internet_access else wireguard_manager.get_split_tunnel_ips()
wg_config = wireguard_manager.get_peer_config(
peer_name=peer_name,
peer_ip=peer_ip,
peer_private_key=peer_private_key,
server_endpoint=server_endpoint,
allowed_ips=allowed_ips,
)
except Exception:
pass
return jsonify({
'username': peer_name,
'wireguard': {
'ip': peer_ip,
'server_public_key': server_public_key,
'endpoint_port': wg_port,
'dns': _resolve_peer_dns(),
'config': wg_config,
},
'email': {
'address': f'{peer_name}@{domain}',
'smtp': {'host': f'mail.{domain}', 'port': 587},
'imap': {'host': f'mail.{domain}', 'port': 993},
},
'caldav': {
'url': f'http://calendar.{domain}',
'username': peer_name,
},
'files': {
'url': f'http://files.{domain}',
'username': peer_name,
},
})
except Exception as e:
return jsonify({'error': str(e)}), 500