681 lines
31 KiB
JavaScript
681 lines
31 KiB
JavaScript
const RadiusRouterManager = {
|
|
name: 'RadiusRouterManager',
|
|
props: {
|
|
show: Boolean,
|
|
userItem: Object
|
|
},
|
|
template: `
|
|
<div>
|
|
<!-- Main Router Management Modal -->
|
|
<tt-dialog
|
|
:show="show"
|
|
:title="'Router Management - ' + (userItem.username || '')"
|
|
@close="$emit('close')"
|
|
size="wide"
|
|
>
|
|
<div class="modal-body-scrollable">
|
|
<div v-if="!routerDevice && !routerLoading" class="table-placeholder" style="min-height: 300px;">
|
|
<i class="fa-duotone fa-router-slash" style="font-size: 48px; opacity: 0.3;"></i>
|
|
<div style="margin-top: 16px;">Kein Router mit dieser IP gefunden</div>
|
|
</div>
|
|
<div v-else>
|
|
<!-- Router Info Header -->
|
|
<div class="router-info-header">
|
|
<i class="fa-duotone fa-router"></i>
|
|
<div class="router-header-text">
|
|
<div class="router-title">
|
|
<tt-skeleton v-if="routerLoading" width="200px" height="22px" />
|
|
<span v-else>{{ routerDevice.deviceInfo.hardwareVersion || 'Router' }}</span>
|
|
</div>
|
|
<div class="router-subtitle" :style="routerLoading ? 'margin-top: 2px' : ''">
|
|
<tt-skeleton v-if="routerLoading" width="140px" height="15px" />
|
|
<span v-else>{{ routerDevice.username || userItem.username }}</span>
|
|
</div>
|
|
</div>
|
|
<button class="ghost-btn refresh-btn" @click="refreshDevice" :disabled="routerLoading || refreshLoading" title="Daten aktualisieren">
|
|
<i class="fa-duotone fa-arrows-rotate" :class="{ 'fa-spin': refreshLoading }"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Router Information Grid -->
|
|
<div class="router-info-grid">
|
|
<tt-info-card icon="fa-microchip" label="Hardware Modell" :value="routerDevice?.deviceInfo?.hardwareVersion" :loading="routerLoading" />
|
|
<tt-info-card icon="fa-code-branch" label="Software Version" :value="routerDevice?.deviceInfo?.softwareVersion" :loading="routerLoading" />
|
|
<tt-info-card icon="fa-barcode" label="CWMP Account" :value="routerDevice?.deviceInfo?.serialNumber" :loading="routerLoading" />
|
|
<tt-info-card icon="fa-fingerprint" label="ACS ID" :value="routerDevice?.deviceId" :loading="routerLoading" />
|
|
<tt-info-card icon="fa-globe" label="Externe IP" :value="routerDevice?.externalIp" :loading="routerLoading" />
|
|
<tt-info-card icon="fa-network-wired" label="Management IP" :value="routerDevice?.managementIp" :loading="routerLoading" />
|
|
</div>
|
|
|
|
<!-- Router Actions Section -->
|
|
<div class="router-actions-section">
|
|
<h4 class="router-actions-header">
|
|
<i class="fa-duotone fa-bolt"></i>
|
|
Router Aktionen
|
|
</h4>
|
|
<div class="router-actions-grid">
|
|
<button class="ghost-btn action-btn" @click="runRemoteAccess" :disabled="routerLoading || routerActionLoading || speedtestLoading">
|
|
<i class="fa-duotone fa-key"></i>
|
|
<span>Remote-Zugriff</span>
|
|
</button>
|
|
<button class="ghost-btn action-btn" @click="rebootRouter" :disabled="routerLoading || routerActionLoading || speedtestLoading">
|
|
<i class="fa-duotone fa-power-off"></i>
|
|
<span>Neustart</span>
|
|
</button>
|
|
<button class="ghost-btn action-btn" @click="pingRouter" :disabled="routerLoading || routerActionLoading || speedtestLoading">
|
|
<i class="fa-duotone fa-signal-bars"></i>
|
|
<span>Ping</span>
|
|
</button>
|
|
<button class="ghost-btn action-btn" @click="runSpeedtest" :disabled="routerLoading || routerActionLoading || speedtestLoading">
|
|
<i class="fa-duotone fa-gauge-high"></i>
|
|
<span>Speedtest</span>
|
|
</button>
|
|
<button class="ghost-btn action-btn" @click="openNetworkStructure" :disabled="routerLoading || routerActionLoading || speedtestLoading">
|
|
<i class="fa-duotone fa-sitemap"></i>
|
|
<span>Netzwerkstruktur</span>
|
|
</button>
|
|
<button class="ghost-btn action-btn" @click="openEventLog" :disabled="routerLoading || routerActionLoading || speedtestLoading">
|
|
<i class="fa-duotone fa-list-timeline"></i>
|
|
<span>Ereignisprotokoll</span>
|
|
</button>
|
|
<button class="ghost-btn action-btn" @click="openWlanKeyModal" :disabled="routerLoading || routerActionLoading || wlanKeyLoading">
|
|
<i class="fa-duotone fa-sliders"></i>
|
|
<span>Netzwerk-Konfig</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</tt-dialog>
|
|
|
|
<!-- SUB MODALS (Managed by this component) -->
|
|
|
|
<!-- Ping Modal -->
|
|
<tt-dialog :show="showPingModal" title="Ping Ergebnis" @close="showPingModal = false">
|
|
<tt-loading-indicator v-if="routerActionLoading && !pingResult" text="Ping läuft..." style="height: 150px;" />
|
|
<div v-else-if="pingResult">
|
|
<div class="kv-redesign">
|
|
<div class="kv-row"><span class="kv-label">Gesendet</span><code class="kv-value">{{ pingResult.packetsTransmitted }}</code></div>
|
|
<div class="kv-row"><span class="kv-label">Empfangen</span><code class="kv-value">{{ pingResult.packetsReceived }}</code></div>
|
|
<div class="kv-row"><span class="kv-label">Verlust</span><code class="kv-value">{{ pingResult.packetLoss }}%</code></div>
|
|
<div class="kv-row"><span class="kv-label">Min / Avg / Max</span><code class="kv-value">{{ pingResult.min }} / {{ pingResult.avg }} / {{ pingResult.max }} ms</code></div>
|
|
</div>
|
|
</div>
|
|
<div v-else class="table-placeholder" style="height: 150px;">Kein Ergebnis.</div>
|
|
</tt-dialog>
|
|
|
|
<!-- Speedtest Modal -->
|
|
<tt-dialog :show="showSpeedtestModal" title="Speedtest Ergebnis" @close="showSpeedtestModal = false" size="wide">
|
|
<tt-loading-indicator v-if="speedtestLoading && speedtestHistory.length === 0" text="Speedtest wird initialisiert..." style="height: 200px;" />
|
|
<div v-else>
|
|
<div class="table-wrap" style="max-height: 500px; overflow-y: auto;">
|
|
<table class="tt-table compact">
|
|
<thead>
|
|
<tr>
|
|
<th style="width: 60px;">#</th>
|
|
<th style="text-align: right">Bandbreite</th>
|
|
<th style="text-align: right">Übertragen</th>
|
|
<th style="text-align: right">Pakete</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="(row, idx) in speedtestHistory" :key="idx">
|
|
<td class="mono small">{{ idx + 1 }}</td>
|
|
<td class="mono small" style="text-align: right">{{ row.bpsFormatted }}</td>
|
|
<td class="mono small" style="text-align: right">{{ row.bytesFormatted }}</td>
|
|
<td class="mono small" style="text-align: right">{{ row.packets }}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<div ref="speedtestBottom"></div>
|
|
</div>
|
|
<div v-if="speedtestLoading" class="center mt-3 muted small">
|
|
<i class="fa-duotone fa-spinner fa-spin"></i> Aktualisiere...
|
|
</div>
|
|
<div v-else class="center mt-3" style="color: var(--ok);">
|
|
<i class="fa-duotone fa-check-circle"></i> Abgeschlossen
|
|
</div>
|
|
</div>
|
|
</tt-dialog>
|
|
|
|
<!-- Remote Access Modal -->
|
|
<tt-dialog :show="showRemoteAccessModal" title="Remote Zugriff Konfiguration" @close="showRemoteAccessModal = false">
|
|
<tt-loading-indicator v-if="remoteAccessLoading" :text="remoteAccessStep" style="height: 200px;" />
|
|
<div v-else-if="remoteAccessResult">
|
|
<div class="alert ok mb-4" style="background-color: #eaf7ef; border: 1px solid #c9e6d8; color: #206a42; padding: 12px; border-radius: 8px;">
|
|
<i class="fa-duotone fa-check-circle"></i> Konfiguration erfolgreich abgeschlossen.
|
|
</div>
|
|
<div class="kv-redesign">
|
|
<div class="kv-row">
|
|
<span class="kv-label">Remote Link</span>
|
|
<div class="kv-value inline-copy">
|
|
<a :href="remoteAccessResult.link" target="_blank" class="link">{{ remoteAccessResult.link }}</a>
|
|
<tt-copy-button :text="remoteAccessResult.link" />
|
|
</div>
|
|
</div>
|
|
<div class="kv-row">
|
|
<span class="kv-label">Username</span>
|
|
<div class="kv-value inline-copy">
|
|
<code class="mono">{{ remoteAccessResult.username }}</code>
|
|
<tt-copy-button :text="remoteAccessResult.username" />
|
|
</div>
|
|
</div>
|
|
<div class="kv-row">
|
|
<span class="kv-label">Password</span>
|
|
<div class="kv-value inline-copy">
|
|
<code class="mono">{{ remoteAccessResult.password }}</code>
|
|
<tt-copy-button :text="remoteAccessResult.password" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mt-4 pt-3" style="border-top: 1px solid var(--border);">
|
|
<button class="ghost-btn" @click="runRemoteAccess(true)" :disabled="remoteAccessLoading">
|
|
<i class="fa-duotone fa-rotate"></i>
|
|
<span>Zugangsdaten neu erstellen</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div v-else class="table-placeholder" style="height: 200px;">Ein Fehler ist aufgetreten.</div>
|
|
</tt-dialog>
|
|
|
|
<!-- Network Structure Modal -->
|
|
<tt-dialog :show="showNetworkStructureModal" title="Netzwerkstruktur" @close="showNetworkStructureModal = false" size="wide">
|
|
<tt-loading-indicator v-if="networkStructureLoading" text="Lade Struktur..." style="min-height: 300px;" />
|
|
<div v-else-if="rootDevice">
|
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
|
|
<span v-if="networkStructureCached" class="small" style="color: var(--tt-text-tertiary);">(Cache)</span>
|
|
<span v-else></span>
|
|
<button class="ghost-btn" @click="openNetworkStructure(true)" :disabled="networkStructureLoading" title="Neu laden">
|
|
<i class="fa-duotone fa-arrows-rotate" :class="{ 'fa-spin': networkStructureLoading }"></i>
|
|
</button>
|
|
</div>
|
|
<div class="network-tree-container">
|
|
<!-- Uses the recursive component -->
|
|
<radius-network-node :device="rootDevice" />
|
|
</div>
|
|
</div>
|
|
<div v-else class="table-placeholder" style="min-height: 300px;">Keine Daten verfügbar.</div>
|
|
</tt-dialog>
|
|
|
|
<!-- Event Log Modal -->
|
|
<tt-dialog :show="showEventLogModal" title="Ereignisprotokoll" @close="showEventLogModal = false" size="wide">
|
|
<tt-loading-indicator v-if="eventLogLoading" text="Lade Ereignisprotokoll..." style="min-height: 300px;" />
|
|
<div v-else-if="eventLogData && eventLogData.length > 0">
|
|
<div class="table-wrap" style="max-height: 500px; overflow-y: auto;">
|
|
<table class="tt-table compact">
|
|
<thead>
|
|
<tr>
|
|
<th style="width: 100px;">Datum</th>
|
|
<th style="width: 80px;">Uhrzeit</th>
|
|
<th style="width: 120px;">Gruppe</th>
|
|
<th>Nachricht</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="(event, idx) in eventLogData" :key="idx">
|
|
<td class="mono small">{{ event.date }}</td>
|
|
<td class="mono small">{{ event.time }}</td>
|
|
<td class="small">{{ event.group }}</td>
|
|
<td class="small">{{ event.msg }}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div v-else class="table-placeholder" style="min-height: 300px;">Keine Ereignisse verfügbar.</div>
|
|
</tt-dialog>
|
|
|
|
<!-- WLAN & Network Config Modal -->
|
|
<tt-dialog :show="showWlanKeyModal" title="Netzwerk-Konfiguration" @close="showWlanKeyModal = false" size="wide">
|
|
<tt-loading-indicator v-if="wlanKeyLoading" text="Lade Konfiguration..." style="min-height: 300px;" />
|
|
<div v-else-if="wlanKeyData" class="network-config-modal">
|
|
<!-- Grid Layout -->
|
|
<div class="config-grid">
|
|
<!-- WLAN Section -->
|
|
<div class="config-card">
|
|
<div class="config-card-header">
|
|
<i class="fa-duotone fa-wifi"></i>
|
|
<span>WLAN</span>
|
|
<span v-if="wlanKeyData.ap_enabled !== undefined" class="status-dot" :class="wlanKeyData.ap_enabled ? 'active' : 'inactive'"></span>
|
|
<span v-if="wlanKeyDataCached" class="cache-badge"><i class="fa-duotone fa-database"></i> Cache</span>
|
|
<button class="config-action-btn" @click="copyAllNetworkConfig" title="Alles kopieren">
|
|
<i class="fa-duotone fa-copy"></i>
|
|
</button>
|
|
<button class="config-action-btn" @click="openWlanKeyModal(true)" :disabled="wlanKeyLoading" title="Neu laden">
|
|
<i class="fa-duotone fa-arrows-rotate" :class="{ 'fa-spin': wlanKeyLoading }"></i>
|
|
</button>
|
|
</div>
|
|
<div class="config-card-body">
|
|
<div class="config-row highlight">
|
|
<span class="config-label">SSID</span>
|
|
<div class="config-value with-copy">
|
|
<code>{{ wlanKeyData.ssid || '-' }}</code>
|
|
<tt-copy-button v-if="wlanKeyData.ssid" :text="wlanKeyData.ssid" />
|
|
</div>
|
|
</div>
|
|
<div class="config-row" v-if="wlanKeyData.ssid_secondary && wlanKeyData.ssid_secondary !== wlanKeyData.ssid">
|
|
<span class="config-label">SSID 5GHz</span>
|
|
<div class="config-value with-copy">
|
|
<code>{{ wlanKeyData.ssid_secondary }}</code>
|
|
<tt-copy-button :text="wlanKeyData.ssid_secondary" />
|
|
</div>
|
|
</div>
|
|
<div class="config-row highlight">
|
|
<span class="config-label">Passwort</span>
|
|
<div class="config-value with-copy">
|
|
<code class="password">{{ wlanKeyData.psk || '-' }}</code>
|
|
<tt-copy-button v-if="wlanKeyData.psk" :text="wlanKeyData.psk" />
|
|
</div>
|
|
</div>
|
|
<div class="config-row">
|
|
<span class="config-label">Sicherheit</span>
|
|
<code class="config-value">{{ wlanKeyData.wpa_type?.toUpperCase() || 'WPA2' }}</code>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- LAN Section -->
|
|
<div class="config-card">
|
|
<div class="config-card-header">
|
|
<i class="fa-duotone fa-network-wired"></i>
|
|
<span>LAN</span>
|
|
</div>
|
|
<div class="config-card-body">
|
|
<div class="config-row highlight">
|
|
<span class="config-label">Router-IP</span>
|
|
<div class="config-value with-copy">
|
|
<code>{{ wlanKeyData.lan?.ip || '-' }}</code>
|
|
<tt-copy-button v-if="wlanKeyData.lan?.ip" :text="wlanKeyData.lan.ip" />
|
|
</div>
|
|
</div>
|
|
<div class="config-row">
|
|
<span class="config-label">Subnetz</span>
|
|
<code class="config-value">{{ wlanKeyData.lan?.subnet || '-' }}</code>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- DHCP Section -->
|
|
<div class="config-card">
|
|
<div class="config-card-header">
|
|
<i class="fa-duotone fa-server"></i>
|
|
<span>DHCP</span>
|
|
</div>
|
|
<div class="config-card-body">
|
|
<div class="config-row">
|
|
<span class="config-label">Bereich</span>
|
|
<code class="config-value" v-if="wlanKeyData.lan?.dhcp_start && wlanKeyData.lan?.dhcp_end">
|
|
{{ wlanKeyData.lan.dhcp_start }} - {{ wlanKeyData.lan.dhcp_end }}
|
|
</code>
|
|
<code class="config-value" v-else>-</code>
|
|
</div>
|
|
<div class="config-row">
|
|
<span class="config-label">DNS</span>
|
|
<code class="config-value small">{{ wlanKeyData.lan?.dns_servers || '-' }}</code>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Device Section -->
|
|
<div class="config-card">
|
|
<div class="config-card-header">
|
|
<i class="fa-duotone fa-router"></i>
|
|
<span>Gerät</span>
|
|
</div>
|
|
<div class="config-card-body">
|
|
<div class="config-row" v-if="wlanKeyData.device_name">
|
|
<span class="config-label">Modell</span>
|
|
<code class="config-value">{{ wlanKeyData.device_name }}</code>
|
|
</div>
|
|
<div class="config-row" v-if="wlanKeyData.known_devices_count !== undefined">
|
|
<span class="config-label">WLAN-Geräte</span>
|
|
<code class="config-value">{{ wlanKeyData.known_devices_count }}</code>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div v-else class="table-placeholder" style="min-height: 300px;">Keine Daten verfügbar.</div>
|
|
</tt-dialog>
|
|
|
|
</div>
|
|
`,
|
|
data: () => ({
|
|
routerLoading: false,
|
|
routerActionLoading: false,
|
|
routerDevice: null,
|
|
|
|
// Sub-Modal States
|
|
showPingModal: false,
|
|
pingResult: null,
|
|
|
|
showSpeedtestModal: false,
|
|
speedtestLoading: false,
|
|
speedtestResult: null,
|
|
speedtestHistory: [],
|
|
speedtestHasStarted: false,
|
|
|
|
showRemoteAccessModal: false,
|
|
remoteAccessLoading: false,
|
|
remoteAccessResult: null,
|
|
remoteAccessStep: '',
|
|
|
|
showNetworkStructureModal: false,
|
|
networkStructureLoading: false,
|
|
networkStructureCached: false,
|
|
rootDevice: null,
|
|
|
|
showEventLogModal: false,
|
|
eventLogLoading: false,
|
|
eventLogData: null,
|
|
refreshLoading: false,
|
|
|
|
showWlanKeyModal: false,
|
|
wlanKeyLoading: false,
|
|
wlanKeyData: null,
|
|
wlanKeyDataCached: false
|
|
}),
|
|
watch: {
|
|
show: {
|
|
handler(val) {
|
|
if (val && this.userItem) {
|
|
this.loadRouterData();
|
|
}
|
|
},
|
|
immediate: true
|
|
},
|
|
userItem(val) {
|
|
if (val && this.show) {
|
|
this.loadRouterData();
|
|
}
|
|
}
|
|
},
|
|
methods: {
|
|
async loadRouterData() {
|
|
this.routerLoading = true;
|
|
this.routerDevice = null;
|
|
this.pingResult = null;
|
|
this.speedtestResult = null;
|
|
this.speedtestLoading = false;
|
|
|
|
try {
|
|
const { data: deviceData } = await axios.get(`${window.TT_CONFIG.BASE_PATH}/Radius/genieacsGetDeviceByMac`, {
|
|
params: { mac: this.userItem.username }
|
|
});
|
|
|
|
if (deviceData?.success) {
|
|
this.routerDevice = deviceData;
|
|
}
|
|
} catch (error) {
|
|
console.error('Error fetching router:', error);
|
|
window.notify('error', 'Fehler beim Laden des Routers');
|
|
}
|
|
this.routerLoading = false;
|
|
},
|
|
async refreshDevice() {
|
|
if (!this.routerDevice?.deviceId) return;
|
|
this.refreshLoading = true;
|
|
try {
|
|
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/Radius/genieacsRefreshDevice`, {
|
|
deviceId: this.routerDevice.deviceId
|
|
});
|
|
if (data?.success) {
|
|
this.routerDevice.deviceInfo = data.deviceInfo;
|
|
this.routerDevice.externalIp = data.externalIp;
|
|
this.routerDevice.managementIp = data.managementIp;
|
|
window.notify('success', 'Daten aktualisiert');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error refreshing device:', error);
|
|
window.notify('error', 'Fehler beim Aktualisieren');
|
|
}
|
|
this.refreshLoading = false;
|
|
},
|
|
async rebootRouter() {
|
|
if (!this.routerDevice || !this.routerDevice.deviceId) return;
|
|
if (!confirm('Möchten Sie den Router wirklich neu starten?')) return;
|
|
|
|
this.routerActionLoading = true;
|
|
try {
|
|
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/Radius/genieacsRebootDevice`, {
|
|
deviceId: this.routerDevice.deviceId
|
|
});
|
|
|
|
if (data.success) {
|
|
window.notify('success', 'Router-Neustart gestartet');
|
|
} else {
|
|
window.notify('error', data.message || 'Fehler beim Neustart');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error rebooting router:', error);
|
|
window.notify('error', 'Fehler beim Neustarten des Routers');
|
|
}
|
|
this.routerActionLoading = false;
|
|
},
|
|
async pingRouter() {
|
|
if (!this.routerDevice) return;
|
|
const pingIp = this.routerDevice.managementIp || this.routerDevice.ip;
|
|
if (!pingIp) return;
|
|
|
|
this.showPingModal = true;
|
|
this.routerActionLoading = true;
|
|
this.pingResult = null;
|
|
try {
|
|
const { data } = await axios.get(`${window.TT_CONFIG.BASE_PATH}/Radius/genieacsPing`, {
|
|
params: { ip: pingIp }
|
|
});
|
|
|
|
if (data.success && data.result) {
|
|
this.pingResult = data.result;
|
|
window.notify('success', 'Ping erfolgreich');
|
|
} else {
|
|
window.notify('error', 'Ping fehlgeschlagen');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error pinging router:', error);
|
|
window.notify('error', 'Fehler beim Pingen des Routers');
|
|
}
|
|
this.routerActionLoading = false;
|
|
},
|
|
async runSpeedtest() {
|
|
if (!this.routerDevice || !this.routerDevice.deviceId) return;
|
|
this.showSpeedtestModal = true;
|
|
this.speedtestLoading = true;
|
|
this.speedtestResult = null;
|
|
this.speedtestHistory = [];
|
|
this.speedtestHasStarted = false;
|
|
|
|
try {
|
|
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/Radius/genieacsRunSpeedtest`, {
|
|
deviceId: this.routerDevice.deviceId
|
|
});
|
|
|
|
if (data.success) {
|
|
this.pollSpeedtestResult();
|
|
} else {
|
|
throw new Error(data.message || "Speedtest konnte nicht gestartet werden");
|
|
}
|
|
} catch (e) {
|
|
window.notify('error', e.response?.data?.message || e.message || 'Fehler beim Starten des Speedtests');
|
|
this.speedtestLoading = false;
|
|
}
|
|
},
|
|
async pollSpeedtestResult() {
|
|
let attempts = 0;
|
|
const maxAttempts = 240;
|
|
|
|
const poll = async () => {
|
|
if (!this.showSpeedtestModal) return;
|
|
if (attempts >= maxAttempts) {
|
|
this.speedtestLoading = false;
|
|
window.notify('error', 'Speedtest Zeitüberschreitung');
|
|
return;
|
|
}
|
|
attempts++;
|
|
|
|
try {
|
|
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/Radius/genieacsGetSpeedtestResult`, {
|
|
deviceId: this.routerDevice.deviceId
|
|
});
|
|
|
|
if (data.success && data.result) {
|
|
this.speedtestHistory.push(data.result);
|
|
this.$nextTick(() => {
|
|
if (this.$refs.speedtestBottom) {
|
|
this.$refs.speedtestBottom.scrollIntoView({ behavior: 'smooth' });
|
|
}
|
|
});
|
|
|
|
if (data.result.bps > 0) this.speedtestHasStarted = true;
|
|
|
|
if (this.speedtestHasStarted && data.result.bps === 0) {
|
|
this.speedtestLoading = false;
|
|
window.notify('success', 'Speedtest abgeschlossen');
|
|
return;
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
|
|
if (this.speedtestLoading) setTimeout(poll, 1000);
|
|
};
|
|
poll();
|
|
},
|
|
async runRemoteAccess(forceRecreate = false) {
|
|
if (!this.routerDevice || !this.routerDevice.deviceId) return;
|
|
this.showRemoteAccessModal = true;
|
|
this.remoteAccessLoading = true;
|
|
this.remoteAccessStep = forceRecreate ? 'Erstelle neue Zugangsdaten...' : 'Konfiguriere Zugriff...';
|
|
this.remoteAccessResult = null;
|
|
|
|
try {
|
|
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/Radius/genieacsRemoteAccess`, {
|
|
deviceId: this.routerDevice.deviceId,
|
|
forceRecreate: forceRecreate
|
|
});
|
|
|
|
if (data.success) {
|
|
this.remoteAccessResult = data;
|
|
if (forceRecreate) {
|
|
window.notify('success', 'Neue Zugangsdaten erstellt');
|
|
}
|
|
} else {
|
|
throw new Error(data.message || "Unbekannter Fehler");
|
|
}
|
|
} catch (error) {
|
|
window.notify('error', error.response?.data?.message || error.message || 'Fehler bei Remote Access');
|
|
} finally {
|
|
this.remoteAccessLoading = false;
|
|
}
|
|
},
|
|
async openNetworkStructure(forceRefresh = false) {
|
|
if (!this.routerDevice || !this.routerDevice.deviceId) return;
|
|
this.showNetworkStructureModal = true;
|
|
this.networkStructureLoading = true;
|
|
this.rootDevice = null;
|
|
this.networkStructureCached = false;
|
|
|
|
try {
|
|
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/Radius/genieacsNetworkStructure`, {
|
|
deviceId: this.routerDevice.deviceId,
|
|
forceRefresh: forceRefresh
|
|
});
|
|
|
|
if (data.root) {
|
|
this.rootDevice = data.root;
|
|
this.networkStructureCached = data.cached === true;
|
|
}
|
|
} catch (error) {
|
|
console.error(error);
|
|
window.notify('error', 'Fehler beim Laden der Netzwerkstruktur');
|
|
} finally {
|
|
this.networkStructureLoading = false;
|
|
}
|
|
},
|
|
async openEventLog() {
|
|
if (!this.routerDevice || !this.routerDevice.deviceId) return;
|
|
this.showEventLogModal = true;
|
|
this.eventLogLoading = true;
|
|
this.eventLogData = null;
|
|
|
|
try {
|
|
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/Radius/genieacsEventLog`, {
|
|
deviceId: this.routerDevice.deviceId
|
|
});
|
|
|
|
if (data.success && data.events) {
|
|
this.eventLogData = data.events;
|
|
} else {
|
|
throw new Error(data.message || "Keine Ereignisse gefunden");
|
|
}
|
|
} catch (error) {
|
|
console.error(error);
|
|
window.notify('error', error.response?.data?.message || 'Fehler beim Laden des Ereignisprotokolls');
|
|
} finally {
|
|
this.eventLogLoading = false;
|
|
}
|
|
},
|
|
async openWlanKeyModal(forceRefresh = false) {
|
|
if (!this.routerDevice || !this.routerDevice.deviceId) return;
|
|
this.showWlanKeyModal = true;
|
|
this.wlanKeyLoading = true;
|
|
this.wlanKeyData = null;
|
|
this.wlanKeyDataCached = false;
|
|
|
|
try {
|
|
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/Radius/genieacsFritzboxWlanKey`, {
|
|
deviceId: this.routerDevice.deviceId,
|
|
forceRefresh: forceRefresh
|
|
});
|
|
|
|
if (data.success && data.wlan) {
|
|
this.wlanKeyData = data.wlan;
|
|
this.wlanKeyDataCached = data.cached === true;
|
|
} else {
|
|
throw new Error(data.message || "Keine WLAN-Daten gefunden");
|
|
}
|
|
} catch (error) {
|
|
console.error(error);
|
|
window.notify('error', error.response?.data?.message || 'Fehler beim Laden der WLAN-Daten');
|
|
} finally {
|
|
this.wlanKeyLoading = false;
|
|
}
|
|
},
|
|
copyAllNetworkConfig() {
|
|
if (!this.wlanKeyData) return;
|
|
|
|
const d = this.wlanKeyData;
|
|
const lines = [
|
|
'=== WLAN ===',
|
|
`SSID: ${d.ssid || '-'}`,
|
|
d.ssid_secondary && d.ssid_secondary !== d.ssid ? `SSID 5GHz: ${d.ssid_secondary}` : null,
|
|
`Passwort: ${d.psk || '-'}`,
|
|
`Sicherheit: ${d.wpa_type?.toUpperCase() || 'WPA2'}`,
|
|
d.ap_enabled !== undefined ? `WLAN aktiv: ${d.ap_enabled ? 'Ja' : 'Nein'}` : null,
|
|
'',
|
|
'=== LAN ===',
|
|
`Router-IP: ${d.lan?.ip || '-'}`,
|
|
`Subnetz: ${d.lan?.subnet || '-'}`,
|
|
'',
|
|
'=== DHCP ===',
|
|
`Bereich: ${d.lan?.dhcp_start && d.lan?.dhcp_end ? `${d.lan.dhcp_start} - ${d.lan.dhcp_end}` : '-'}`,
|
|
`DNS: ${d.lan?.dns_servers || '-'}`,
|
|
'',
|
|
'=== Gerät ===',
|
|
d.device_name ? `Modell: ${d.device_name}` : null,
|
|
d.known_devices_count !== undefined ? `WLAN-Geräte: ${d.known_devices_count}` : null,
|
|
].filter(Boolean).join('\n');
|
|
|
|
navigator.clipboard.writeText(lines).then(() => {
|
|
window.notify('success', 'Netzwerk-Konfiguration kopiert');
|
|
}).catch(() => {
|
|
window.notify('error', 'Kopieren fehlgeschlagen');
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
if (window.VueApp) {
|
|
window.VueApp.component('radius-router-manager', RadiusRouterManager);
|
|
} |