fix: DNS split-horizon in DDNS mode, service access filter, health check, verbosity persistence
Unit Tests / test (push) Successful in 7m32s
Unit Tests / test (push) Successful in 7m32s
- DNS (critical): add _configured_dns_params() that returns (primary_domain, split_horizon_zones) from config_manager so all apply_all_dns_rules() callers pass the correct primary zone (e.g. 'pic.ngo') and split-horizon list (e.g. ['pic1.pic.ngo']) instead of the FQDN as the primary — fixes DNS_PROBE_FINISHED_BAD_CONFIG for all external domains when on VPN - firewall_manager: add split_horizon_zones param to apply_all_dns_rules() and forward it to generate_corefile() - Peers: filter service_access list to installed services only; peers.py derives valid services from config_manager.get_installed_services() with the email→mail ID mapping; Peers.jsx fetches from /api/store/installed and filters the checkboxes and defaults accordingly - Health check: fix file_manager→'files' ID mapping so files service health is checked when installed (was silently skipped due to 'file' vs 'files') - Verbosity persistence: move log_levels.json from non-mounted /app/api/config/ to CONFIG_DIR (/app/config/) which maps to config/api/ on the host; both load (managers.py) and save (routes/services.py) updated Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+25
-12
@@ -1,18 +1,18 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Plus, Trash2, Edit, Eye, Shield, Copy, Download, Key, AlertTriangle, CheckCircle, Globe, Lock, Users, Server } from 'lucide-react';
|
||||
import { peerRegistryAPI, wireguardAPI, cellLinkAPI, getCsrfToken } from '../services/api';
|
||||
import { peerRegistryAPI, wireguardAPI, cellLinkAPI, storeAPI, getCsrfToken } from '../services/api';
|
||||
import { useConfig } from '../contexts/ConfigContext';
|
||||
import QRCode from 'qrcode';
|
||||
|
||||
const FULL_TUNNEL_IPS = '0.0.0.0/0, ::/0';
|
||||
|
||||
const emptyForm = () => ({
|
||||
const emptyForm = (availableServiceKeys = ['webdav']) => ({
|
||||
name: '',
|
||||
description: '',
|
||||
public_key: '',
|
||||
persistent_keepalive: 25,
|
||||
internet_access: true,
|
||||
service_access: ['calendar', 'files', 'mail', 'webdav'],
|
||||
service_access: availableServiceKeys,
|
||||
peer_access: true,
|
||||
create_calendar: false,
|
||||
password: '',
|
||||
@@ -52,14 +52,20 @@ function Toggle({ checked, onChange, label, description }) {
|
||||
);
|
||||
}
|
||||
|
||||
const STORE_ID_TO_ACCESS = { email: 'mail', calendar: 'calendar', files: 'files' };
|
||||
const ALL_SERVICES = [
|
||||
{ key: 'calendar', label: 'Calendar' },
|
||||
{ key: 'files', label: 'Files' },
|
||||
{ key: 'mail', label: 'Webmail' },
|
||||
{ key: 'webdav', label: 'WebDAV' },
|
||||
];
|
||||
|
||||
function Peers() {
|
||||
const { domain = 'cell' } = useConfig();
|
||||
const SERVICES = [
|
||||
{ key: 'calendar', label: 'Calendar', domain: `calendar.${domain}` },
|
||||
{ key: 'files', label: 'Files', domain: `files.${domain}` },
|
||||
{ key: 'mail', label: 'Webmail', domain: `mail.${domain}` },
|
||||
{ key: 'webdav', label: 'WebDAV', domain: `webdav.${domain}` },
|
||||
];
|
||||
const [installedServiceKeys, setInstalledServiceKeys] = useState(['webdav']);
|
||||
const SERVICES = ALL_SERVICES
|
||||
.filter(s => installedServiceKeys.includes(s.key))
|
||||
.map(s => ({ ...s, domain: `${s.key}.${domain}` }));
|
||||
|
||||
const [peers, setPeers] = useState([]);
|
||||
const [connectedCells, setConnectedCells] = useState([]);
|
||||
@@ -69,7 +75,7 @@ function Peers() {
|
||||
const [showEditModal, setShowEditModal] = useState(false);
|
||||
const [showViewModal, setShowViewModal] = useState(false);
|
||||
const [selectedPeer, setSelectedPeer] = useState(null);
|
||||
const [formData, setFormData] = useState(emptyForm());
|
||||
const [formData, setFormData] = useState(emptyForm(installedServiceKeys));
|
||||
const [showAdvanced, setShowAdvanced] = useState(false);
|
||||
const [peerConfig, setPeerConfig] = useState('');
|
||||
const [qrCodeDataUrl, setQrCodeDataUrl] = useState('');
|
||||
@@ -81,6 +87,13 @@ function Peers() {
|
||||
useEffect(() => {
|
||||
fetchPeers();
|
||||
cellLinkAPI.listConnections().then(r => setConnectedCells(r.data || [])).catch(() => {});
|
||||
storeAPI.listInstalled().then(r => {
|
||||
const installed = r.data?.installed || {};
|
||||
const keys = ['webdav', ...Object.keys(installed)
|
||||
.map(id => STORE_ID_TO_ACCESS[id])
|
||||
.filter(Boolean)];
|
||||
setInstalledServiceKeys(keys);
|
||||
}).catch(() => {});
|
||||
}, []);
|
||||
|
||||
const showToast = (msg, type = 'success') => {
|
||||
@@ -212,7 +225,7 @@ PersistentKeepalive = ${peer.persistent_keepalive || 25}`;
|
||||
? Object.entries(provisioned).filter(([, v]) => v).map(([k]) => k).join(', ')
|
||||
: '';
|
||||
setShowAddModal(false);
|
||||
setFormData(emptyForm());
|
||||
setFormData(emptyForm(installedServiceKeys));
|
||||
setErrors({});
|
||||
fetchPeers();
|
||||
showToast(
|
||||
@@ -471,7 +484,7 @@ PersistentKeepalive = ${peer.persistent_keepalive || 25}`;
|
||||
<h1 className="text-2xl font-bold text-gray-900">Peers</h1>
|
||||
<p className="mt-1 text-gray-600">Manage VPN peer connections and access policies</p>
|
||||
</div>
|
||||
<button onClick={() => { setFormData(emptyForm()); setErrors({}); setShowAdvanced(false); setShowAddModal(true); }}
|
||||
<button onClick={() => { setFormData(emptyForm(installedServiceKeys)); setErrors({}); setShowAdvanced(false); setShowAddModal(true); }}
|
||||
className="btn btn-primary flex items-center">
|
||||
<Plus className="h-4 w-4 mr-2" />Add Peer
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user