File Storage — Setup & Deployment
Voraussetzungen
Das File-Storage-System ist vollständig implementiert und produktiv. Code, Dependency und Migration sind bereits Teil des Repositories:
| Bereich | Inhalt | Status |
|---|---|---|
| Schema & Migration | Prisma Schema, Migration 20251128215910_add_file_storage (eingespielt) | Abgeschlossen |
| Dependency | @vercel/blob ^2.3.1 in package.json | Installiert |
| Storage-Layer | TypeScript Interfaces, Vercel Blob Provider, Storage Factory, File Validation | Abgeschlossen |
| Services | Upload Service, Download Service, Permission Service | Abgeschlossen |
| API-Routes | Upload, Download, Public, List, Admin | Abgeschlossen |
| DSGVO Cron-Jobs | vercel.json-Konfiguration aktiv | Abgeschlossen |
| Frontend | FileUploadDialog, FileList | Abgeschlossen |
| Dokumentation | Architektur + Setup | Abgeschlossen |
Da Code und Migration bereits vorhanden sind, beschränkt sich das Setup einer neuen Umgebung auf das Setzen der Secrets (Blob-Token und Cron-Secret).
Schritt-für-Schritt Setup (neue Umgebung)
Dependency-Installation und Prisma-Migration sind bereits Teil des Repositories — beim regulären
pnpm install/pnpm initdbwerden sie automatisch übernommen. Für ein frisches Setup genügt:pnpm install # installiert u. a. @vercel/blob
pnpm initdb # spielt Migrationen (inkl. add_file_storage) ein und seedet
1. Vercel Blob Store erstellen
1.1 Blob Store anlegen
- Vercel Dashboard öffnen
- Projekt auswählen
- Storage (linke Sidebar) anklicken
- Create Database anklicken
- Blob → Continue auswählen
- Region: Frankfurt (eu-central-1)
- Create anklicken
1.2 Token generieren
- Den neuen Blob Store anklicken
- Settings Tab öffnen
- Zu Tokens scrollen
- Generate Token anklicken
- Name:
File Storage Read/Write - Permissions: Read and Write aktivieren
- Create anklicken
- Wichtig: Token sofort kopieren — er wird nur einmal angezeigt
1.3 Token in Vercel setzen
- Im Projekt: Settings → Environment Variables
- Add New anklicken
- Name:
BLOB_READ_WRITE_TOKEN - Value:
vercel_blob_rw_...(generierter Token) - Environments: Production, Preview, Development aktivieren
- Save anklicken
2. Cron Secret generieren
Windows PowerShell:
[Convert]::ToBase64String((1..32 | ForEach-Object { Get-Random -Minimum 0 -Maximum 256 }))
Linux/Mac:
openssl rand -base64 32
Den generierten String in Vercel als Environment-Variable setzen:
- Settings → Environment Variables
- Add New anklicken
- Name:
CRON_SECRET - Value: generierter String
- Environments: Production, Preview aktivieren
- Save anklicken
3. Environment Variables lokal setzen
Die lokale Umgebung lädt .env (im Verzeichnis webapp/). Datei erstellen oder erweitern:
# File Storage
BLOB_READ_WRITE_TOKEN=vercel_blob_rw_...
BLOB_PRIVATE_READ_WRITE_TOKEN=vercel_blob_rw_...
FILE_ENCRYPTION_KEY=<openssl rand -base64 32>
CRON_SECRET=<generiertes-secret>
# Optional: Größenlimits (nur bei Vercel Pro sinnvoll — siehe Abschnitt Hobby-Plan)
# FILE_MAX_SIZE_IMAGE=10485760 # 10MB
# FILE_MAX_SIZE_PDF=26214400 # 25MB
Sicherstellen, dass .env über das Repository-.gitignore (webapp/.env*) ausgeschlossen ist und der Token nicht committed wird.
4. Deployment verifizieren
Im Vercel Dashboard unter Deployments:
- Status muss Ready sein
- Build-Logs dürfen keine Fehler zeigen
Cron-Jobs prüfen (im Projekt-Dashboard unter Cron Jobs):
| Pfad | Schedule | Status |
|---|---|---|
/api/cron/check-expired-files | 0 2 * * * (täglich 2:00 Uhr) | Active |
/api/cron/delete-non-critical-files | 0 3 * * * (täglich 3:00 Uhr) | Active |
Falls Cron-Jobs nicht angezeigt werden: sicherstellen, dass vercel.json committed ist, dann Re-Deploy mit vercel --prod.
Environment Variables
Required
| Variable | Beschreibung |
|---|---|
BLOB_READ_WRITE_TOKEN | Token des öffentlichen Stores (Format: vercel_blob_rw_...) |
BLOB_PRIVATE_READ_WRITE_TOKEN | Token des privaten Stores |
FILE_ENCRYPTION_KEY | Hauptschlüssel, 32 Bytes als Base64. In der Preview: DEVELOP_FILE_ENCRYPTION_KEY. Siehe Dateischutz |
CRON_SECRET | Geheimnis zur Authentifizierung der Cron-Job-Endpunkte |
Optional (mit Standardwerten)
| Variable | Standard | Beschreibung |
|---|---|---|
FILE_MAX_SIZE_IMAGE | 4000000 | Maximale Bildgröße in Bytes (4 MB, Hobby-Plan) |
FILE_MAX_SIZE_PDF | 4000000 | Maximale PDF-Größe in Bytes (4 MB, Hobby-Plan) |
RETENTION_STUDENT_DOCS | 3650 | Aufbewahrungsdauer Schülerdokumente in Tagen (10 Jahre) |
RETENTION_PROFILE_IMAGE | 730 | Aufbewahrungsdauer Profilbilder in Tagen (2 Jahre) |
Vercel Hobby-Plan Einschränkungen
Der Vercel Hobby-Plan hat ein Payload-Limit von 4,5 MB pro Request. Für Datei-Uploads bedeutet das:
- Maximale Dateigröße: 4 MB
FILE_MAX_SIZE_IMAGEundFILE_MAX_SIZE_PDFmüssen auf4000000gesetzt bleiben
Was funktioniert
| Dateityp | Typische Größe | Status |
|---|---|---|
| Zeugnisse | ~500 KB | Funktioniert |
| Bescheinigungen | ~200 KB | Funktioniert |
| Profilbilder | ~1 MB | Funktioniert |
| Standard-PDFs | ~2 MB | Funktioniert |
Was nicht funktioniert
- Große PDFs mit vielen Seiten (> 4 MB)
- Hochauflösende Scans (> 4 MB)
- Große Bilddateien (> 4 MB)
Das Frontend zeigt eine Warnung an, wenn eine Datei das Limit überschreitet.
Upgrade-Optionen
Option A: Vercel Pro (empfohlen bei ausreichendem Budget)
- Kosten: $20/Monat
- Payload-Limit: 50 MB
- Einfachste Lösung
Option B: Direct-Upload (kostenlos, aber aufwendiger)
- Browser uploaded direkt zu Vercel Blob, umgeht das Next.js Payload-Limit
- Erfordert zusätzliche Implementierung
- Für den aktuellen Einsatzbereich (Schuldokumente < 4 MB) nicht erforderlich
Troubleshooting
"BLOB_READ_WRITE_TOKEN is not set"
- Vercel Dashboard → Settings → Environment Variables prüfen — ist der Token gesetzt?
- Re-Deploy ausführen
- Für lokale Entwicklung:
.envprüfen
Upload schlägt fehl
Häufige Ursachen:
- Datei ist größer als 4 MB (Hobby-Plan Limit)
- Falscher Dateityp (z. B.
.exe) - Token fehlt oder ist falsch
Diagnose:
- Browser-Konsole öffnen (F12) und Fehlerdetails lesen
- Vercel Logs prüfen
- Dateigröße reduzieren
- Token überprüfen
Cron-Jobs nicht sichtbar
- Ist
vercel.jsoncommitted? (git log --oneline -1prüfen) - Re-Deploy ausführen:
vercel --prod - 1–2 Minuten warten, Dashboard neu laden
TypeScript-Fehler nach Migration
npx prisma generate
pnpm typecheck
Wenn Fehler weiterhin bestehen:
rm -rf node_modules .next
pnpm install
pnpm build
Migration schlägt fehl
# Schema prüfen
npx prisma format
# Migration Reset — ACHTUNG: löscht alle Daten!
npx prisma migrate reset
# Migration neu ausführen
npx prisma migrate dev
Cron-Job manuell testen
curl -X GET "https://<domain>.vercel.app/api/cron/check-expired-files" \
-H "Authorization: Bearer <cron-secret>"
Erwartete Antwort:
{
"success": true,
"markedCount": 0,
"timestamp": "..."
}
Nach dem Setup
Verifikations-Checkliste
Bereits im Repository enthalten (kein manueller Schritt nötig):
-
@vercel/blob(^2.3.1) installiert - Migration
20251128215910_add_file_storagevorhanden - Cron-Jobs in
vercel.jsonregistriert
Pro Umgebung zu prüfen:
- Vercel Blob Store erstellt
-
BLOB_READ_WRITE_TOKENin Vercel und in.envgesetzt - Privater Blob Store erstellt,
BLOB_PRIVATE_READ_WRITE_TOKENin Vercel und in.envgesetzt -
FILE_ENCRYPTION_KEYje Umgebung gesetzt, Kopie außerhalb von Vercel gesichert -
CRON_SECRETin Vercel und in.envgesetzt - Deployment-Status: Ready
- Cron-Jobs im Dashboard aktiv
- Test-Upload erfolgreich
- Test-Download erfolgreich
- Datei im Blob Store sichtbar (Vercel Dashboard → Storage → Blob Store)
Nächste Schritte
Frontend-Integration — FileUpload in bestehende Seiten einbinden:
import { FileUploadDialog } from '@/components/file/FileUploadDialog';
import { FileList } from '@/components/file/FileList';
<FileList studentId={params.id} category="STUDENT_DOCUMENT" />
<FileUploadDialog
open={uploadOpen}
onOpenChange={setUploadOpen}
onUploadComplete={() => refetch()}
defaultCategory="STUDENT_DOCUMENT"
studentId={params.id}
/>
Admin-Page — Datei anlegen unter app/admin/files/page.tsx für zentrales File-Management.
Mittelfristig:
- Unit-Tests und E2E-Tests für Upload/Download/Permissions schreiben
- Monitoring über Vercel Analytics aktivieren
- Storage-Verbrauch überwachen (Vercel Dashboard → Storage)
- Backup-Strategie evaluieren (Vercel Blob hat kein Auto-Backup)