334 lines
14 KiB
Markdown
334 lines
14 KiB
Markdown
# 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
|
|
|
|
## 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](docs/postgresql-migration.md).
|
|
|
|
```bash
|
|
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](docs/postgresql-migration.md) enthält auch einen lokalen
|
|
Docker-Start ohne HTTPS-Domain.
|
|
|
|
Testdaten können gezielt erzeugt werden:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
docker --version
|
|
docker compose version
|
|
```
|
|
|
|
### 2. Projekt und Umgebungsvariablen
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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):
|
|
|
|
```bash
|
|
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](docs/usb-appliance.md)
|
|
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](docs/usb-intake.md).
|
|
|
|
## Backup und Wiederherstellung
|
|
|
|
Backup-Verzeichnis anlegen und Skripte ausführbar machen:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
./scripts/restore.sh backups/backup_2026-09-19_0200.sql.gz
|
|
```
|
|
|
|
Beispiel für einen täglichen Cronjob um 02:00 Uhr:
|
|
|
|
```cron
|
|
0 2 * * * cd /opt/csr-inventory && ./scripts/backup.sh >> /var/log/csr-backup.log 2>&1
|
|
```
|
|
|
|
## Tests und Prüfungen
|
|
|
|
```bash
|
|
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):
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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](docs/traefik.md).
|
|
|
|
## 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](docs/datenbankschema.md).
|
|
|
|
Ü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.
|