Polymorphe Datei-Anhänge (PDF/JPEG/PNG/WebP, Whitelist statt Blacklist -
owasp-Grundsatz) an beliebige Ressource, gleiches entitaet_typ/entitaet_id-
Muster wie Mangel/Historie. Lokale Ablage (settings.upload_dir, kein Cloud-
Zwang, Self-Hosting-Anforderung), server-generierter Dateiname verhindert
Path-Traversal/Namenskollisionen.
POST /dokumente (multipart), GET /dokumente (Filter Pflicht: entitaet_typ +
entitaet_id), GET /dokumente/{id}/download, DELETE /dokumente/{id}. Hochladen:
alle Mitarbeiter+, Löschen: Materialverantwortliche+Leitung+Admin.
Frontend: wiederverwendbares DokumentePanel (Upload/Liste/Download/Löschen),
eingebunden in ObjektSection (je Objekt) und MangelListePage (Fotos zu
Mängeln) - weitere Ressourcen (Geräteinstanz, Fahrzeugdetails, Benutzer)
können denselben Baustein später einfach wiederverwenden.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
30 lines
1.4 KiB
Python
30 lines
1.4 KiB
Python
import uuid
|
|
from datetime import datetime
|
|
|
|
from sqlalchemy import ForeignKey, Integer, String
|
|
from sqlalchemy.dialects.postgresql import TIMESTAMP, UUID
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from app.db.base import Base
|
|
|
|
|
|
class Dokument(Base):
|
|
"""Roadmap Phase 5 (Modul Dokumente): polymorpher Datei-Anhang an beliebige
|
|
Ressource (Objekt, Objektposition, Geräteinstanz, Mangel, Fahrzeugdetails,
|
|
Benutzer, ...) - gleiches entitaet_typ/entitaet_id-Muster wie Historie/
|
|
Mangel. Datei liegt lokal auf Platte (self-hosted, kein Cloud-Zwang),
|
|
speicherpfad ist relativ zu settings.upload_dir."""
|
|
|
|
__tablename__ = "dokument"
|
|
|
|
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
entitaet_typ: Mapped[str] = mapped_column(String, nullable=False)
|
|
entitaet_id: Mapped[str] = mapped_column(String, nullable=False)
|
|
dateiname: Mapped[str] = mapped_column(String, nullable=False)
|
|
speicherpfad: Mapped[str] = mapped_column(String, nullable=False)
|
|
mime_type: Mapped[str] = mapped_column(String, nullable=False)
|
|
groesse_bytes: Mapped[int] = mapped_column(Integer, nullable=False)
|
|
beschreibung: Mapped[str | None] = mapped_column(String)
|
|
hochgeladen_von: Mapped[int] = mapped_column(ForeignKey("benutzer.id"), nullable=False)
|
|
hochgeladen_am: Mapped[datetime] = mapped_column(TIMESTAMP(timezone=True), nullable=False)
|