Warehouse project/fix

This commit is contained in:
Luca Haid
2025-12-03 14:08:44 +00:00
parent ff070bac73
commit 594a926262
10 changed files with 1560 additions and 735 deletions
+2
View File
@@ -16,10 +16,12 @@
.radius-scope .inline-copy { display:flex; align-items:center; gap:8px; justify-content: flex-end; }
.radius-scope .grid { display:grid; }
.radius-scope .g-2 { gap: 8px; }
.radius-scope .g-3 { gap: 12px; }
.radius-scope .g-4 { gap: 16px; }
.radius-scope .g-6 { gap: 24px; }
.radius-scope .cols-1 { grid-template-columns: 1fr; }
.radius-scope .cols-2 { grid-template-columns: repeat(2, minmax(0,1fr)); }
.radius-scope .cols-3 { grid-template-columns: repeat(3, minmax(0,1fr)); }
.radius-scope .cols-4 { grid-template-columns: repeat(4, minmax(0,1fr)); }
@media (max-width: 900px){ .radius-scope .cols-4 { grid-template-columns: repeat(2, minmax(0,1fr)); } }
@media (max-width: 600px){ .radius-scope .cols-4 { grid-template-columns: 1fr; } }
+420 -131
View File
@@ -57,7 +57,7 @@ Vue.component('radius-users', {
<th style="text-align: center; width: 183px;">Username</th>
<th style="text-align: center">Info</th>
<th style="text-align: center; width: 190px;">Status</th>
<th style="text-align: center; width: 115px;">Aktionen</th>
<th style="text-align: center; width: 180px;">Aktionen</th>
</tr>
</thead>
</template>
@@ -105,8 +105,8 @@ Vue.component('radius-users', {
class="fa-duotone fa-circle-info"></i></button>
<button class="ghost-btn" @click="openTransferModal(item.username)" data-tooltip="Transfer Statistik"
data-tooltip-align="left"><i class="fa-duotone fa-chart-line"></i></button>
<!-- <button class="ghost-btn" @click="openRouterManagement(item)" data-tooltip="Router Management"-->
<!-- data-tooltip-align="left"><i class="fa-duotone fa-router"></i></button>-->
<button v-if="window.TT_CONFIG.ACS_ENABLED" class="ghost-btn" @click="openRouterManagement(item)" data-tooltip="Router Management"
data-tooltip-align="left"><i class="fa-duotone fa-router"></i></button>
</td>
</template>
<template #observer>
@@ -395,6 +395,7 @@ Vue.component('radius-users', {
</div>
</div>
</radius-modal>
<radius-modal :show="showRouterModal" :title="'Router Management - ' + (routerData.username || '')" @close="closeRouterModal" modal-class="modal-card-wide">
<div class="modal-body-scrollable">
<div v-if="routerLoading" class="table-placeholder" style="min-height: 300px;">
@@ -406,79 +407,159 @@ Vue.component('radius-users', {
<div style="margin-top: 16px;">Kein Router mit dieser IP gefunden</div>
</div>
<div v-else>
<div class="kv-redesign">
<div class="kv-row"><span class="kv-label">Hersteller</span><code class="kv-value">{{ routerDevice.deviceInfo.manufacturer || '—' }}</code></div>
<div class="kv-row"><span class="kv-label">Modell</span><code class="kv-value">{{ routerDevice.deviceInfo.productClass || '—' }}</code></div>
<div class="kv-row"><span class="kv-label">Hardware Version</span><code class="kv-value">{{ routerDevice.deviceInfo.hardwareVersion || '—' }}</code></div>
<div class="kv-row"><span class="kv-label">Software Version</span><code class="kv-value">{{ routerDevice.deviceInfo.softwareVersion || '—' }}</code></div>
<div class="kv-row"><span class="kv-label">Seriennummer</span><code class="kv-value">{{ routerDevice.deviceInfo.serialNumber || '—' }}</code></div>
<div class="kv-row"><span class="kv-label">Device ID</span>
<div class="kv-value inline-copy">
<code>{{ routerDevice.deviceId || '—' }}</code>
<button v-if="routerDevice.deviceId" class="icon-btn sm" @click="copy(routerDevice.deviceId, $event)" data-tooltip="Kopieren"><i class="fa-duotone fa-copy copy-icon"></i><i class="fa-duotone fa-check check-icon"></i></button>
<div style="padding: 8px;">
<div class="kv-redesign grid g-2 cols-2">
<div class="kv-row"><span class="kv-label">Hardware Version</span><code class="kv-value">{{ routerDevice.deviceInfo.hardwareVersion || '—' }}</code></div>
<div class="kv-row"><span class="kv-label">Software Version</span><code class="kv-value">{{ routerDevice.deviceInfo.softwareVersion || '—' }}</code></div>
<div class="kv-row full-width"><span class="kv-label">Seriennummer</span><code class="kv-value">{{ routerDevice.deviceInfo.serialNumber || '—' }}</code></div>
<div class="kv-row"><span class="kv-label">Device ID</span>
<div class="kv-value inline-copy">
<code>{{ routerDevice.deviceId || '—' }}</code>
<button v-if="routerDevice.deviceId" class="icon-btn sm" @click="copy(routerDevice.deviceId, $event)" data-tooltip="Kopieren"><i class="fa-duotone fa-copy copy-icon"></i><i class="fa-duotone fa-check check-icon"></i></button>
</div>
</div>
<div class="kv-row"><span class="kv-label">Externe IP</span>
<div class="kv-value inline-copy">
<code>{{ routerDevice.ip || '—' }}</code>
<button v-if="routerDevice.ip" class="icon-btn sm" @click="copy(routerDevice.ip, $event)" data-tooltip="Kopieren"><i class="fa-duotone fa-copy copy-icon"></i><i class="fa-duotone fa-check check-icon"></i></button>
</div>
</div>
<div class="kv-row"><span class="kv-label">Management IP</span>
<div class="kv-value inline-copy">
<code>{{ routerDevice.managementIp || '—' }}</code>
<button v-if="routerDevice.managementIp" class="icon-btn sm" @click="copy(routerDevice.managementIp, $event)" data-tooltip="Kopieren"><i class="fa-duotone fa-copy copy-icon"></i><i class="fa-duotone fa-check check-icon"></i></button>
</div>
</div>
</div>
</div>
<div class="kv-row"><span class="kv-label">Externe IP</span>
<div class="kv-value inline-copy">
<code>{{ routerDevice.ip || '—' }}</code>
<button v-if="routerDevice.ip" class="icon-btn sm" @click="copy(routerDevice.ip, $event)" data-tooltip="Kopieren"><i class="fa-duotone fa-copy copy-icon"></i><i class="fa-duotone fa-check check-icon"></i></button>
</div>
</div>
<div class="kv-row"><span class="kv-label">Management IP</span>
<div class="kv-value inline-copy">
<code>{{ routerDevice.managementIp || '—' }}</code>
<button v-if="routerDevice.managementIp" class="icon-btn sm" @click="copy(routerDevice.managementIp, $event)" data-tooltip="Kopieren"><i class="fa-duotone fa-copy copy-icon"></i><i class="fa-duotone fa-check check-icon"></i></button>
</div>
</div>
<div class="kv-row"><span class="kv-label">Management Username</span>
<div class="kv-value inline-copy">
<code v-if="!loadingUsername">{{ managementUsername || '—' }}</code>
<div v-else class="skeleton-line" style="width: 100px; height: 16px;"></div>
<button v-if="managementUsername && !loadingUsername" class="icon-btn sm" @click="copy(managementUsername, $event)" data-tooltip="Kopieren"><i class="fa-duotone fa-copy copy-icon"></i><i class="fa-duotone fa-check check-icon"></i></button>
<button class="icon-btn sm" @click="fetchManagementUsername" :disabled="loadingUsername" data-tooltip="Aktualisieren" data-tooltip-align="left">
<i class="fa-duotone fa-arrows-rotate" :class="{'fa-spin': loadingUsername}"></i>
</button>
</div>
</div>
</div>
<div class="mt-3">
<h4 style="margin-bottom: 12px; font-size: 14px; font-weight: 600;">Router Aktionen</h4>
<div class="grid g-3 cols-3">
<button class="ghost-btn" @click="refreshRouter" :disabled="routerActionLoading" style="padding: 12px;">
<i class="fa-duotone fa-arrows-rotate"></i> Aktualisieren
</button>
<button class="ghost-btn" @click="rebootRouter" :disabled="routerActionLoading" style="padding: 12px;">
<i class="fa-duotone fa-power-off"></i> Neustart
</button>
<button class="ghost-btn" @click="pingRouter" :disabled="routerActionLoading" style="padding: 12px;">
<i class="fa-duotone fa-signal-bars"></i> Ping
</button>
<button class="danger-btn" @click="confirmFactoryReset" :disabled="routerActionLoading" style="padding: 12px;">
<i class="fa-duotone fa-triangle-exclamation"></i> Factory Reset
</button>
<button class="ghost-btn" @click="showParameterModal = true" :disabled="routerActionLoading" style="padding: 12px;">
<i class="fa-duotone fa-sliders"></i> Parameter lesen
</button>
<button class="ghost-btn" @click="showSetParameterModal = true" :disabled="routerActionLoading" style="padding: 12px;">
<i class="fa-duotone fa-pen-to-square"></i> Parameter setzen
</button>
</div>
</div>
<div v-if="pingResult" class="mt-3">
<h4 style="margin-bottom: 12px; font-size: 14px; font-weight: 600;">Ping Ergebnis</h4>
<div class="kv-redesign">
<div class="kv-row"><span class="kv-label">Pakete gesendet</span><code class="kv-value">{{ pingResult.packetsTransmitted }}</code></div>
<div class="kv-row"><span class="kv-label">Pakete empfangen</span><code class="kv-value">{{ pingResult.packetsReceived }}</code></div>
<div class="kv-row"><span class="kv-label">Paketverlust</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 class="mt-3">
<h4 style="margin-bottom: 12px; font-size: 14px; font-weight: 600;">Router Aktionen</h4>
<div class="grid g-2 cols-3">
<button class="ghost-btn" @click="runRemoteAccess" :disabled="routerActionLoading || speedtestLoading">
<i class="fa-duotone fa-key"></i> Remote-Zugriff
</button>
<button class="ghost-btn" @click="refreshRouter" :disabled="routerActionLoading || speedtestLoading">
<i class="fa-duotone fa-arrows-rotate"></i> Aktualisieren
</button>
<button class="ghost-btn" @click="rebootRouter" :disabled="routerActionLoading || speedtestLoading">
<i class="fa-duotone fa-power-off"></i> Neustart
</button>
<button class="ghost-btn" @click="pingRouter" :disabled="routerActionLoading || speedtestLoading">
<i class="fa-duotone fa-signal-bars"></i> Ping
</button>
<button class="ghost-btn" @click="showParameterModal = true" :disabled="routerActionLoading || speedtestLoading">
<i class="fa-duotone fa-sliders"></i> Parameter lesen
</button>
<button class="ghost-btn" @click="showSetParameterModal = true" :disabled="routerActionLoading || speedtestLoading">
<i class="fa-duotone fa-pen-to-square"></i> Parameter setzen
</button>
<button class="ghost-btn" @click="runSpeedtest" :disabled="routerActionLoading || speedtestLoading">
<i class="fa-duotone fa-gauge-high"></i> Speedtest
</button>
<button class="danger-btn" @click="confirmFactoryReset" :disabled="routerActionLoading || speedtestLoading">
<i class="fa-duotone fa-triangle-exclamation"></i> Factory Reset
</button>
</div>
</div>
</div>
</div>
</div>
</radius-modal>
<radius-modal :show="showPingModal" title="Ping Ergebnis" @close="showPingModal = false">
<div v-if="routerActionLoading && !pingResult" class="table-placeholder" style="height: 150px;">
<div class="btn-loader" style="width: 40px; height: 40px;"></div>
<div class="mt-3">Ping läuft...</div>
</div>
<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>
</radius-modal>
<radius-modal :show="showSpeedtestModal" title="Speedtest Ergebnis" @close="showSpeedtestModal = false" modal-class="modal-card-wide">
<div v-if="speedtestLoading && speedtestHistory.length === 0" class="table-placeholder" style="height: 200px;">
<div class="btn-loader" style="width: 40px; height: 40px;"></div>
<div class="mt-3">Speedtest wird initialisiert...</div>
</div>
<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>
</radius-modal>
<radius-modal :show="showRemoteAccessModal" title="Remote Zugriff Konfiguration" @close="showRemoteAccessModal = false">
<div v-if="remoteAccessLoading" class="table-placeholder" style="height: 200px;">
<div class="btn-loader" style="width: 40px; height: 40px;"></div>
<div class="mt-3">{{ remoteAccessStep }}</div>
</div>
<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>
<button class="icon-btn sm" @click="copy(remoteAccessResult.link, $event)" data-tooltip="Kopieren">
<i class="fa-duotone fa-copy copy-icon"></i><i class="fa-duotone fa-check check-icon"></i>
</button>
</div>
</div>
<div class="kv-row">
<span class="kv-label">Username</span>
<div class="kv-value inline-copy">
<code class="mono">{{ remoteAccessResult.username }}</code>
<button class="icon-btn sm" @click="copy(remoteAccessResult.username, $event)" data-tooltip="Kopieren">
<i class="fa-duotone fa-copy copy-icon"></i><i class="fa-duotone fa-check check-icon"></i>
</button>
</div>
</div>
<div class="kv-row">
<span class="kv-label">Password</span>
<div class="kv-value inline-copy">
<code class="mono">{{ remoteAccessResult.password }}</code>
<button class="icon-btn sm" @click="copy(remoteAccessResult.password, $event)" data-tooltip="Kopieren">
<i class="fa-duotone fa-copy copy-icon"></i><i class="fa-duotone fa-check check-icon"></i>
</button>
</div>
</div>
</div>
</div>
<div v-else class="table-placeholder" style="height: 200px;">
Ein Fehler ist aufgetreten.
</div>
</radius-modal>
<radius-modal :show="showParameterModal" title="Parameter lesen" @close="showParameterModal = false">
<div>
<div class="field">
@@ -564,6 +645,16 @@ Vue.component('radius-users', {
routerData: {},
routerDevice: null,
pingResult: null,
speedtestLoading: false,
speedtestResult: null,
speedtestHistory: [],
speedtestHasStarted: false,
showPingModal: false,
showSpeedtestModal: false,
showRemoteAccessModal: false,
remoteAccessLoading: false,
remoteAccessResult: null,
remoteAccessStep: '',
showParameterModal: false,
parameterName: '',
showSetParameterModal: false,
@@ -972,6 +1063,8 @@ Vue.component('radius-users', {
this.routerData = item;
this.routerDevice = null;
this.pingResult = null;
this.speedtestResult = null;
this.speedtestLoading = false;
this.managementUsername = '';
try {
@@ -986,8 +1079,7 @@ Vue.component('radius-users', {
const deviceData = await deviceResponse.json();
if (deviceData.success) {
this.routerDevice = deviceData;
// Automatically fetch management username
await this.fetchManagementUsername();
// Management Username removed
}
}
}
@@ -1004,6 +1096,8 @@ Vue.component('radius-users', {
this.routerData = {};
this.routerDevice = null;
this.pingResult = null;
this.speedtestResult = null;
this.speedtestLoading = false;
this.showParameterModal = false;
this.showSetParameterModal = false;
this.parameterName = '';
@@ -1070,6 +1164,7 @@ Vue.component('radius-users', {
const pingIp = this.routerDevice.managementIp || this.routerDevice.ip;
if (!pingIp) return;
this.showPingModal = true; // Open modal immediately
this.routerActionLoading = true;
this.pingResult = null;
try {
@@ -1151,14 +1246,9 @@ Vue.component('radius-users', {
}
this.routerActionLoading = false;
},
async setParameter() {
if (!this.routerDevice || !this.routerDevice.deviceId || !this.setParameterName || !this.setParameterValue) return;
this.routerActionLoading = true;
async setParameterValues(parameters) {
if (!this.routerDevice || !this.routerDevice.deviceId || !parameters) return false;
try {
const parameters = {};
parameters[this.setParameterName] = this.setParameterValue;
const response = await fetch(`${window.TT_CONFIG.BASE_PATH}/Radius/genieacsSetParameters`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
@@ -1167,73 +1257,272 @@ Vue.component('radius-users', {
parameters: parameters
})
});
if (response.ok) {
const data = await response.json();
if (data.success) {
window.notify('success', 'Parameter erfolgreich gesetzt');
this.showSetParameterModal = false;
this.setParameterName = '';
this.setParameterValue = '';
} else {
window.notify('error', data.message || 'Fehler beim Setzen des Parameters');
}
}
return response.ok && (await response.json()).success;
} catch (e) {
console.error('Error setting parameter:', e);
window.notify('error', 'Fehler beim Setzen des Parameters');
console.error('Error setting parameters:', e);
return false;
}
},
async setParameter() {
if (!this.routerDevice || !this.routerDevice.deviceId || !this.setParameterName || !this.setParameterValue) return;
this.routerActionLoading = true;
const parameters = {};
parameters[this.setParameterName] = this.setParameterValue;
if (await this.setParameterValues(parameters)) {
window.notify('success', 'Parameter erfolgreich gesetzt');
this.showSetParameterModal = false;
this.setParameterName = '';
this.setParameterValue = '';
} else {
window.notify('error', 'Fehler beim Setzen des Parameters');
}
this.routerActionLoading = false;
},
async fetchManagementUsername() {
async runSpeedtest() {
if (!this.routerDevice || !this.routerDevice.deviceId) return;
this.loadingUsername = true;
this.showSpeedtestModal = true; // Open modal immediately
this.speedtestLoading = true;
this.speedtestResult = null;
this.speedtestHistory = [];
this.speedtestHasStarted = false;
try {
const response = await fetch(`${window.TT_CONFIG.BASE_PATH}/Radius/genieacsGetParameters`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
deviceId: this.routerDevice.deviceId,
parameters: ['InternetGatewayDevice.User.1.Username']
})
// 1. Set CPE Parameters
const params = {
'InternetGatewayDevice.X_AVM-DE_SpeedtestServer.UDP.Start': 1,
'InternetGatewayDevice.X_AVM-DE_SpeedtestServer.UDP.StartBidirect': 1,
'InternetGatewayDevice.X_AVM-DE_SpeedtestServer.UDP.WANAccess': true
};
if (!await this.setParameterValues(params)) {
throw new Error("Konnte Speedtest-Parameter nicht setzen");
}
// 2. Trigger Speedtest Server
const ip = this.routerDevice.ip; // External IP
if (!ip) throw new Error("Keine IP-Adresse gefunden");
const stRes = await fetch(`${window.TT_CONFIG.BASE_PATH}/Radius/genieacsRunSpeedtest`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ ip: ip })
});
if (response.ok) {
const data = await response.json();
if (data.success) {
window.notify('success', 'Username-Abfrage gestartet. Bitte warten Sie einen Moment und klicken Sie erneut auf Aktualisieren.');
// Wait a bit and then fetch the device data to get the updated value
setTimeout(async () => {
try {
const deviceResponse = await fetch(`${window.TT_CONFIG.BASE_PATH}/Radius/genieacsGetDeviceInfo?deviceId=${encodeURIComponent(this.routerDevice.deviceId)}`);
if (deviceResponse.ok) {
const deviceInfo = await deviceResponse.json();
if (deviceInfo.success && deviceInfo.fullData) {
// Extract username from device data
const usernameData = deviceInfo.fullData['InternetGatewayDevice.User.1.Username'];
if (usernameData && usernameData.value && usernameData.value[0]) {
this.managementUsername = usernameData.value[0];
if (!stRes.ok) {
const err = await stRes.json();
throw new Error(err.message || "Speedtest-Server Fehler");
}
// 3. Poll for result
this.pollSpeedtestResult();
} catch (e) {
window.notify('error', e.message);
this.speedtestLoading = false;
}
},
async pollSpeedtestResult() {
let attempts = 0;
const maxAttempts = 240; // 2 min
const resultParam = 'InternetGatewayDevice.X_AVM-DE_SpeedtestServer.UDP.Result';
const poll = async () => {
if (!this.showSpeedtestModal) return;
if (attempts >= maxAttempts) {
this.speedtestLoading = false;
window.notify('error', 'Speedtest Zeitüberschreitung');
return;
}
attempts++;
try {
await fetch(`${window.TT_CONFIG.BASE_PATH}/Radius/genieacsGetParameters`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
deviceId: this.routerDevice.deviceId,
parameters: [resultParam]
})
});
setTimeout(async () => {
try {
const val = await this.fetchDeviceParameterValue(resultParam);
if (val && typeof val === 'string' && val.includes("BPS")) {
const parsed = this.parseSpeedtestResult(val);
if (parsed) {
this.speedtestHistory.push(parsed);
this.$nextTick(() => {
if (this.$refs.speedtestBottom) {
this.$refs.speedtestBottom.scrollIntoView({ behavior: 'smooth' });
}
});
if (parsed.bps > 0) this.speedtestHasStarted = true;
if (this.speedtestHasStarted && parsed.bps === 0) {
this.speedtestLoading = false;
window.notify('success', 'Speedtest abgeschlossen');
return;
}
}
} catch (e) {
console.error('Error fetching updated device info:', e);
} finally {
this.loadingUsername = false;
}
}
}, 3000);
} else {
this.loadingUsername = false;
window.notify('error', data.message || 'Fehler beim Lesen des Usernames');
} catch(e) { console.error(e); }
if (this.speedtestLoading) setTimeout(poll, 500);
}, 500);
} catch (e) {
console.error(e);
if (this.speedtestLoading) setTimeout(poll, 500);
}
};
poll();
},
async fetchDeviceParameterValue(paramName) {
if (!this.routerDevice || !this.routerDevice.deviceId) return null;
try {
const deviceResponse = await fetch(`${window.TT_CONFIG.BASE_PATH}/Radius/genieacsGetDeviceInfo?deviceId=${encodeURIComponent(this.routerDevice.deviceId)}`);
if (deviceResponse.ok) {
const deviceInfo = await deviceResponse.json();
if (deviceInfo.success && deviceInfo.fullData) {
const paramData = deviceInfo.fullData[paramName];
if (paramData && paramData.value && paramData.value[0]) {
return paramData.value[0];
}
}
}
} catch (e) {
console.error('Error fetching management username:', e);
window.notify('error', 'Fehler beim Lesen des Usernames');
this.loadingUsername = false;
console.error('Error fetching parameter value:', e);
}
return null;
},
formatBits(bps) {
if (!bps) return '0 Mbit/s';
// 1 Mbit = 1,000,000 bits (Standard network speed unit)
const mbits = bps / 1000000;
return mbits.toFixed(2) + ' Mbit/s';
},
parseSpeedtestResult(raw) {
try {
const bpsMatch = raw.match(/BPS\s+(\d+)/);
const bytesMatch = raw.match(/Bytes\s+(\d+)/);
const packetsMatch = raw.match(/Packets\s+(\d+)/);
if (bpsMatch) {
const bps = parseInt(bpsMatch[1]);
const bytes = bytesMatch ? parseInt(bytesMatch[1]) : 0;
const packets = packetsMatch ? parseInt(packetsMatch[1]) : 0;
return {
raw: raw,
bps: bps,
bpsFormatted: this.formatBits(bps),
bytes: bytes,
bytesFormatted: window.RadiusUtils.formatBytes(bytes),
packets: packets
};
}
} catch (e) {
console.error("Error parsing speedtest result", e);
}
return null;
},
generatePassword(length) {
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
let ret = "";
for (let i = 0; i < length; ++i) {
ret += chars.charAt(Math.floor(Math.random() * chars.length));
}
return ret;
},
async runRemoteAccess() {
if (!this.routerDevice || !this.routerDevice.deviceId) return;
this.showRemoteAccessModal = true;
this.remoteAccessLoading = true;
this.remoteAccessStep = 'Konfiguriere Parameter...';
this.remoteAccessResult = null;
const password = this.generatePassword(12);
const timestamp = Math.floor(Date.now() / 1000).toString();
try {
const params = {
'InternetGatewayDevice.User.1.Enable': 1,
'InternetGatewayDevice.User.1.Password': password,
'InternetGatewayDevice.User.1.RemoteAccessCapable': 1,
'InternetGatewayDevice.User.1.Username': timestamp
};
const success = await this.setParameterValues(params);
if (!success) throw new Error("Fehler beim Setzen der Parameter");
this.remoteAccessStep = 'Warte auf TR069-User...';
this.pollRemoteUsername(password);
} catch (e) {
this.remoteAccessLoading = false;
window.notify('error', e.message);
}
},
async pollRemoteUsername(password) {
let attempts = 0;
const maxAttempts = 60; // 60 * 2s = 120s
const userParam = 'InternetGatewayDevice.User.1.Username';
const poll = async () => {
if (!this.showRemoteAccessModal) return;
if (attempts >= maxAttempts) {
this.remoteAccessLoading = false;
window.notify('error', 'Remote Access Zeitüberschreitung');
return;
}
attempts++;
try {
// Refresh param
await fetch(`${window.TT_CONFIG.BASE_PATH}/Radius/genieacsGetParameters`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
deviceId: this.routerDevice.deviceId,
parameters: [userParam]
})
});
setTimeout(async () => {
try {
const val = await this.fetchDeviceParameterValue(userParam);
if (val && typeof val === 'string' && val.startsWith('TR069-')) {
this.remoteAccessResult = {
link: `https://${this.routerDevice.ip}:9090`,
username: val,
password: password
};
this.remoteAccessLoading = false;
return;
}
} catch(e) { console.error(e); }
if (this.remoteAccessLoading) setTimeout(poll, 2000);
}, 2000);
} catch (e) {
console.error(e);
if (this.remoteAccessLoading) setTimeout(poll, 2000);
}
};
poll();
},
async fetchManagementUsername() {
// Kept as requested method, but removed call in openRouterManagement if no longer needed
// User asked to remove "row", usually implies logic removal too to save resources?
// But I will keep the method definition just in case, but I already removed the UI row.
// Since the user asked to remove the "Row", I removed the UI part.
// The automatic fetching was in openRouterManagement, I removed it there too.
}
}
});