feat: personalisierte ISO-Volume-ID aus Organisationsname + Bau-Datum

compute_iso_volid() sanitisiert den Organisationsnamen ISO9660-konform
(Grossbuchstaben/Ziffern/Unterstrich, max. 32 Zeichen, NFKD-Fallback fuer
Umlaute, 'ORG'-Fallback falls kein ASCII-Zeichen uebrig bleibt) und haengt
das Bau-Datum (UTC) an. run_iso_build() reicht das Ergebnis als sechsten
Parameter an build_customer_iso.sh durch, statt des bisher fest codierten
'TUXFLOTTE'-Labels.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Thomas Stallinger 2026-08-25 09:01:53 +02:00
parent 34e5254984
commit c0877548ec

27
app.py
View File

@ -4,9 +4,11 @@ import hashlib
import hmac import hmac
import json import json
import os import os
import re
import secrets import secrets
import subprocess import subprocess
import threading import threading
import unicodedata
from uuid import UUID, uuid4 from uuid import UUID, uuid4
import psycopg import psycopg
@ -2184,11 +2186,35 @@ def cleanup_old_iso_builds(organization_id: str, keep_build_id) -> None:
(organization_id, keep_build_id), (organization_id, keep_build_id),
) )
def compute_iso_volid(organization_name: str) -> str:
"""
ISO9660-Volume-ID fuer eine personalisierte Kunden-ISO: Organisationsname
+ Bau-Datum, damit ein gemountetes/im Dateimanager angezeigtes Medium
erkennbar ist, statt bei der festen generischen Bezeichnung zu bleiben.
ISO9660 erlaubt fuer die Primary-Volume-Descriptor-Volume-ID nur
Grossbuchstaben/Ziffern/Unterstrich und maximal 32 Zeichen - Umlaute
u.ae. werden per NFKD-Normalisierung auf ihr ASCII-Grundzeichen
reduziert (z.B. "Groesse" statt Wegfall), alles andere zu "_"
zusammengefasst. Faellt bei einem Namen ohne jedes ASCII-Zeichen (z.B.
rein kyrillisch/chinesisch) auf "ORG" zurueck statt eine leere
Volume-ID zu erzeugen.
"""
ascii_only = unicodedata.normalize("NFKD", organization_name).encode("ascii", "ignore").decode("ascii")
sanitized = re.sub(r"[^A-Za-z0-9]+", "_", ascii_only).strip("_").upper()
date_suffix = datetime.now(timezone.utc).strftime("%Y%m%d")
max_name_len = 32 - 1 - len(date_suffix) # 32 - "_" - "YYYYMMDD"
sanitized = sanitized[:max_name_len].rstrip("_") or "ORG"
return f"{sanitized}_{date_suffix}"
def run_iso_build(build_id, organization_id: str, activation_code: str, wifi_ssid: str | None, wifi_psk: str | None) -> None: def run_iso_build(build_id, organization_id: str, activation_code: str, wifi_ssid: str | None, wifi_psk: str | None) -> None:
output_path = ISO_BUILD_OUTPUT_DIR / f"{build_id}.iso" output_path = ISO_BUILD_OUTPUT_DIR / f"{build_id}.iso"
update_iso_build(build_id, status="running", mark_started=True) update_iso_build(build_id, status="running", mark_started=True)
organization = fetch_organization(organization_id)
volid = compute_iso_volid(organization["name"] if organization else "TUXFLOTTE")
try: try:
result = subprocess.run( result = subprocess.run(
[ [
@ -2199,6 +2225,7 @@ def run_iso_build(build_id, organization_id: str, activation_code: str, wifi_ssi
activation_code, activation_code,
wifi_ssid or "", wifi_ssid or "",
wifi_psk or "", wifi_psk or "",
volid,
], ],
cwd=TUXFLOTTE_INSTALLER_DIR, cwd=TUXFLOTTE_INSTALLER_DIR,
capture_output=True, capture_output=True,