17 KiB
CS-R Geräteverwaltung
Produktionsnahe interne Geräteverwaltung als kompakter Django-Monolith. Die Anwendung ersetzt die bisherige Excel-Liste und ist für den Betrieb auf einem kleinen Linux-VPS ausgelegt.
Funktionen
- Rollenbasierte Anmeldung mit
ADMIN,EDITORundVIEWER - Schneller Quick-Add für einzelne Geräte oder Serien mit automatisch fortlaufenden, niemals wiederverwendeten Gerätenummern
- Serverseitige Suche, kombinierbare Filter, Sortierung und Pagination
- Kompakte Gerätetabelle mit ein-/ausblendbaren Spalten und Inline-Bearbeitung
- Detail-Drawer und vollständige Bearbeitungsansicht
- Optimistic Concurrency gegen unbemerktes Überschreiben
- Globale, deaktivierbare Auswahlwerte und Mitarbeiterverwaltung
- Benutzerdefinierte Felder ohne neue Datenbankmigration
- Transaktionaler XLSX-Import mit Vorschau und Spaltenzuordnung
- CSV-, XLSX- und ODS-Export für LibreOffice, auch für gefilterte Bestände
- Bearbeitbare Statusfarben mit klar unterscheidbarer Farbpalette
- CPU-Livevorschläge aus einer Intel-/AMD-Masterliste mit Hersteller- und Serienfiltern
- Vollständige Änderungshistorie und gerätebezogenes Audit-Log
- Abgesicherte JSON-API mit getrennten Lese-/Schreibschlüsseln, Ablaufdatum und API-Audit
- Archivierung und Wiederherstellung statt versehentlichem Löschen
- PostgreSQL-Trigramm- und JSONB-Indizes für große Bestände
- Docker Compose mit PostgreSQL, Django/Gunicorn und Traefik/HTTPS
- Healthchecks, persistente Volumes und einfache Backup-/Restore-Skripte
Anleitung für den LAN-Server
Für Kollegen: Lokales Deployment und Serverbetrieb. Die Anleitung enthält Zugang, Update mit Backup, Prüfung, Fehlerbehebung und Datenbankzugriff für den LAN-Server.
Lokale Entwicklung
Voraussetzungen: Python 3.12 oder neuer und PostgreSQL 17, oder Docker Compose. Die Anwendung verwendet auch lokal PostgreSQL. Für bestehende SQLite-Daten siehe Docker-Migration zu PostgreSQL.
python3 -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
# POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD und POSTGRES_HOST
# in der Prozessumgebung setzen (lokal typischerweise POSTGRES_HOST=localhost).
python manage.py migrate
python manage.py seed_initial_data
python manage.py createsuperuser
python manage.py runserver
Ohne PostgreSQL-Verbindung startet die Datenbankfunktion nicht; es wird keine
SQLite-Datenbank mehr automatisch geöffnet oder angelegt. .env wird von Docker
Compose geladen, bei direktem Python-Start müssen die Variablen gesetzt sein.
Der Migrationsleitfaden enthält auch einen lokalen
Docker-Start ohne HTTPS-Domain.
Testdaten können gezielt erzeugt werden:
python manage.py generate_test_devices 5000
Installation auf Netcup/Linux
1. Server vorbereiten
Empfohlen sind Ubuntu 24.04 oder Debian 12, mindestens 2 GB RAM, eine feste IPv4/IPv6-Adresse und eine Domain. In DNS einen A- und optional AAAA-Record der gewünschten Subdomain auf den Server zeigen lassen. Die Ports 22, 80 und 443 in der Firewall öffnen.
Docker gemäß der offiziellen Docker-Anleitung installieren. Anschließend prüfen:
docker --version
docker compose version
2. Projekt und Umgebungsvariablen
git clone <REPOSITORY-URL> csr-inventory
cd csr-inventory
cp .env.example .env
In .env mindestens ändern:
SECRET_KEY: langer Zufallswert, z. B. mitopenssl rand -base64 48POSTGRES_PASSWORD: starkes, einmaliges DatenbankpasswortAPP_HOST: Domain ohne Protokoll/Port, z. B.geraete.example.orgACME_EMAIL: Kontaktadresse für öffentliche TLS-ZertifikateALLOWED_HOSTS: dieselbe Domain sowie127.0.0.1,localhostfür den internen HealthcheckCSRF_TRUSTED_ORIGINS: vollständige HTTPS-URL
Die Datei .env niemals in Git einchecken.
3. Starten
docker compose up -d --build
docker compose ps
docker compose logs -f web traefik
Beim Start wartet der Webdienst auf PostgreSQL, führt Migrationen und das idempotente Seed-Command aus und sammelt statische Dateien. Traefik fordert anschließend automatisch ein TLS-Zertifikat an.
4. Ersten Administrator anlegen
docker compose exec web python manage.py createsuperuser
Danach unter https://IHRE-DOMAIN/accounts/login/ anmelden. Weitere Konten und ihre Rollen ADMIN, EDITOR oder VIEWER werden direkt im Bereich „Benutzer“ verwaltet. Es gibt keine öffentliche Registrierung.
Aktualisierung
Vor jeder Aktualisierung ein Backup erstellen. Danach:
git pull
docker compose up -d --build
docker compose logs -f web
Migrationen und Seed-Daten werden beim Containerstart automatisch angewendet.
Sichere API für interne Skripte
Die API läuft unter derselben HTTPS-Domain wie die Anwendung, zum Beispiel
https://inventory.example.org/api/v1/. Eine zusätzliche FastAPI-Anwendung oder
eine zweite Subdomain ist für diesen internen Anwendungsfall nicht nötig. Das hält
Betrieb, Rechteprüfung und Änderungsprotokoll an einer Stelle.
Browser-Sitzung und API sind bewusst getrennt: Auch ein im Browser angemeldeter Benutzer kann die API ohne API-Schlüssel nicht lesen. Schlüssel werden nur gehasht gespeichert, sind widerrufbar, laufen standardmäßig nach 90 Tagen ab und ihr Klartext wird nur beim Anlegen einmal angezeigt.
Einen reinen Leseschlüssel für ein aktives Konto erstellen:
docker compose exec web python manage.py create_api_key ausgabedokumente JohannesHundshammer --read-only
Einen Schreibschlüssel für ein Konto mit Rolle EDITOR oder ADMIN erstellen:
docker compose exec web python manage.py create_api_key hardware-test-team2 JohannesHundshammer --write --expires-days 30
Den ausgegebenen Schlüssel sofort in einem Secret-Store oder in einer nur für den
jeweiligen Benutzer lesbaren Umgebungsdatei speichern. Niemals in Quellcode, Git,
Tickets oder Logdateien schreiben. Im Django-Admin unter /admin/ können
Administratoren Schlüssel sperren und die letzte Nutzung prüfen.
Beispiel: Gerät 30 lesen:
curl --fail --silent \
-H "Authorization: Api-Key $CSR_API_KEY" \
https://inventory.example.org/api/v1/devices/30/
Für eine Änderung zuerst revision lesen und als expected_revision
zurücksenden. Bestehende Clients können weiterhin updated_at als
expected_updated_at senden. Veraltete Versionen werden mit HTTP 409 abgewiesen:
curl --fail --silent --request PATCH \
-H "Authorization: Api-Key $CSR_API_KEY" \
-H "Content-Type: application/json" \
--data '{"cpu":"Intel Core i5-8500","ram_gb":16,"hwid":"ABC-123","expected_revision":1}' \
https://inventory.example.org/api/v1/devices/30/
Mehrere Geräte atomar ändern (höchstens 500 pro Anfrage):
curl --fail --silent --request POST \
-H "Authorization: Api-Key $CSR_API_KEY" \
-H "Content-Type: application/json" \
--data '{"device_numbers":[30,31,32],"changes":{"status":2,"recipient":5}}' \
https://inventory.example.org/api/v1/devices/bulk-update/
Verfügbare Auswahl-IDs liefert GET /api/v1/choices/. Die Geräteliste unter
GET /api/v1/devices/ unterstützt number, number_prefix, status, type,
employee, changed_since, archived, page und page_size (maximal 100).
Die API nimmt ausschließlich bekannte Gerätefelder an. Benutzerpasswörter, Passwort-Hashes und Kontoverwaltung sind absichtlich nicht Teil der API. Jede Feldänderung enthält in der Änderungshistorie sowohl das zugeordnete Konto als auch den Namen des verwendeten API-Clients.
Geräte per Live-USB erfassen
Für den Werkstattbetrieb gibt es einen eigenständigen USB-Stick
im separaten Ordner ../USB-Intake: Nach Eingabe von Server-IP und API-Schlüssel
startet die Prüfung automatisch. Ergebnisse werden direkt hochgeladen oder
dauerhaft auf dem Stick gespeichert. Gespeicherte
Berichte lassen sich später gesammelt senden, auch per WLAN. Der Archiso-Build
enthält alle benötigten Programme und benötigt keine interne Festplatte.
Laptops und Mini-PCs können ohne interne SSD/HDD von einem Ubuntu-Live-USB
gestartet, kurz auf CPU/RAM geprüft und über die API neu erfasst werden.
../USB-Intake/intake.py sammelt Hardwaredaten und speichert einen offline
nutzbaren Bericht; der Upload erstellt das Gerät mit automatisch vergebener
Nummer und Prüfbericht im Kommentar. Wiederholte Uploads desselben Berichts
erzeugen keine weiteren Geräte. Einrichtung und Ablauf stehen in der
Live-USB-Anleitung.
Backup und Wiederherstellung
Backup-Verzeichnis anlegen und Skripte ausführbar machen:
chmod +x scripts/*.sh
./scripts/backup.sh
Das Ergebnis liegt beispielsweise unter backups/backup_2026-09-19_0200.sql.gz. Backups regelmäßig auf ein anderes System kopieren.
Wiederherstellung zuerst in einer Testumgebung prüfen. Für eine leere Zieldatenbank:
./scripts/restore.sh backups/backup_2026-09-19_0200.sql.gz
Beispiel für einen täglichen Cronjob um 02:00 Uhr:
0 2 * * * cd /opt/csr-inventory && ./scripts/backup.sh >> /var/log/csr-backup.log 2>&1
Tests und Prüfungen
python manage.py test
python manage.py check
python manage.py makemigrations --check --dry-run
Diese Befehle verwenden PostgreSQL. CI prüft PostgreSQL 17 einschließlich
gleichzeitiger Bearbeitungen. Ohne PostgreSQL können reine Unit-Tests explizit
mit python manage.py test --settings=csr_inventory.test_settings ausgeführt
werden; PostgreSQL-Sperrtests werden dabei übersprungen. Für Tests DEBUG=true
und SECURE_SSL_REDIRECT=false in der Prozessumgebung setzen.
Browserprüfung mit einer automatisch angelegten und anschließend entfernten SQLite-Datenbank (berührt die produktive PostgreSQL-Datenbank nicht):
pip install playwright
python -m playwright install chromium --only-shell
python scripts/test_browser.py
Der Playwright-Lauf prüft Anmeldung, Rollen, Geräteliste, Erfassung, Bearbeitung, Ablauf und Aufgaben, Archiv, Einstellungen, Import/Export, Änderungshistorie sowie Mobilnavigation und Dunkelmodus.
Für einen bewusst beschreibbaren Testserver kann scripts/test_server_browser.py
mit CSR_TEST_BASE_URL, CSR_TEST_USER und CSR_TEST_PASSWORD ausgeführt
werden. Der Lauf legt eindeutig markierte Testgeräte an und gibt die Kennung
TEST_MARKER zur anschließenden Bereinigung aus. Er akzeptiert das lokale
selbstsignierte HTTPS-Zertifikat des Testservers.
Docker-Prüfung auf einem Rechner mit Docker:
docker compose config
docker compose build
docker compose up -d
curl -f https://IHRE-DOMAIN/health/
Importformat
Der XLSX-Import erkennt typische deutsche Spaltennamen automatisch. Unterstützte Core-Spalten sind:
Nr., Regal, Status, Datum, Empfänger, Ort, Ticket, Typ, Preis, Akku, WLAN, LAN, Zoll, RAM, SSD, HDD, CPU, HWID, Modell, Bearbeiter, Spender, Kommentar.
Vor dem Import erscheint eine Vorschau mit Spaltenzuordnung. Bereits vorhandene Gerätenummern führen zum vollständigen Abbruch; es bleibt kein Teilimport zurück. Mitarbeiter werden über ihr Kürzel zugeordnet.
Betrieb und Sicherheit
- Nur Traefik veröffentlicht Ports; PostgreSQL ist nicht von außen erreichbar.
- Excel-Uploads werden nicht öffentlich über Traefik ausgeliefert.
- CSRF-Schutz, sichere Cookies, HSTS, serverseitige Rollenprüfung und Django-Passworthashing sind im Produktionsmodus aktiv.
- Die API akzeptiert keine Browser-Sitzungen, liefert nur JSON, begrenzt Seitengrößen und drosselt wiederholte Anfragen.
- Technische Fehler werden nur in Container-Logs geschrieben:
docker compose logs web. - Für mindestens monatliche Sicherheitsupdates die Basisimages neu bauen:
docker compose build --pull. - Für große Datenbestände nach umfangreichen Importen PostgreSQL analysieren:
docker compose exec db vacuumdb -U "$POSTGRES_USER" -d "$POSTGRES_DB" --analyze.
Architektur
- Django Templates und leichtes Vanilla-JavaScript; kein separater Frontend-Build
- Django ORM und Migrationen
- PostgreSQL 17 mit
pg_trgm - Gunicorn als WSGI-Server
- Traefik als Reverse Proxy mit automatischem HTTPS
- Keine Microservices, kein Redis, kein Elasticsearch
Traefik und LAN-Zertifikate
Traefik verwendet einen File-Provider ohne Docker-Socket. Statische Dateien liefert WhiteNoise aus. Öffentliche Domains nutzen Let's Encrypt; für private IP-Adressen siehe Traefik-Betrieb.
Geräteablauf und Aufgaben
Jedes Gerät hat einen eigenen Arbeitsschritt: Eingang → Inspektion → bei Bedarf Reparatur → Lagerung → Ausgabe. Bearbeiter können auf der Geräteseite Schritte mit Notiz dokumentieren und Aufgaben mit Fälligkeit und zuständiger Person anlegen. Offene Aufgaben stehen unter „Aufgaben“. Vor Lagerung oder Ausgabe müssen sie erledigt sein; für die Ausgabe ist ein Empfänger erforderlich. Eine Rückgabe beginnt wieder beim Eingang. Änderungen erscheinen in der Ablaufhistorie und im Auditprotokoll. Bei Reparatur, Lagerung und Ausgabe wird der bisherige Bestandsstatus automatisch auf Defekt, Fertig beziehungsweise Ausgegeben gesetzt.
Die Gerätetypen Laptop, AiO, Desktop, Bildschirm und Drucker zeigen
nur passende Hardwarefelder. Bei einem Typwechsel werden nicht mehr
passende Werte unter custom_values.legacy_hardware erhalten und aus
den aktiven Feldern entfernt. inventory_device enthält nur gemeinsame
Gerätedaten. Laptop, Desktop, AiO, Monitor und Drucker besitzen eigene
Spezifikationstabellen; die bisherige Computertabelle bleibt für frei
definierte Gerätetypen. Monitore haben unter anderem Diagonale, Auflösung
und Anschlüsse, Laptops RAM, SSD/HDD, CPU, Akku und Anschlüsse. Drucker
haben WLAN/LAN, Anschlüsse, Druckverfahren, Duplex und Farbdruck.
Migration 0012 verschiebt bestehende Computerwerte in die passenden Tabellen.
Export, Filter und API behalten die bisherigen Feldnamen und ergänzen die
neuen Typfelder. Details stehen in Datenbankschema.
Über „Ablauf“ in der Hauptnavigation sind alle Arbeitsschritte direkt erreichbar. Auf dem Board lassen sich Aufgaben anlegen und Geräte zum nächsten Schritt bewegen. Ein Klick auf eine Tabellenzeile öffnet die Geräteseite; Ablauf und Aufgaben stehen dort vor den Stammdaten. In der Inspektion werden Mängel und passende technische Daten erfasst, bei der Reparatur die durchgeführten Arbeiten und in der Lagerung der Empfänger. Befunde und Reparaturen bleiben mit Zeit und Bearbeiter als eigene Datensätze erhalten (Migration 0011).
Vor dem Update einer bestehenden Installation eine Datenbanksicherung
erstellen, dann python manage.py migrate ausführen. Ein Löschen der
Datenbank ist nicht nötig und würde Geräte und Benutzerkonten entfernen.
USB AIO station API
The USB station resolves hardware identity with POST /api/v1/devices/resolve/, then reads the returned inventory number, workflow stage, and status. For INTAKE devices only, POST /api/v1/devices/<number>/intake/ accepts verified CPU/model/RAM/storage measurements plus hwid and expected_revision. Later workflow stages reject intake writes.
POST /api/v1/devices/<number>/inspection/ accepts a UUID request_id, hwid, expected_revision, phase (STARTED, IN_PROGRESS, COMPLETE), technician, notes and a dictionary of test results. Each result preserves outcome, notes, evidence and attempt history. The API moves allowed devices to INSPECTION, sets status to Inspektion, adds a readable inspection record and retains the structured snapshot. A unique request ID prevents duplicate records after lost responses; reused IDs with different content are rejected. Inventory workflow checks, hardware matching and API-key permissions remain enforced. Completed inspections require a named technician and no unfinished results; unavailable tests remain distinct from hardware failures.
Apply migrations through 0015_inspectionsubmission before deploying the new USB application. Wi-Fi credentials and inventory API keys stay on USB persistent storage, not in this repository.
Korrekturen für die Geräteansicht (Migration 0014)
Die Geräteliste bleibt auch bei kleinen oder niedrigen Fenstern scrollbar. Neue
Zusatzspalten werden bei vorhandenen Spalteneinstellungen automatisch eingeblendet;
anschließend können sie weiterhin über die Spaltenauswahl ausgeblendet werden.
Gespeicherte JA/NEIN-Werte und Zahlenwerte wie 0 bleiben beim Bearbeiten sichtbar.
Laptop und AiO bieten jetzt camera (Kamera) und sound (Sound). Bildschirme
bieten hdmi, vga, dvi, displayport (DP) sowie aspect_ratio
(Seitenverhältnis, beispielsweise 16:9). Die Ausstattungsfelder verwenden
YES, NO oder UNKNOWN und sind in der Geräteliste filterbar. Sie stehen auch
in Erfassung, Inspektion, API und Tabellenimport/-export zur Verfügung.
Beim Update python manage.py migrate ausführen; Docker erledigt dies beim
Containerstart. Bestehende Anschlussnotizen bleiben erhalten. Neue Auswahlfelder
starten mit „UNBEKANNT“, da Freitext keine verlässliche JA/NEIN-Zuordnung erlaubt.
Die Gitea-Pipeline prüft zusätzlich die Browserabläufe einschließlich kleiner
Bildschirme und benutzerdefinierter Spalten.