Sharely — Technische Dokumentation

Version: 1.0.0 · Lizenz: MIT · Node.js: ≥ 18

Inhaltsverzeichnis

1. Projektübersicht

2. Architektur

3. Tech-Stack

4. Verzeichnisstruktur

5. Konfiguration & Umgebungsvariablen

6. Datenmodelle

7. REST API

8. WebSocket API

9. Datei-Serving-Routen

10. Authentifizierung & Sicherheit

11. Upload-System

12. Thumbnail-Generierung

13. E-Mail & SMTP

14. Internationalisierung

15. Frontend (React SPA)

16. Admin-Dashboard

17. GDPR / Datenschutz

18. Hintergrund-Jobs

19. Deployment

20. Entwicklungsumgebung

21. Migrationen & Skripte

22. End-to-End-Tests


1. Projektübersicht

Sharely ist eine selbst gehostete Dateifreigabe-Plattform mit einer sauberen Web-Oberfläche, ShareX-Integration und API-Zugang. Nutzer laden Screenshots, Dateien und Medien hoch und teilen sie sofort über Kurz-Links.

Kernfunktionen

FeatureBeschreibung
Web-UploadDrag-and-Drop, bis zu 500 Dateien gleichzeitig
Chunked UploadDateien bis 2 GB via parallelem Multi-Part-Upload
ShareX-Integration.sxcu-Konfigurationsdatei per Knopfdruck herunterladbar
API-UploadBearer-Token-Authentifizierung, kompatibel mit curl/wget
Datei-ViewerBilder zoomen, Videos/Audio streamen (HTTP Range), PDFs inline, Code syntax-highlighted
Einbettungsmodi*embed* (OG/Twitter-Card-HTML) oder *raw* (Direkt-Redirect)
ThumbnailsAutomatische JPEG-Vorschaubilder für Videos (ffmpeg) und PDFs (ghostscript)
CollectionsGruppensammlungen von Dateien mit optionalem Passwort und Ablaufdatum
Share-LinksPer-Datei-Links mit Passwort, Ablauf und Download-Limit
Echtzeit-UIWebSocket-basierte Live-Updates (Upload, Delete, View-Counter, Admin-Stats)
Mehrsprachigkeit8 Sprachen: EN, DE, FR, ES, IT, PT, JA, ZH
Admin-DashboardStatistiken, Nutzerverwaltung, Dateiverwaltung, Audit-Log (CSV-Export)
GDPR-ComplianceDatenschutz-Features nach EU-DSGVO (Art. 17, 20, 32 u. a.)
XBackBone-ImportMigration bestehender XBackBone-Installationen
Docker-readydocker compose up -d startet die vollständige Umgebung

2. Architektur

┌─────────────────────────────────────────────────────────────┐
│                        Browser / Client                      │
│            React 18 SPA  ·  Vite  ·  Tailwind CSS           │
│   ┌──────────────────────────────────────────────────────┐   │
│   │  HTTP REST (/api/*)            WebSocket (/ws)       │   │
│   └──────────────────────────────────────────────────────┘   │
└───────────────────────────┬────────────────────┬─────────────┘
                            │ HTTP               │ WS
┌───────────────────────────▼────────────────────▼─────────────┐
│                    Express.js (app.js)                        │
│                                                               │
│  ┌──────────────┐  ┌───────────┐  ┌──────────┐  ┌────────┐  │
│  │  /api/auth   │  │  /api/*   │  │   /f/*   │  │  /s/*  │  │
│  │  /api/install│  │  routes   │  │  files   │  │ shares │  │
│  └──────────────┘  └───────────┘  └──────────┘  └────────┘  │
│                                                               │
│  Middleware: session · rate-limit · CSRF · CSP · auth        │
│                                                               │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌────────────┐  │
│  │  multer  │  │  ws.js   │  │  mailer  │  │ retention  │  │
│  │  upload  │  │ WebSocket│  │ nodemailer│  │  cleanup   │  │
│  └──────────┘  └──────────┘  └──────────┘  └────────────┘  │
└───────────────────────────┬───────────────────────────────────┘
                            │
┌───────────────────────────▼───────────────────────────────────┐
│                      MongoDB (Mongoose)                        │
│  User · File · Collection · ShareLink · SiteSettings          │
│  AuditLog                                                      │
└───────────────────────────────────────────────────────────────┘
                            │
              ┌─────────────▼───────────────┐
              │     Dateisystem (uploads/)   │
              │  {folderName}/  ·  .chunks/  │
              │  .thumbnails/   ·  .avatars/ │
              └─────────────────────────────┘

Datenfluss: Standard-Upload

Browser → POST /api/web-upload (multipart/form-data)
       → multer: Datei in uploads/{folderName}/
       → File.createUnique() → MongoDB
       → generateThumbnail() (async, non-blocking)
       → logAudit()
       → broadcast('file:uploaded') via WebSocket
       ← JSON: { files: [...] }

Datenfluss: ShareX-Upload

ShareX → POST /upload (token im Formular-Body)
       → multer: Datei temporär in uploads/
       → requireApiKey: Token-Lookup → User
       → fs.renameSync → uploads/{folderName}/
       → File.create() → MongoDB
       ← JSON: { url, delete_url }

3. Tech-Stack

SchichtTechnologieVersion
LaufzeitNode.js≥ 18
Backend-FrameworkExpress.js4.x
DatenbankMongoDB + MongooseMongo 7, Mongoose 8.x
Sessionsexpress-session + connect-mongo
EchtzeitWebSocket (ws)8.x
Datei-UploadMulter1.x
E-MailNodemailer8.x
Passwort-Hashingbcryptjs2.x (12 Rounds)
API-Key-HashingSHA-256 (Node Crypto)
Rate-Limitingexpress-rate-limit8.x
XBackBone-Importsql.js1.x
Frontend-FrameworkReact 1818.3.x
Routing (Frontend)React Router v66.x
Build-ToolVite6.x
StylingTailwind CSS + Radix UI3.x
UI-Komponentenshadcn/ui (Radix primitives)
IconsFontAwesome6.x
i18ni18next + react-i18next
Syntax-Highlightinghighlight.js11.x
ContainerDocker + Docker Compose
TestsPlaywright (E2E)1.60.x

4. Verzeichnisstruktur

sharely/
├── app.js                          # Express-Einstiegspunkt, Startup-Sequenz
├── package.json
├── .env.example                    # Vorlage für alle Umgebungsvariablen
├── Dockerfile
├── docker-compose.yml
│
├── src/
│   ├── config/
│   │   └── db.js                   # MongoDB-Verbindung (Mongoose)
│   ├── middleware/
│   │   ├── auth.js                 # requireLogin / requireAdmin / requireApiKey
│   │   └── upload.js               # Multer-Konfiguration, Blockliste
│   ├── models/
│   │   ├── AuditLog.js             # Audit-Events (TTL 90 Tage)
│   │   ├── Collection.js           # Dateisammlungen
│   │   ├── File.js                 # Datei-Metadaten
│   │   ├── ShareLink.js            # Per-Datei-Share-Links
│   │   ├── SiteSettings.js         # Singleton: Betreiber-Einstellungen
│   │   └── User.js                 # Nutzerkonten + API-Keys
│   ├── routes/
│   │   ├── api.js                  # Haupt-API (Upload, Gallery, Admin, ...)
│   │   ├── auth.js                 # Login / Register / Password-Reset
│   │   ├── files.js                # Datei-Serving, OG-Embeds, Range-Requests
│   │   ├── import.js               # XBackBone-Migration
│   │   ├── install.js              # Erstinstallations-Endpoint
│   │   └── shares.js               # Share-Link-Datei-Serving
│   ├── jobs/
│   │   └── retentionCleanup.js     # Tägliches Löschen abgelaufener Dateien
│   ├── migrations/
│   │   ├── migrateApiKeyHashes.js  # Einmalig: Plaintexts → SHA-256-Hashes
│   │   └── migrateUserFolders.js   # Einmalig: Dateien in Nutzer-Ordner verschieben
│   ├── utils/
│   │   ├── audit.js                # logAudit()-Hilfsfunktion
│   │   ├── generateThumbnail.js    # ffmpeg / ghostscript Integration
│   │   ├── mailer.js               # Nodemailer-Wrapper + i18n-E-Mail-Templates
│   │   └── sanitizeFilename.js     # Dateiname bereinigen
│   └── ws.js                       # WebSocket-Server + Action-Dispatcher
│
├── client/                         # React-Frontend
│   ├── index.html
│   ├── package.json
│   ├── vite.config.js
│   ├── tailwind.config.js
│   └── src/
│       ├── main.jsx                # React-Einstiegspunkt
│       ├── App.jsx                 # Router-Konfiguration
│       ├── index.css               # Globale Styles
│       ├── context/
│       │   └── AuthContext.jsx     # Globaler Auth-State
│       ├── hooks/
│       │   ├── use-toast.js        # Toast-Notification-Hook
│       │   └── useWebSocket.js     # WS-Verbindung + Event-Handler
│       ├── components/
│       │   ├── Layout.jsx          # App-Shell (Navbar, Sidebar)
│       │   ├── ProtectedRoute.jsx  # Auth-Guard
│       │   ├── ShareLinkDialog.jsx # Share-Link-Erstell-Dialog
│       │   ├── AddToCollectionDialog.jsx
│       │   ├── CookieBanner.jsx
│       │   ├── LanguageSelector.jsx
│       │   ├── RequireEmailDialog.jsx
│       │   ├── UserAvatar.jsx
│       │   └── ui/                 # shadcn/ui Basis-Komponenten
│       ├── pages/
│       │   ├── Upload.jsx          # Upload-Seite
│       │   ├── Gallery.jsx         # Datei-Galerie
│       │   ├── FileView.jsx        # Datei-Detailansicht
│       │   ├── Collections.jsx     # Sammlungsübersicht
│       │   ├── CollectionView.jsx  # Einzelne Sammlung
│       │   ├── ShareView.jsx       # Öffentliche Share-Link-Seite
│       │   ├── Settings.jsx        # Nutzer-Einstellungen
│       │   ├── Login.jsx
│       │   ├── Register.jsx
│       │   ├── ForgotPassword.jsx
│       │   ├── ResetPassword.jsx
│       │   ├── Install.jsx         # Erstinstallation
│       │   ├── PrivacyPolicy.jsx
│       │   ├── TermsOfService.jsx
│       │   └── admin/
│       │       ├── Dashboard.jsx   # Admin-Startseite
│       │       ├── Users.jsx       # Nutzerverwaltung
│       │       ├── Files.jsx       # Dateiverwaltung
│       │       ├── AuditLog.jsx    # Audit-Log-Ansicht
│       │       ├── SiteSettings.jsx# Betreiber-Einstellungen
│       │       └── Import.jsx      # XBackBone-Import
│       ├── i18n/
│       │   ├── index.js            # i18next-Konfiguration
│       │   └── locales/
│       │       ├── de.json
│       │       ├── en.json
│       │       ├── es.json
│       │       ├── fr.json
│       │       ├── it.json
│       │       ├── ja.json
│       │       ├── pt.json
│       │       └── zh.json
│       └── lib/
│           └── utils.js            # Tailwind-Helfer (cn())
│
├── scripts/
│   ├── setup-db.js
│   ├── mongo-init.js               # MongoDB-Initialisierungsskript
│   ├── migrate-uploads-to-user-folders.js
│   └── generate-missing-thumbnails.js
│
├── e2e/                            # Playwright-Tests
│   ├── admin.spec.js
│   ├── bulk-actions-fixes.spec.js
│   ├── gallery.spec.js
│   ├── sharelink.spec.js
│   ├── tags.spec.js
│   ├── upload.spec.js
│   ├── helpers.js
│   └── global-setup.js
│
├── uploads/                        # Datei-Uploads (Laufzeit)
│   ├── .thumbnails/
│   ├── .avatars/
│   └── .chunks/                    # Temporäre Chunks
│
└── docs/assets/                    # Screenshots und Logo

5. Konfiguration & Umgebungsvariablen

Alle Variablen werden aus .env geladen (via dotenv). Die Datei .env.example enthält die vollständige Vorlage.

Pflichtfelder

VariableBeschreibung
SESSION_SECRETGeheimnis für Session-Verschlüsselung — langer Zufallsstring, z. B. openssl rand -hex 32
MONGO_ROOT_PASSWORDMongoDB-Root-Passwort (nur für Docker Compose benötigt)
MONGO_APP_PASSWORDMongoDB-App-Nutzer-Passwort

Alle Umgebungsvariablen

VariableStandardBeschreibung
PORT3000TCP-Port des HTTP-Servers
MONGODB_URI_(aus Docker Compose konstruiert)_Vollständige MongoDB-Verbindungs-URI
MONGO_ROOT_PASSWORDMongoDB-Root-Passwort
MONGO_APP_USERappuserMongoDB-App-Nutzername
MONGO_APP_PASSWORDMongoDB-App-Nutzer-Passwort
MONGO_DB_NAMEsharelyMongoDB-Datenbankname
SESSION_SECRETPflicht — Session-Verschlüsselungsgeheimnis
BASE_URLhttp://localhost:3000Öffentliche Basis-URL für generierte Share-Links (kein abschließendes /)
SITE_NAMEsharelySitename in Open Graph Embeds
MAX_FILE_SIZE_MB100Maximale Dateigröße für Standard-Uploads in MB (Chunked Uploads bis 2 GB unabhängig davon)
ALLOW_REGISTRATIONtruefalse deaktiviert öffentliche Registrierung
SMTP_HOSTSMTP-Server-Hostname; leer lassen = E-Mail-Features deaktiviert
SMTP_PORT587SMTP-Port
SMTP_SECUREfalsetrue für implizites TLS (Port 465), false für STARTTLS
SMTP_USERSMTP-Benutzername
SMTP_PASSSMTP-Passwort
SMTP_FROM_(SMTP_USER)_Absenderadresse in ausgehenden E-Mails
UPLOAD_DIR./uploadsAbsoluter Pfad zum Upload-Verzeichnis
NODE_ENVproduction aktiviert sichere Cookies

6. Datenmodelle

User (src/models/User.js)

{
  username:                    String (3–32, unique, alphanumerisch + _-)
  password:                    String (bcrypt, 12 Rounds)
  role:                        'admin' | 'user'
  apiKey:                      String (Legacy, nach Migration leer)
  apiKeyHash:                  String (SHA-256, unique, sparse)
  apiKeyPrefix:                String (erste 8 Zeichen des Klartexts)
  folderName:                  String (unique, sparse, max 64)
  avatarExt:                   String | null (.jpg/.png/.gif/.webp)
  embedMode:                   'embed' | 'raw'
  isActive:                    Boolean
  email:                       String (lowercase, unique, sparse)
  emailVerified:               Boolean
  emailVerificationToken:      String | null (SHA-256-Hash des Klartexts)
  emailVerificationExpires:    Date | null
  passwordResetToken:          String | null (SHA-256-Hash)
  passwordResetExpires:        Date | null
  language:                    'en'|'de'|'fr'|'es'|'it'|'pt'|'ja'|'zh'
  predefinedTags:              [String] (max 100 Tags × 50 Zeichen)
  createdAt:                   Date
}

Wichtige Methoden:

File (src/models/File.js)

{
  shortId:      String (8 Hex-Zeichen: 6 Timestamp + 2 Random, unique)
  deleteToken:  String (32 Hex-Zeichen, unique)
  originalName: String (sanitisiert)
  storedName:   String (relativer Pfad: "folderName/8hex.ext")
  mimeType:     String
  size:         Number (Bytes)
  uploader:     ObjectId → User
  views:        Number
  tags:         [String] (max 20 × 50 Zeichen)
  createdAt:    Date
}

Virtuals:

Short-ID-Algorithmus:

shortId = hex(seconds_since_2024-01-01, 6 chars) + randomBytes(2).hex()

Collection (src/models/Collection.js)

{
  shortId:     String (8 Hex, unique)
  name:        String (max 100)
  description: String (max 500)
  owner:       ObjectId → User
  files:       [ObjectId → File]
  password:    String | null (bcrypt)
  expiresAt:   Date | null
  createdAt:   Date
}
Abgelaufene Collections werden nach 7 weiteren Tagen durch MongoDB-TTL-Index automatisch gelöscht.
{
  token:         String (32 Hex, unique)
  file:          ObjectId → File
  createdBy:     ObjectId → User
  label:         String (max 100)
  password:      String | null (bcrypt)
  expiresAt:     Date | null
  downloadLimit: Number (-1 = unbegrenzt)
  downloadCount: Number
  createdAt:     Date
}
Wie Collections: 7 Tage Karenzzeit nach Ablauf via TTL-Index.

SiteSettings (src/models/SiteSettings.js)

Singleton-Dokument (_id: 'singleton'):

{
  operatorName:        String
  operatorAddress:     String
  operatorEmail:       String
  cloudflareAnalytics: Boolean
  fileRetentionDays:   Number (0 = deaktiviert)
  encryptionAtRest:    Boolean
  sessionDurationDays: Number (Standard: 7)
  allowRegistration:   Boolean
}

AuditLog (src/models/AuditLog.js)

{
  timestamp: Date (TTL-Index: 90 Tage)
  userId:    ObjectId → User | null
  username:  String | null
  action:    String
  ip:        String | null
  meta:      Mixed (action-spezifische Metadaten)
}

7. REST API

Basis-URL: /api

Alle JSON-Endpunkte geben Content-Type: application/json zurück. Fehler: { "error": "Nachricht" }.


Authentifizierung (/api/auth)

MethodePfadAuthBeschreibung
GET/meSessionEingeloggter Nutzer
POST/loginEinloggen (rate-limited: 10/15min)
POST/registerRegistrieren (rate-limited, erster Nutzer wird Admin)
POST/logoutAusloggen
GET/smtp-enabledPrüft ob SMTP konfiguriert ist
GET/verify-email?token=E-Mail-Adresse bestätigen
GET/verify-reset-token?token=Reset-Token prüfen
POST/forgot-passwordPasswort-Reset-E-Mail senden (rate-limited: 5/Std.)
POST/reset-passwordNeues Passwort setzen

Login-Request:

POST /api/auth/login
{ "username": "max", "password": "meinPasswort123" }

Login-Response:

{
  "user": {
    "id": "...", "username": "max", "role": "user",
    "avatarUrl": null, "email": "[email protected]",
    "emailVerified": true, "language": "de"
  }
}

Datei-Upload

MethodePfadAuthBeschreibung
POST/uploadAPI-KeyShareX-Upload (Legacy-Endpoint in app.js)
POST/api/uploadAPI-KeyShareX/API-Upload (Feld: file)
POST/api/web-uploadSessionWeb-Upload (Feld: files[], max 500)
POST/api/chunk/initSessionChunked Upload initialisieren
POST/api/chunk/:uploadIdSessionEinen Chunk hochladen (Feld: chunk)
POST/api/chunk/:uploadId/completeSessionChunks zusammenführen
DELETE/api/chunk/:uploadIdSessionUpload abbrechen & aufräumen

API-Upload-Response:

{
  "url": "https://example.com/f/a1b2c3d4",
  "raw": "https://example.com/f/a1b2c3d4/raw",
  "delete_url": "https://example.com/api/delete/a1b2c3d4",
  "short_id": "a1b2c3d4",
  "filename": "screenshot.png",
  "size": 102400
}

Chunked-Upload-Flow

1. POST /api/chunk/init{ uploadId: "32hex" }

2. POST /api/chunk/:uploadId (Body: chunkIndex=N, File: chunk) → { received: N }

Parallel mit 3–5 gleichzeitigen Chunks

3. POST /api/chunk/:uploadId/complete{ files: [fileObject] }


Dateiverwaltung

MethodePfadAuthBeschreibung
GET/api/gallerySessionEigene Dateien (Admin: alle), paginiert (24/Seite), Filter: q, type, tag, page
GET/api/file/:shortIdDatei-Metadaten (inkrementiert View-Counter)
PATCH/api/file/:shortIdSessionTags/Name aktualisieren
DELETE/api/file/:shortIdSessionDatei löschen
DELETE/api/delete/:shortIdAPI-KeyDatei löschen (API-Key-Auth)
POST/api/files/bulkSessionBulk-Aktionen: delete, tag, removeTag, addToCollection, moveToCollection
GET/api/tagsSessionAlle Tag-Vorschläge des Nutzers

Gallery-Query-Parameter:


Nutzer-Einstellungen

MethodePfadAuthBeschreibung
GET/api/my-keySessionAPI-Key-Prefix anzeigen
POST/api/regen-keySessionAPI-Key neu generieren
GET/api/sharex-configSessionShareX .sxcu herunterladen (regeneriert Key)
PATCH/api/user/usernameSessionBenutzername ändern (Passwort erforderlich)
PATCH/api/user/passwordSessionPasswort ändern
PATCH/api/user/emailSessionE-Mail ändern (sendet Bestätigungs-E-Mail)
PATCH/api/user/languageSessionUI-Sprache setzen
PATCH/api/user/embed-modeSessionEmbed-Modus setzen (embed/raw)
POST/api/user/resend-verificationSessionBestätigungs-E-Mail erneut senden
GET/api/user/exportSessionDaten-Export (GDPR Art. 20) als JSON
DELETE/api/user/accountSessionAccount löschen (GDPR Art. 17, Passwort erforderlich)
GET/api/user/predefined-tagsSessionVordefinierte Tags abrufen
PATCH/api/user/predefined-tagsSessionVordefinierte Tags aktualisieren
POST/api/user/avatarSessionAvatar hochladen (max 2 MB, JPEG/PNG/GIF/WebP)
DELETE/api/user/avatarSessionAvatar löschen
GET/api/user/avatar/:userIdAvatar servieren

MethodePfadAuthBeschreibung
GET/api/file/:shortId/share-linksSession (Owner/Admin)Alle Share-Links einer Datei
POST/api/file/:shortId/share-linksSession (Owner/Admin)Share-Link erstellen
DELETE/api/share-links/:tokenSession (Owner/Creator/Admin)Share-Link löschen
GET/api/share-links/:tokenShare-Link-Metadaten (öffentlich)
POST/api/share-links/:token/verifyShare-Link-Passwort prüfen

Share-Link erstellen:

POST /api/file/a1b2c3d4/share-links
{
  "label": "Für Kollegen",
  "password": "geheim",
  "expiresAt": "2025-12-31T23:59:59Z",
  "downloadLimit": 10
}

Collections

MethodePfadAuthBeschreibung
GET/api/collectionsSessionEigene Collections (Admin: alle)
POST/api/collectionsSessionCollection erstellen
GET/api/collections/:idCollection anzeigen (öffentlich, ggf. Passwort)
PATCH/api/collections/:idSession (Owner/Admin)Collection aktualisieren
DELETE/api/collections/:idSession (Owner/Admin)Collection löschen
POST/api/collections/:id/filesSession (Owner/Admin)Datei zur Collection hinzufügen
DELETE/api/collections/:id/files/:fileShortIdSession (Owner/Admin)Datei aus Collection entfernen
POST/api/collections/:id/verifyCollection-Passwort prüfen

Admin-Endpunkte

MethodePfadAuthBeschreibung
GET/api/admin/statsAdminDashboard-Statistiken
GET/api/admin/usersAdminAlle Nutzer (inkl. Datei-Counts)
POST/api/admin/usersAdminNutzer erstellen
PATCH/api/admin/users/:id/toggleAdminNutzer aktivieren/deaktivieren
PATCH/api/admin/users/:id/roleAdminNutzer-Rolle ändern
DELETE/api/admin/users/:idAdminNutzer löschen
POST/api/admin/users/:id/regen-keyAdminAPI-Key regenerieren
PATCH/api/admin/users/:id/passwordAdminPasswort setzen
PATCH/api/admin/users/:id/folderAdminOrdnername ändern (verschiebt Dateien)
GET/api/admin/filesAdminAlle Dateien, paginiert (30/Seite)
GET/api/admin/site-settingsAdminBetreiber-Einstellungen lesen
PATCH/api/admin/site-settingsAdminBetreiber-Einstellungen aktualisieren
GET/api/admin/audit-logAdminPaginierter Audit-Log (50/Seite)
GET/api/admin/audit-log/exportAdminAudit-Log als CSV herunterladen
GET/api/site-settingsÖffentliche Betreiber-Infos (für Datenschutzseite)

Site-Settings (öffentlich)

GET /api/site-settings
{
  "operatorName": "Musterfirma GmbH",
  "operatorAddress": "Musterstr. 1, 12345 Musterstadt",
  "operatorEmail": "[email protected]",
  "cloudflareAnalytics": false,
  "fileRetentionDays": 365,
  "encryptionAtRest": false,
  "sessionDurationDays": 7
}

8. WebSocket API

Verbindung: wss://example.com/ws (nur für eingeloggte Nutzer, Session-Cookie erforderlich)

Protokoll

Client → Server (Request):

{ "id": "req-abc123", "action": "file:list", "payload": { "type": "image", "page": 1 } }

Server → Client (Response):

{ "id": "req-abc123", "data": { ... } }

Server → Client (Error):

{ "id": "req-abc123", "error": "Forbidden", "status": 403 }

Server → Client (Broadcast):

{ "event": "file:uploaded", "data": { "shortId": "a1b2c3d4", "uploaderId": "..." } }

Verfügbare Actions

ActionAuthBeschreibung
site-settings:getÖffentliche Site-Einstellungen
auth:meUserEigene Nutzer-Daten
file:getUserDatei-Details (inkrementiert Views)
file:listUserDateiliste mit Filter/Paginierung
file:deleteUserDatei löschen
user:get-keyUserAPI-Key-Prefix
user:regen-keyUserAPI-Key neu generieren
user:change-passwordUserPasswort ändern
user:change-usernameUserBenutzername ändern
user:change-emailUserE-Mail ändern
user:change-languageUserSprache setzen
user:change-embed-modeUserEmbed-Modus setzen
user:resend-verificationUserBestätigungs-E-Mail erneut senden
user:exportUserDaten-Export
user:delete-accountUserAccount löschen
admin:statsAdminDashboard-Statistiken
admin:settings:getAdminSite-Einstellungen lesen
admin:settings:updateAdminSite-Einstellungen aktualisieren
admin:users:listAdminAlle Nutzer
admin:users:createAdminNutzer erstellen
admin:users:toggleAdminNutzer aktivieren/deaktivieren
admin:users:roleAdminNutzer-Rolle ändern
admin:users:deleteAdminNutzer löschen
admin:users:regen-keyAdminAPI-Key regenerieren
admin:users:passwordAdminPasswort setzen
admin:users:folderAdminOrdnername ändern
admin:files:listAdminAlle Dateien
admin:audit-log:listAdminPaginierter Audit-Log

Broadcast-Events

EventEmpfängerPayload
file:uploadedUploader{ shortId, uploaderId }
file:deletedDatei-Owner{ shortId, uploaderId }
file:viewAlle{ shortId, views }
user:createdAdmins{ id, username, role, ... }
user:deletedAdmins{ id }
user:updatedAdmins{ id, ...geänderte Felder }
audit:logAdminsVollständiges AuditLog-Objekt
settings:updatedAdminsAktualisiertes SiteSettings-Objekt
stats:invalidateAdmins{} (Stats-Refresh auslösen)

9. Datei-Serving-Routen

/f/:shortId — Datei-Viewer

- embedMode = 'embed': OG-HTML mit Redirect

- embedMode = 'raw' + Bild/Video/Audio: HTTP 302 → /f/:shortId/raw

/f/:shortId/raw — Direktzugriff

Liefert die Datei als HTTP-Response mit Range-Request-Unterstützung (206 Partial Content). Bilder/Videos/Audio: Content-Disposition: inline, andere: attachment.

/f/:shortId/download — Force-Download

Wie /raw, aber immer Content-Disposition: attachment.

/f/:shortId/thumb — Thumbnail

Liefert JPEG-Thumbnail (für Videos und PDFs). Cache-Control: public, max-age=86400.

/f/:shortId/delete/:token — ShareX-Deletion

Löscht Datei ohne Session via eindeutigem Delete-Token.

Prüft Passwort (via Session-Flag), Ablaufdatum und Download-Limit, dann liefert die Datei aus.


10. Authentifizierung & Sicherheit

Session-Authentifizierung

API-Key-Authentifizierung

Middleware-Kette (src/middleware/auth.js)

requireLogin    → prüft req.session.user → 401 wenn nicht eingeloggt
requireAdmin    → wie requireLogin, zusätzlich role === 'admin' → 403
requireApiKey   → prüft Authorization-Header oder req.body.token

CSRF-Schutz

requireSameOrigin() in app.js vergleicht Origin-Header mit Host-Header für alle API-Routen. Ergänzt sameSite: 'strict'-Cookies.

Content Security Policy

default-src 'self';
script-src 'self' 'unsafe-inline' https://static.cloudflareinsights.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: blob:;
media-src 'self' blob:;
connect-src 'self' https://cloudflareinsights.com;
frame-ancestors 'self';

Security-Headers

Rate-Limiting

EndpunktLimitFenster
Upload (/upload, /api/upload, /api/web-upload)60 Requests15 Minuten
Auth (/api/auth/login, /register, etc.)10 Requests15 Minuten
Passwort-Reset5 Requests1 Stunde

Datei-Blockliste

Folgende MIME-Types und Erweiterungen werden auf Upload abgelehnt:

Blockierte MIME-Types: application/x-executable, application/x-sh, application/x-csh, application/x-bat

Blockierte Erweiterungen: .bat, .cmd, .com, .ps1, .psm1, .psd1, .sh, .bash, .csh, .zsh, .fish, .vbs, .vbe, .jse, .scr, .pif, .application, .gadget, .hta, .php, .php3–5, .phtml, .asp, .aspx, .jsp, .jspx, .cfm

Path-Traversal-Schutz

Alle Datei-Zugriffe über resolveUploadPath() prüfen, ob der aufgelöste Pfad innerhalb von UPLOAD_DIR liegt.


11. Upload-System

Standard-Upload (Multer)

Chunked Upload (>250 MB)

Der Frontend-Client wechselt automatisch in den Chunked-Modus für große Dateien.

Server-seitige Verzeichnisstruktur:

uploads/.chunks/{uploadId}/
  meta.json          { filename, mimeType, totalSize, totalChunks, userId, createdAt }
  chunk-0
  chunk-1
  ...
  chunk-N

Chunk-Größe: 10–20 MB (max 51 MB akzeptiert für Rückwärtskompatibilität)

Parallelität: 3–5 gleichzeitige Chunk-Uploads

Assemblierung: Stream-basiert (kein vollständiges Laden in RAM)

Avatar-Upload


12. Thumbnail-Generierung

Thumbnails werden asynchron nach dem Upload generiert (.catch(() => {}) — Fehler werden ignoriert).

Video-Thumbnails (ffmpeg)

ffmpeg -y -i <file> -ss 00:00:01 -vframes 1 \
  -vf "scale=320:320:force_original_aspect_ratio=increase,crop=320:320" \
  -q:v 3 uploads/.thumbnails/<shortId>.jpg

PDF-Thumbnails (Ghostscript)

gs -dNOPAUSE -dBATCH -dSAFER \
  -sDEVICE=jpeg -dFirstPage=1 -dLastPage=1 \
  -r72 -dJPEGQ=85 \
  -sOutputFile=uploads/.thumbnails/<shortId>.jpg \
  <file>

Timeout: 30 Sekunden pro Thumbnail-Generierung

Fallback: Wenn ffmpeg/ghostscript nicht verfügbar, wird die Generierung lautlos übersprungen.

Backfill-Skript

npm run migrate:thumbnails
# oder im Container:
docker exec -it <container> npm run migrate:thumbnails

13. E-Mail & SMTP

Konfiguration

SMTP wird aktiviert, wenn SMTP_HOST gesetzt ist. mailer.isConfigured() prüft diesen Wert.

E-Mail-Templates

Alle E-Mails werden in der Sprache des Nutzers gesendet (8 Sprachen). Templates sind in src/utils/mailer.js eingebettet.

Gesendete E-Mail-Typen:

TypTriggerToken-Gültigkeit
E-Mail-BestätigungRegistrierung, E-Mail-Änderung24 Stunden
Passwort-ResetPOST /api/auth/forgot-password1 Stunde

Token-Sicherheit


14. Internationalisierung

Bibliothek: i18next + react-i18next + i18next-browser-languagedetector

Unterstützte Sprachen:

CodeSprache
enEnglish
deDeutsch
frFrançais
esEspañol
itItaliano
ptPortuguês
ja日本語
zh中文

Sprachauswahl:

1. Browser-Language-Detection (automatisch)

2. Nutzer-Einstellung in der Datenbank (user.language)

3. Persistierung über PATCH /api/user/language

Übersetzungsdateien: client/src/i18n/locales/{code}.json


15. Frontend (React SPA)

Router-Konfiguration (client/src/App.jsx)

RouteKomponenteAuth
/Redirect → /galleryNein
/auth/loginLogin.jsxNein
/auth/registerRegister.jsxNein
/auth/forgot-passwordForgotPassword.jsxNein
/auth/reset-passwordResetPassword.jsxNein
/installInstall.jsxNein
/uploadUpload.jsxJa
/galleryGallery.jsxJa
/f/:shortIdFileView.jsxJa
/collectionsCollections.jsxJa
/c/:idCollectionView.jsxNein (öffentlich)
/s/:tokenShareView.jsxNein (öffentlich)
/settingsSettings.jsxJa
/adminDashboard.jsxAdmin
/admin/usersUsers.jsxAdmin
/admin/filesFiles.jsxAdmin
/admin/audit-logAuditLog.jsxAdmin
/admin/site-settingsSiteSettings.jsxAdmin
/admin/importImport.jsxAdmin
/privacyPrivacyPolicy.jsxNein
/termsTermsOfService.jsxNein

Auth-Context (client/src/context/AuthContext.jsx)

Globaler State für den eingeloggten Nutzer. Wird beim App-Start via GET /api/auth/me initialisiert.

WebSocket-Hook (client/src/hooks/useWebSocket.js)

Verwaltet die persistente WS-Verbindung. Stellt sendMessage() und Event-Handler-Registration bereit. Reconnect-Logik bei Verbindungsabbruch.

UI-Komponenten

Basiert auf shadcn/ui (Radix UI Primitives + Tailwind CSS):


16. Admin-Dashboard

Das Admin-Dashboard (/admin/*) ist nur für Nutzer mit role: 'admin' zugänglich.

Dashboard (/admin)

Nutzerverwaltung (/admin/users)

Dateiverwaltung (/admin/files)

Audit-Log (/admin/audit-log)

Site-Einstellungen (/admin/site-settings)

XBackBone-Import (/admin/import)


17. GDPR / Datenschutz

FeatureDSGVO-Artikel
Datenschutzerklärung (konfigurierbar)Art. 13/14 – Transparenz
AGB-Seite (konfigurierbar)Art. 13/14 – Transparenz
Daten-Export (JSON mit URLs)Art. 20 – Datenportabilität
Account-Selbstlöschung (Dateien + Daten)Art. 17 – Löschrecht
Audit-Log (90-Tage-TTL via MongoDB)Art. 5(2) – Rechenschaftspflicht
Audit-Log-CSV-ExportArt. 5(2) – Rechenschaftspflicht
Konfigurierbarer Datei-RetentionArt. 5(1)(e) – Speicherbegrenzung
API-Keys als SHA-256-HashArt. 32 – Sicherheit
Passwörter als bcrypt (12 Rounds)Art. 32 – Sicherheit
Cookie-Consent für Cloudflare AnalyticsArt. 13 – Transparenz
Anonymisierung bei Account-LöschungArt. 17 – Löschrecht

GDPR-Lösch-Flow

Bei Account-Löschung (user:delete-account / DELETE /api/user/account):

1. Alle Dateien des Nutzers werden von der Festplatte gelöscht

2. Thumbnails werden gelöscht

3. Avatar wird gelöscht

4. Audit-Log-Einträge werden anonymisiert (username: '[deleted]', ip: null, userId: null)

5. User-Dokument wird gelöscht

6. Session wird zerstört


18. Hintergrund-Jobs

Retention-Cleanup (src/jobs/retentionCleanup.js)

MongoDB-TTL-Indizes (automatisch)

CollectionTTLTrigger
AuditLog90 Tagetimestamp
Collection7 Tage nach expiresAtexpiresAt
ShareLink7 Tage nach expiresAtexpiresAt

19. Deployment

Docker (empfohlen)

git clone https://github.com/Christianoooooo/sharely.git
cd sharely
cp .env.example .env
# .env bearbeiten (SESSION_SECRET, MONGO-Passwörter, BASE_URL)
docker compose up -d

Services:

Volumes:

Health-Checks: App prüft HTTP 200 auf /, MongoDB prüft db.adminCommand('ping').

Nginx-Reverse-Proxy

server {
    listen 443 ssl;
    server_name files.example.com;

    client_max_body_size 2100M;  # Mind. so groß wie größter Chunk + Puffer

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket-Support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
BASE_URL in .env muss der öffentlichen Domain entsprechen.

Erster Start

Der erste registrierte Nutzer erhält automatisch die admin-Rolle (User.countDocuments() === 0).


20. Entwicklungsumgebung

Voraussetzungen

Setup

# Backend
npm install
cp .env.example .env
# .env anpassen

# Frontend
cd client
npm install

Starten

# Backend (Port 3000, mit nodemon)
npm run dev

# Frontend (Port 5173, separates Terminal)
cd client
npm run dev

Der Vite-Dev-Server proxyt API-Requests automatisch an localhost:3000.

Build

npm run build   # Baut client/dist/, Backend bleibt unverändert
npm start       # Startet produktiven Express-Server

21. Migrationen & Skripte

Automatische Migrationen (bei jedem Start)

Diese Migrationen laufen beim App-Start in app.js und sind idempotent:

MigrationDateiFunktion
User-Folder-Migrationsrc/migrations/migrateUserFolders.jsVerschiebt Dateien aus uploads/ in uploads/{folderName}/
API-Key-Hash-Migrationsrc/migrations/migrateApiKeyHashes.jsKonvertiert Klartext-API-Keys zu SHA-256-Hashes

Manuelle Skripte

# Thumbnails für bereits vorhandene Dateien generieren
npm run migrate:thumbnails
# oder:
node scripts/generate-missing-thumbnails.js

# Uploads in Nutzer-Ordner verschieben (manuell)
npm run migrate:user-folders
# oder:
node scripts/migrate-uploads-to-user-folders.js

Datenbankinitialisierung (Docker)

scripts/mongo-init.js wird beim ersten Start des MongoDB-Containers ausgeführt und erstellt den App-Nutzer mit den richtigen Berechtigungen.


22. End-to-End-Tests

Framework: Playwright (@playwright/test)

Test-Dateien

DateiTestsuite
e2e/upload.spec.jsUpload-Flows
e2e/gallery.spec.jsGalerie und Datei-Verwaltung
e2e/admin.spec.jsAdmin-Dashboard
e2e/sharelink.spec.jsShare-Link-Erstellung und -Nutzung
e2e/tags.spec.jsTag-Verwaltung
e2e/bulk-actions-fixes.spec.jsBulk-Aktionen

Ausführen

# Alle Tests
npm run test:e2e

# Mit UI
npm run test:e2e:ui

Playwright-Konfiguration: playwright.config.js

Global Setup: e2e/global-setup.js (erstellt Test-Nutzer, -Admin etc.)

Helfer: e2e/helpers.js (gemeinsame Hilfsfunktionen)


Anhang: Audit-Log-Aktionen

AktionTrigger
loginErfolgreicher Login
logoutLogout
registerRegistrierung
uploadDatei-Upload
delete_fileDatei gelöscht
delete_accountAccount gelöscht
change_passwordPasswort geändert
change_usernameBenutzername geändert
change_emailE-Mail geändert
verify_emailE-Mail bestätigt
forgot_passwordPasswort-Reset angefordert
reset_passwordPasswort zurückgesetzt
regen_api_keyAPI-Key regeneriert
sharex_configShareX-Konfiguration heruntergeladen
export_dataDaten-Export
admin_create_userAdmin: Nutzer erstellt
admin_delete_userAdmin: Nutzer gelöscht
admin_toggle_userAdmin: Nutzer aktiviert/deaktiviert
admin_change_roleAdmin: Nutzer-Rolle geändert
admin_change_passwordAdmin: Passwort gesetzt
admin_regen_keyAdmin: API-Key regeneriert

*Dokumentation generiert aus dem Quellcode von sharely v1.0.0*