Files
dogfather-universe/workspace/assets/css/dateien.css
T
DogFatherGit 81b51ab94f Workspace: Dateiablage mit Freigabe-Ablauf (Phase 1 vollstaendig)
/workspace/dateien.html -- Ablegen per Ziehen oder Auswaehlen,
Entwurf -> Review -> Freigabe, Filter je Zustand.

OHNE NEUE ABHAENGIGKEIT. Uploads laufen ueblicherweise ueber multer.
Hier schickt der Browser die Datei roh im Rumpf, der Name steht im Kopf
(URI-kodiert, damit Umlaute heil ankommen). express.raw() bringt den
passenden Empfaenger schon mit -- kein Zerlegen von multipart/form-data
von Hand, was gerade hier heikel waere.

Die drei Punkte, an denen Dateiablagen typischerweise scheitern:

1. Die Dateien liegen AUSSERHALB des Repos (workspace-daten/dateien/).
   Lagen sie darin, wuerde express.static sie ungeprueft ans Netz geben.
2. Auf der Platte traegt jede Datei einen erzeugten Zufallsnamen, der
   Originalname steht nur in der Datenbank. Geprueft mit dem Dateinamen
   "../../etc/passwd": gespeichert wurde "passwd", auf der Platte ein
   Zufallsname IM Ordner -- nichts ist ausgebrochen.
3. Ausgeliefert wird immer als Download mit neutralem Typ, dazu
   nosniff und CSP sandbox. Geprueft mit einer hochgeladenen
   boese.html: kommt als application/octet-stream zurueck, kann also
   keinen Code im Namen der Domain ausfuehren.

Freigabe nach Konzept: Der Zustand "freigegeben" ist eine Abnahme und
bleibt dem Management vorbehalten. Geprueft -- Luna kann Entwurf ->
Review, aber nicht freigeben (403); eine freigegebene Datei kann sie
weder aendern noch loeschen.

Sichtbarkeit wie ueberall: Chef 2 Dateien, Luna 1, Mika 0. Zugriff auf
eine fremde Datei: 404, nicht 403.

Ein Fehler beim Testen gefunden: Bei zu grosser Datei brach express.raw
ab, BEVOR der eigene Code lief -- der Fehler landete im allgemeinen
Behandler als HTTP 500. Fuer die Nutzerin sah das aus wie ein kaputter
Server statt wie "Datei zu gross". Jetzt faengt ein eigener
Fehlerbehandler am Ende des Routers das ab und antwortet mit 413 und
einer verstaendlichen Meldung.

Damit ist Phase 1 aus dem Konzept vollstaendig.
2026-08-28 01:14:00 +02:00

105 lines
3.8 KiB
CSS

/* ===================================================================
Dateiablage mit Freigabe-Ablauf.
=================================================================== */
.ablage {
display: grid; place-items: center; gap: 6px;
padding: 34px 20px; margin-bottom: 16px;
border: 1px dashed var(--rand);
border-radius: var(--radius);
background: rgba(255, 255, 255, .02);
cursor: pointer; text-align: center;
transition: border-color var(--tempo), background var(--tempo);
}
.ablage:hover, .ablage:focus-visible {
border-color: rgba(63, 189, 245, .5);
background: rgba(63, 189, 245, .05);
outline: none;
}
/* Während eine Datei darüber schwebt, deutlich sichtbar -- sonst weiß
niemand, ob das Loslassen an dieser Stelle etwas bewirkt. */
.ablage[data-bereit="ja"] {
border-color: var(--akzent);
border-style: solid;
background: rgba(63, 189, 245, .10);
}
.ablage__zeichen {
width: 30px; height: 30px;
fill: none; stroke: var(--text-still);
stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}
.ablage:hover .ablage__zeichen, .ablage[data-bereit="ja"] .ablage__zeichen { stroke: var(--akzent); }
.ablage__text { margin: 0; font-size: .92rem; color: var(--text-leise); }
.ablage__text strong { color: var(--akzent); }
.ablage__klein { margin: 0; font-size: .78rem; color: var(--text-still); }
.hochlade-zeile { margin-bottom: 18px; }
.hochlade-zeile select {
min-width: 240px; padding: 9px 12px;
background: rgba(255, 255, 255, .045);
border: 1px solid var(--rand); border-radius: var(--radius-klein);
color: var(--text); font: inherit;
}
.fortschritt {
margin: 0 0 14px; padding: 10px 14px;
background: rgba(63, 189, 245, .08);
border: 1px solid rgba(63, 189, 245, .3);
border-radius: var(--radius-klein);
font-size: .86rem; color: var(--text-leise);
}
.filter { display: flex; gap: 8px; margin: 0 0 20px; flex-wrap: wrap; }
.filter .schritt[aria-pressed="true"] {
background: rgba(63, 189, 245, .14);
border-color: rgba(63, 189, 245, .55);
color: var(--text);
}
/* ---------- Liste ------------------------------------------------------- */
.datei {
display: grid; gap: 4px 16px; align-items: center;
grid-template-columns: 34px 1fr auto;
padding: 13px 16px; margin-bottom: 9px;
background: rgba(255, 255, 255, .03);
border: 1px solid var(--rand);
border-left: 3px solid var(--rand);
border-radius: var(--radius-klein);
}
.datei[data-status="entwurf"] { border-left-color: #6f89a8; }
.datei[data-status="review"] { border-left-color: #e0a33c; }
.datei[data-status="freigegeben"] { border-left-color: #4dab77;
background: rgba(77, 171, 119, .05); }
.datei__zeichen {
grid-row: span 2; width: 26px; height: 26px;
fill: none; stroke: var(--text-still);
stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}
.datei__name { font-weight: 600; font-size: .93rem; word-break: break-word; }
.datei__zeile {
display: flex; flex-wrap: wrap; gap: 8px;
font-size: .78rem; color: var(--text-still);
}
.datei__knoepfe { grid-row: span 2; display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.marke-status {
padding: 1px 9px; border-radius: 999px;
font-size: .68rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
}
.marke-status[data-status="entwurf"] { background: rgba(111, 137, 168, .18); color: #a8bdd4; }
.marke-status[data-status="review"] { background: rgba(224, 163, 60, .16); color: #e6b45c; }
.marke-status[data-status="freigegeben"] { background: rgba(77, 171, 119, .18); color: #79d1a2; }
.leer-hinweis {
padding: 26px 20px; text-align: center;
color: var(--text-still); font-size: .9rem;
border: 1px dashed var(--rand); border-radius: var(--radius);
}
@media (max-width: 720px) {
.datei { grid-template-columns: 34px 1fr; }
.datei__knoepfe { grid-row: auto; grid-column: 1 / -1; margin-top: 9px; }
}