Initialize clean CSR inventory repository
This commit is contained in:
297
README.md
Normal file
297
README.md
Normal file
@@ -0,0 +1,297 @@
|
||||
# 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.
|
||||
|
||||
## 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.
|
||||
|
||||
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.
|
||||
Reference in New Issue
Block a user