Compare commits
3 Commits
feature/up
...
78594c5bca
| Author | SHA1 | Date | |
|---|---|---|---|
| 78594c5bca | |||
| 96d559f819 | |||
| a3a7298447 |
@ -133,6 +133,20 @@
|
|||||||
<p class="text-center text-muted mt-1">Lade Update-Status...</p>
|
<p class="text-center text-muted mt-1">Lade Update-Status...</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Enrollment Section -->
|
||||||
|
<div id="enrollmentSection" class="hidden" style="margin-bottom: 1rem; padding: 1rem; background: rgba(255, 255, 255, 0.05); border-radius: var(--radius-sm); border: 1px dashed var(--color-border);">
|
||||||
|
<h4 style="margin-top: 0; margin-bottom: 0.5rem;"><i data-lucide="link"></i> Gerät registrieren (Enrollment)</h4>
|
||||||
|
<p class="text-muted" style="font-size: 0.875rem; margin-bottom: 0.5rem;">
|
||||||
|
Dieses Gerät ist noch nicht beim Update-Service registriert. Bitte geben Sie einen gültigen Enrollment-Token ein.
|
||||||
|
</p>
|
||||||
|
<form id="enrollmentForm" style="display: flex; gap: 0.5rem; align-items: center;">
|
||||||
|
<input type="text" id="enrollToken" placeholder="Enrollment-Token eingeben..." required style="flex: 1;" />
|
||||||
|
<button type="submit" class="small">
|
||||||
|
<i data-lucide="check"></i> Registrieren
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<button id="checkUpdateBtn" type="button">
|
<button id="checkUpdateBtn" type="button">
|
||||||
<i data-lucide="search"></i>
|
<i data-lucide="search"></i>
|
||||||
@ -395,6 +409,8 @@
|
|||||||
// Update management
|
// Update management
|
||||||
async function refreshUpdateStatus() {
|
async function refreshUpdateStatus() {
|
||||||
const statusDiv = document.getElementById('updateStatus');
|
const statusDiv = document.getElementById('updateStatus');
|
||||||
|
// Do not clear the div if we are just refreshing to avoid flickering, maybe?
|
||||||
|
// Actually, loading spinner is fine.
|
||||||
statusDiv.innerHTML = '<div class="spinner" style="margin: 0 auto;"></div><p class="text-center text-muted mt-1">Lade Update-Status...</p>';
|
statusDiv.innerHTML = '<div class="spinner" style="margin: 0 auto;"></div><p class="text-center text-muted mt-1">Lade Update-Status...</p>';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -407,6 +423,18 @@
|
|||||||
: data.last_status === 'failed'
|
: data.last_status === 'failed'
|
||||||
? '<span class="badge error"><i data-lucide="x-circle"></i> Fehlgeschlagen</span>'
|
? '<span class="badge error"><i data-lucide="x-circle"></i> Fehlgeschlagen</span>'
|
||||||
: '<span class="badge neutral"><i data-lucide="minus-circle"></i> Unbekannt</span>';
|
: '<span class="badge neutral"><i data-lucide="minus-circle"></i> Unbekannt</span>';
|
||||||
|
|
||||||
|
const enrolledBadge = data.enrolled
|
||||||
|
? '<span class="badge success"><i data-lucide="link"></i> Verbunden</span>'
|
||||||
|
: '<span class="badge warning"><i data-lucide="link-2-off"></i> Nicht registriert</span>';
|
||||||
|
|
||||||
|
// Toggle Enrollment Section
|
||||||
|
const enrollSection = document.getElementById('enrollmentSection');
|
||||||
|
if (data.enrolled) {
|
||||||
|
enrollSection.classList.add('hidden');
|
||||||
|
} else {
|
||||||
|
enrollSection.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
statusDiv.innerHTML = `
|
statusDiv.innerHTML = `
|
||||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem;">
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem;">
|
||||||
@ -414,6 +442,10 @@
|
|||||||
<div class="text-muted" style="font-size: 0.75rem; text-transform: uppercase; margin-bottom: 0.25rem;">Version</div>
|
<div class="text-muted" style="font-size: 0.75rem; text-transform: uppercase; margin-bottom: 0.25rem;">Version</div>
|
||||||
<div style="color: var(--color-accent); font-weight: 600;">${data.current_version}</div>
|
<div style="color: var(--color-accent); font-weight: 600;">${data.current_version}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="text-muted" style="font-size: 0.75rem; text-transform: uppercase; margin-bottom: 0.25rem;">Registrierung</div>
|
||||||
|
<div>${enrolledBadge}</div>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="text-muted" style="font-size: 0.75rem; text-transform: uppercase; margin-bottom: 0.25rem;">Letzter Status</div>
|
<div class="text-muted" style="font-size: 0.75rem; text-transform: uppercase; margin-bottom: 0.25rem;">Letzter Status</div>
|
||||||
<div>${statusBadge}</div>
|
<div>${statusBadge}</div>
|
||||||
@ -637,6 +669,35 @@
|
|||||||
|
|
||||||
document.getElementById('refreshLogsBtn').addEventListener('click', refreshUpdateLogs);
|
document.getElementById('refreshLogsBtn').addEventListener('click', refreshUpdateLogs);
|
||||||
|
|
||||||
|
document.getElementById('enrollmentForm').addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const tokenInput = document.getElementById('enrollToken');
|
||||||
|
const token = tokenInput.value.trim();
|
||||||
|
const btn = e.target.querySelector('button');
|
||||||
|
|
||||||
|
if (!token) return;
|
||||||
|
|
||||||
|
const originalHTML = btn.innerHTML;
|
||||||
|
btn.innerHTML = '<div class="spinner"></div> Registriere...';
|
||||||
|
btn.disabled = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await api('/update/enroll', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ enroll_token: token })
|
||||||
|
});
|
||||||
|
showToast('Gerät erfolgreich registriert!', 'success');
|
||||||
|
tokenInput.value = '';
|
||||||
|
await refreshUpdateStatus();
|
||||||
|
} catch (err) {
|
||||||
|
showToast('Registrierung fehlgeschlagen: ' + err.message, 'error');
|
||||||
|
} finally {
|
||||||
|
btn.innerHTML = originalHTML;
|
||||||
|
btn.disabled = false;
|
||||||
|
lucide.createIcons();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
checkSession();
|
checkSession();
|
||||||
checkOidcStatus();
|
checkOidcStatus();
|
||||||
|
|||||||
68
scripts/manual_enroll.py
Executable file
68
scripts/manual_enroll.py
Executable file
@ -0,0 +1,68 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Add project root to sys.path to allow imports
|
||||||
|
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
sys.path.insert(0, str(PROJECT_ROOT))
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
from backend.settings import get_settings
|
||||||
|
from backend.update import _ensure_parent
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Manual Enrollment Client")
|
||||||
|
parser.add_argument("--url", default="https://update.wlkns.org", help="Update Service URL")
|
||||||
|
parser.add_argument("--token", required=True, help="Enrollment Token")
|
||||||
|
parser.add_argument("--project", default="safe-kiddo-control", help="Project ID")
|
||||||
|
parser.add_argument("--insecure", action="store_true", help="Disable SSL verification")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
settings = get_settings()
|
||||||
|
|
||||||
|
print(f"[*] Enrolling client {os.uname().nodename}...")
|
||||||
|
print(f"[*] Service URL: {args.url}")
|
||||||
|
print(f"[*] Project ID: {args.project}")
|
||||||
|
|
||||||
|
enroll_url = f"{args.url}/v1/enroll"
|
||||||
|
payload = {
|
||||||
|
"project_id": args.project,
|
||||||
|
"client_id": os.uname().nodename,
|
||||||
|
"software_id": "safe-kiddo",
|
||||||
|
"enroll_token": args.token,
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
with httpx.Client(verify=not args.insecure, timeout=10.0) as client:
|
||||||
|
response = client.post(enroll_url, json=payload)
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
print(f"[!] Error {response.status_code}: {response.text}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
token = data.get("token")
|
||||||
|
if not token:
|
||||||
|
print("[!] Error: No token received in response")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
token_path = Path(settings.update_token_file)
|
||||||
|
_ensure_parent(token_path)
|
||||||
|
token_path.write_text(token, encoding="utf-8")
|
||||||
|
|
||||||
|
# Make sure only root/service user can read it
|
||||||
|
os.chmod(token_path, 0o600)
|
||||||
|
|
||||||
|
print(f"[+] Success! Token saved to {token_path}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[!] Exception: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user