2026-10-02 07:36:03 +00:00

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, EDITOR und VIEWER
  • 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. mit openssl rand -base64 48
  • POSTGRES_PASSWORD: starkes, einmaliges Datenbankpasswort
  • APP_HOST: Domain ohne Protokoll/Port, z. B. geraete.example.org
  • ACME_EMAIL: Kontaktadresse für öffentliche TLS-Zertifikate
  • ALLOWED_HOSTS: dieselbe Domain sowie 127.0.0.1,localhost für den internen Healthcheck
  • CSRF_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.

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.

Description
No description provided
Readme 549 KiB
Languages
Python 79.6%
HTML 12.4%
JavaScript 7.6%
Shell 0.3%
Dockerfile 0.1%