Daynivo names for the files on the SD card and the command line tool
- daynivo-config.json, daynivo-authorized_keys and daynivo-network-reset on the boot partition; errors go to daynivo-config.error.txt. - daynivo-kiosk-ctl, with voxip-kiosk-ctl as a symlink to it. - The voxip-* names keep working, so existing SD cards and instructions stay valid; when both config files are present the Daynivo one is applied last. - Internal names (services, /etc/voxip-kiosk, user voxip) are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
348644a48a
commit
b0840d240e
17 files changed
+108
-76
No files matched your search
@@ -1,4 +1,4 @@
|
||||
"""voxip-kiosk-ctl — command-line management over SSH (run with sudo)."""
|
||||
"""daynivo-kiosk-ctl — command-line management over SSH (run with sudo)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -33,7 +33,7 @@ def cmd_status(args) -> int:
|
||||
print(f"Apparaat: {config['device_name']}")
|
||||
print(f"URL: {config['url'] if args.show_secrets else mask_url(config['url'])}")
|
||||
print(f"Beheer: {'https' if config['admin_https'] else 'http'}://{os.uname().nodename}.local:{config['admin_port']}")
|
||||
print(f"Wachtwoord: {'ingesteld' if paths.admin_file.exists() else 'NOG NIET INGESTELD (setupcode: sudo voxip-kiosk-ctl setup-code)'}")
|
||||
print(f"Wachtwoord: {'ingesteld' if paths.admin_file.exists() else 'NOG NIET INGESTELD (setupcode: sudo daynivo-kiosk-ctl setup-code)'}")
|
||||
for key, info in services.summary().items():
|
||||
print(f"{info['unit']:<34} {info['label']} (herstarts: {info['restarts']})")
|
||||
if agent:
|
||||
@@ -138,7 +138,7 @@ def cmd_version(_args) -> int:
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(prog="voxip-kiosk-ctl", description="Beheer van Daynivo Kiosk OS vanaf de commandoregel.")
|
||||
parser = argparse.ArgumentParser(prog="daynivo-kiosk-ctl", description="Beheer van Daynivo Kiosk OS vanaf de commandoregel.")
|
||||
sub = parser.add_subparsers(dest="command", required=True)
|
||||
p = sub.add_parser("status", help="toon de status")
|
||||
p.add_argument("--show-secrets", action="store_true", help="toon de volledige URL inclusief token")
|
||||
|
||||
+28
-21
@@ -33,9 +33,11 @@ UPDATE_UNIT = "voxip-kiosk-update-now"
|
||||
UPDATER_PATH = "/usr/lib/voxip-kiosk/voxip-kiosk-updater"
|
||||
ACTIVATE_UNIT = "voxip-network-activate"
|
||||
BOOT_DIRS = (Path("/boot/firmware"), Path("/boot"))
|
||||
RESET_TRIGGER = "voxip-network-reset"
|
||||
IMPORT_CONFIG = "voxip-config.json"
|
||||
IMPORT_KEYS = "voxip-authorized_keys"
|
||||
# Files on the boot partition. The voxip-* names are from before the rename to
|
||||
# Daynivo and keep working, so existing SD cards and instructions stay valid.
|
||||
RESET_TRIGGERS = ("daynivo-network-reset", "voxip-network-reset")
|
||||
IMPORT_CONFIGS = ("voxip-config.json", "daynivo-config.json") # applied in this order: the Daynivo name wins
|
||||
IMPORT_KEYS = ("voxip-authorized_keys", "daynivo-authorized_keys")
|
||||
SSH_KEY_TYPES = ("ssh-ed25519", "ssh-rsa", "ecdsa-sha2-", "sk-ssh-ed25519@openssh.com", "sk-ecdsa-sha2-")
|
||||
|
||||
# Verbs the web interface may call through sudo. Everything else is only
|
||||
@@ -306,15 +308,16 @@ class Helper:
|
||||
self.network_restore(boot=True)
|
||||
result["rolled_back"] = True
|
||||
boot = self._boot_dir()
|
||||
trigger = boot / RESET_TRIGGER if boot else None
|
||||
if trigger and trigger.exists():
|
||||
triggers = [boot / name for name in RESET_TRIGGERS if (boot / name).exists()] if boot else []
|
||||
if triggers:
|
||||
stash = self.paths.network_state_dir / f"disabled-{int(time.time())}"
|
||||
stash.mkdir(parents=True, mode=0o700)
|
||||
for iface in netconfig.INTERFACES:
|
||||
src = self._keyfile_path(iface)
|
||||
if src.exists():
|
||||
shutil.move(str(src), str(stash / src.name))
|
||||
trigger.unlink()
|
||||
for trigger in triggers:
|
||||
trigger.unlink()
|
||||
log(syslog.LOG_WARNING, f"Netwerk-reset uitgevoerd; Daynivo-profielen verplaatst naar {stash}")
|
||||
result["reset"] = True
|
||||
return result
|
||||
@@ -335,12 +338,14 @@ class Helper:
|
||||
if boot is None:
|
||||
return {"imported": []}
|
||||
imported = []
|
||||
conf = boot / IMPORT_CONFIG
|
||||
if conf.exists():
|
||||
for name in IMPORT_CONFIGS:
|
||||
conf = boot / name
|
||||
if not conf.exists():
|
||||
continue
|
||||
try:
|
||||
data = json.loads(conf.read_text(encoding="utf-8-sig"))
|
||||
if not isinstance(data, dict):
|
||||
raise HelperError("voxip-config.json moet een JSON-object zijn")
|
||||
raise HelperError(f"{name} moet een JSON-object zijn")
|
||||
data = dict(data)
|
||||
host = data.pop("hostname", None)
|
||||
country = data.pop("wifi_country", None)
|
||||
@@ -351,22 +356,24 @@ class Helper:
|
||||
self.set_hostname(str(host))
|
||||
if country:
|
||||
self.set_wifi_country(str(country))
|
||||
conf.rename(conf.with_name(IMPORT_CONFIG + ".applied"))
|
||||
imported.append(IMPORT_CONFIG)
|
||||
log(syslog.LOG_NOTICE, "Configuratie geïmporteerd vanaf de bootpartitie")
|
||||
conf.rename(conf.with_name(name + ".applied"))
|
||||
imported.append(name)
|
||||
log(syslog.LOG_NOTICE, f"Configuratie geïmporteerd vanaf de bootpartitie ({name})")
|
||||
except (HelperError, cfg.ConfigError, v.ValidationError, ValueError, OSError) as exc:
|
||||
(boot / "voxip-config.error.txt").write_text(f"{exc}\n", encoding="utf-8")
|
||||
conf.rename(conf.with_name(IMPORT_CONFIG + ".rejected"))
|
||||
log(syslog.LOG_ERR, f"voxip-config.json geweigerd: {exc}")
|
||||
keys = boot / IMPORT_KEYS
|
||||
if keys.exists():
|
||||
(boot / name.replace(".json", ".error.txt")).write_text(f"{exc}\n", encoding="utf-8")
|
||||
conf.rename(conf.with_name(name + ".rejected"))
|
||||
log(syslog.LOG_ERR, f"{name} geweigerd: {exc}")
|
||||
for name in IMPORT_KEYS:
|
||||
keys = boot / name
|
||||
if not keys.exists():
|
||||
continue
|
||||
try:
|
||||
self._import_ssh_keys(keys.read_text(encoding="utf-8-sig"))
|
||||
keys.rename(keys.with_name(IMPORT_KEYS + ".applied"))
|
||||
imported.append(IMPORT_KEYS)
|
||||
keys.rename(keys.with_name(name + ".applied"))
|
||||
imported.append(name)
|
||||
except (HelperError, OSError) as exc:
|
||||
keys.rename(keys.with_name(IMPORT_KEYS + ".rejected"))
|
||||
log(syslog.LOG_ERR, f"SSH-sleutels geweigerd: {exc}")
|
||||
keys.rename(keys.with_name(name + ".rejected"))
|
||||
log(syslog.LOG_ERR, f"SSH-sleutels geweigerd ({name}): {exc}")
|
||||
return {"imported": imported}
|
||||
|
||||
def _import_ssh_keys(self, text: str) -> None:
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
</label>
|
||||
<button type="submit" class="btn btn-primary btn-block">Inloggen</button>
|
||||
</form>
|
||||
<p class="muted small">Wachtwoord vergeten? Log via SSH in en voer <code>sudo voxip-kiosk-ctl reset-password</code> uit.</p>
|
||||
<p class="muted small">Wachtwoord vergeten? Log via SSH in en voer <code>sudo daynivo-kiosk-ctl reset-password</code> uit.</p>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -94,7 +94,7 @@
|
||||
<h2>Herstel bij een onbereikbare kiosk</h2>
|
||||
<ul class="plain">
|
||||
<li>Iedere wijziging wordt na {{ rollback_seconds // 60 }} minuten automatisch teruggedraaid als je hem niet bevestigt, ook als de stroom tussendoor uitvalt.</li>
|
||||
<li>Noodherstel: zet een leeg bestand <code>voxip-network-reset</code> op de bootpartitie van de SD-kaart en start de Pi. Alle Daynivo-netwerkprofielen worden dan verwijderd (terug naar DHCP).</li>
|
||||
<li>Noodherstel: zet een leeg bestand <code>daynivo-network-reset</code> op de bootpartitie van de SD-kaart en start de Pi. Alle Daynivo-netwerkprofielen worden dan verwijderd (terug naar DHCP).</li>
|
||||
</ul>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -4,7 +4,7 @@
|
||||
<section class="card card-narrow">
|
||||
<h1>Welkom bij Daynivo Kiosk Manager</h1>
|
||||
<p>Stel eerst een uniek beheerderswachtwoord in. Je hebt daarvoor de <strong>setupcode</strong> nodig die op het
|
||||
scherm van de kiosk staat (of via SSH: <code>sudo voxip-kiosk-ctl setup-code</code>).</p>
|
||||
scherm van de kiosk staat (of via SSH: <code>sudo daynivo-kiosk-ctl setup-code</code>).</p>
|
||||
{% if error %}<div class="alert alert-error" role="alert">{{ error }}</div>{% endif %}
|
||||
<form method="post" class="form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
|
||||
Reference in new issue
Block a user