Sharely — Documentazione Tecnica

Versione: 1.0.0 · Licenza: MIT · Node.js: ≥ 18

Indice

1. Panoramica del progetto

2. Architettura

3. Stack tecnologico

4. Struttura delle directory

5. Configurazione e variabili d'ambiente

6. Modelli di dati

7. API REST

8. API WebSocket

9. Route di servizio dei file

10. Autenticazione e sicurezza

11. Sistema di upload

12. Generazione delle miniature

13. Email e SMTP

14. Internazionalizzazione

15. Frontend (React SPA)

16. Pannello di amministrazione

17. GDPR / Privacy

18. Job in background

19. Deployment

20. Ambiente di sviluppo

21. Migrazioni e script

22. Test end-to-end


1. Panoramica del progetto

Sharely è una piattaforma di condivisione file self-hosted con un'interfaccia web pulita, integrazione con ShareX e accesso tramite API. Gli utenti caricano screenshot, file e contenuti multimediali e li condividono istantaneamente tramite link brevi.

Funzionalità principali

FunzionalitàDescrizione
Upload webDrag-and-drop, fino a 500 file contemporaneamente
Upload a blocchiFile fino a 2 GB tramite upload multi-part parallelo
Integrazione ShareXFile di configurazione .sxcu scaricabile con un clic
Upload via APIAutenticazione con token Bearer, compatibile con curl/wget
Visualizzatore fileZoom sulle immagini, streaming video/audio (HTTP Range), PDF inline, codice con evidenziazione della sintassi
Modalità di incorporazione*embed* (HTML OG/Twitter Card) o *raw* (reindirizzamento diretto)
MiniatureAnteprime JPEG automatiche per video (ffmpeg) e PDF (ghostscript)
RaccolteGruppi di file con password e data di scadenza opzionali
Link di condivisioneLink per file con password, scadenza e limite di download
Interfaccia in tempo realeAggiornamenti live via WebSocket (upload, eliminazione, contatore visualizzazioni, statistiche admin)
Multilingua8 lingue: EN, DE, FR, ES, IT, PT, JA, ZH
Pannello adminStatistiche, gestione utenti, gestione file, log di audit (esportazione CSV)
Conformità GDPRFunzionalità privacy conformi al GDPR dell'UE (Art. 17, 20, 32, ecc.)
Importazione XBackBoneMigrazione di installazioni XBackBone esistenti
Pronto per Dockerdocker compose up -d avvia l'ambiente completo

2. Architettura

┌─────────────────────────────────────────────────────────────┐
│                        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/ │
              └─────────────────────────────┘

Flusso di dati: Upload standard

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: [...] }

Flusso di dati: Upload ShareX

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. Stack tecnologico

LivelloTecnologiaVersione
RuntimeNode.js≥ 18
Framework backendExpress.js4.x
DatabaseMongoDB + MongooseMongo 7, Mongoose 8.x
Sessioniexpress-session + connect-mongo
Tempo realeWebSocket (ws)8.x
Upload fileMulter1.x
EmailNodemailer8.x
Hashing passwordbcryptjs2.x (12 round)
Hashing chiavi APISHA-256 (Node Crypto)
Rate limitingexpress-rate-limit8.x
Importazione XBackBonesql.js1.x
Framework frontendReact 1818.3.x
Routing (frontend)React Router v66.x
Strumento di buildVite6.x
StileTailwind CSS + Radix UI3.x
Componenti UIshadcn/ui (Radix primitives)
IconeFontAwesome6.x
i18ni18next + react-i18next
Evidenziazione sintassihighlight.js11.x
ContainerDocker + Docker Compose
TestPlaywright (E2E)1.60.x

4. Struttura delle directory

sharely/
├── app.js                          # Punto di ingresso Express, sequenza di avvio
├── package.json
├── .env.example                    # Modello per tutte le variabili d'ambiente
├── Dockerfile
├── docker-compose.yml
│
├── src/
│   ├── config/
│   │   └── db.js                   # Connessione MongoDB (Mongoose)
│   ├── middleware/
│   │   ├── auth.js                 # requireLogin / requireAdmin / requireApiKey
│   │   └── upload.js               # Configurazione Multer, lista di blocco
│   ├── models/
│   │   ├── AuditLog.js             # Eventi di audit (TTL 90 giorni)
│   │   ├── Collection.js           # Raccolte di file
│   │   ├── File.js                 # Metadati dei file
│   │   ├── ShareLink.js            # Link di condivisione per file
│   │   ├── SiteSettings.js         # Singleton: impostazioni operatore
│   │   └── User.js                 # Account utente + chiavi API
│   ├── routes/
│   │   ├── api.js                  # API principale (upload, galleria, admin, ...)
│   │   ├── auth.js                 # Login / Registrazione / Reset password
│   │   ├── files.js                # Servizio file, embed OG, richieste di intervallo
│   │   ├── import.js               # Migrazione XBackBone
│   │   ├── install.js              # Endpoint di installazione iniziale
│   │   └── shares.js               # Servizio file tramite link di condivisione
│   ├── jobs/
│   │   └── retentionCleanup.js     # Eliminazione giornaliera dei file scaduti
│   ├── migrations/
│   │   ├── migrateApiKeyHashes.js  # Una tantum: testi in chiaro → hash SHA-256
│   │   └── migrateUserFolders.js   # Una tantum: spostamento file nelle cartelle utente
│   ├── utils/
│   │   ├── audit.js                # Funzione helper logAudit()
│   │   ├── generateThumbnail.js    # Integrazione ffmpeg / ghostscript
│   │   ├── mailer.js               # Wrapper Nodemailer + template email i18n
│   │   └── sanitizeFilename.js     # Sanificazione del nome file
│   └── ws.js                       # Server WebSocket + dispatcher azioni
│
├── client/                         # Frontend React
│   ├── index.html
│   ├── package.json
│   ├── vite.config.js
│   ├── tailwind.config.js
│   └── src/
│       ├── main.jsx                # Punto di ingresso React
│       ├── App.jsx                 # Configurazione del router
│       ├── index.css               # Stili globali
│       ├── context/
│       │   └── AuthContext.jsx     # Stato di autenticazione globale
│       ├── hooks/
│       │   ├── use-toast.js        # Hook per notifiche toast
│       │   └── useWebSocket.js     # Connessione WS + gestori eventi
│       ├── components/
│       │   ├── Layout.jsx          # Shell dell'app (barra di navigazione, sidebar)
│       │   ├── ProtectedRoute.jsx  # Guardia di autenticazione
│       │   ├── ShareLinkDialog.jsx # Dialogo di creazione link di condivisione
│       │   ├── AddToCollectionDialog.jsx
│       │   ├── CookieBanner.jsx
│       │   ├── LanguageSelector.jsx
│       │   ├── RequireEmailDialog.jsx
│       │   ├── UserAvatar.jsx
│       │   └── ui/                 # Componenti base shadcn/ui
│       ├── pages/
│       │   ├── Upload.jsx          # Pagina di upload
│       │   ├── Gallery.jsx         # Galleria file
│       │   ├── FileView.jsx        # Vista dettaglio file
│       │   ├── Collections.jsx     # Panoramica raccolte
│       │   ├── CollectionView.jsx  # Raccolta singola
│       │   ├── ShareView.jsx       # Pagina pubblica del link di condivisione
│       │   ├── Settings.jsx        # Impostazioni utente
│       │   ├── Login.jsx
│       │   ├── Register.jsx
│       │   ├── ForgotPassword.jsx
│       │   ├── ResetPassword.jsx
│       │   ├── Install.jsx         # Installazione iniziale
│       │   ├── PrivacyPolicy.jsx
│       │   ├── TermsOfService.jsx
│       │   └── admin/
│       │       ├── Dashboard.jsx   # Home page admin
│       │       ├── Users.jsx       # Gestione utenti
│       │       ├── Files.jsx       # Gestione file
│       │       ├── AuditLog.jsx    # Vista log di audit
│       │       ├── SiteSettings.jsx# Impostazioni operatore
│       │       └── Import.jsx      # Importazione XBackBone
│       ├── i18n/
│       │   ├── index.js            # Configurazione i18next
│       │   └── locales/
│       │       ├── de.json
│       │       ├── en.json
│       │       ├── es.json
│       │       ├── fr.json
│       │       ├── it.json
│       │       ├── ja.json
│       │       ├── pt.json
│       │       └── zh.json
│       └── lib/
│           └── utils.js            # Helper Tailwind (cn())
│
├── scripts/
│   ├── setup-db.js
│   ├── mongo-init.js               # Script di inizializzazione MongoDB
│   ├── migrate-uploads-to-user-folders.js
│   └── generate-missing-thumbnails.js
│
├── e2e/                            # Test Playwright
│   ├── 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/                        # File caricati (runtime)
│   ├── .thumbnails/
│   ├── .avatars/
│   └── .chunks/                    # Blocchi temporanei
│
└── docs/assets/                    # Screenshot e logo

5. Configurazione e variabili d'ambiente

Tutte le variabili vengono caricate da .env (tramite dotenv). Il file .env.example contiene il modello completo.

Campi obbligatori

VariabileDescrizione
SESSION_SECRETSegreto per la crittografia delle sessioni — stringa casuale lunga, es. openssl rand -hex 32
MONGO_ROOT_PASSWORDPassword root di MongoDB (richiesta solo per Docker Compose)
MONGO_APP_PASSWORDPassword dell'utente applicativo MongoDB

Tutte le variabili d'ambiente

VariabilePredefinitoDescrizione
PORT3000Porta TCP del server HTTP
MONGODB_URI_(costruita da Docker Compose)_URI di connessione MongoDB completa
MONGO_ROOT_PASSWORDPassword root di MongoDB
MONGO_APP_USERappuserNome utente applicativo MongoDB
MONGO_APP_PASSWORDPassword dell'utente applicativo MongoDB
MONGO_DB_NAMEsharelyNome del database MongoDB
SESSION_SECRETObbligatorio — segreto di crittografia delle sessioni
BASE_URLhttp://localhost:3000URL base pubblica per i link di condivisione generati (senza / finale)
SITE_NAMEsharelyNome del sito negli embed Open Graph
MAX_FILE_SIZE_MB100Dimensione massima file per gli upload standard in MB (upload a blocchi fino a 2 GB indipendentemente)
ALLOW_REGISTRATIONtruefalse disabilita la registrazione pubblica
SMTP_HOSTHostname del server SMTP; lasciare vuoto per disabilitare le funzionalità email
SMTP_PORT587Porta SMTP
SMTP_SECUREfalsetrue per TLS implicito (porta 465), false per STARTTLS
SMTP_USERNome utente SMTP
SMTP_PASSPassword SMTP
SMTP_FROM_(SMTP_USER)_Indirizzo mittente nelle email in uscita
UPLOAD_DIR./uploadsPercorso assoluto alla directory di upload
NODE_ENVproduction abilita i cookie sicuri

6. Modelli di dati

User (src/models/User.js)

{
  username:                    String (3–32, unico, alfanumerico + _-)
  password:                    String (bcrypt, 12 round)
  role:                        'admin' | 'user'
  apiKey:                      String (legacy, vuoto dopo la migrazione)
  apiKeyHash:                  String (SHA-256, unico, sparse)
  apiKeyPrefix:                String (primi 8 caratteri del testo in chiaro)
  folderName:                  String (unico, sparse, max 64)
  avatarExt:                   String | null (.jpg/.png/.gif/.webp)
  embedMode:                   'embed' | 'raw'
  isActive:                    Boolean
  email:                       String (minuscolo, unico, sparse)
  emailVerified:               Boolean
  emailVerificationToken:      String | null (hash SHA-256 del testo in chiaro)
  emailVerificationExpires:    Date | null
  passwordResetToken:          String | null (hash SHA-256)
  passwordResetExpires:        Date | null
  language:                    'en'|'de'|'fr'|'es'|'it'|'pt'|'ja'|'zh'
  predefinedTags:              [String] (max 100 tag × 50 caratteri)
  createdAt:                   Date
}

Metodi importanti:

File (src/models/File.js)

{
  shortId:      String (8 caratteri esadecimali: 6 timestamp + 2 casuali, unico)
  deleteToken:  String (32 caratteri esadecimali, unico)
  originalName: String (sanificato)
  storedName:   String (percorso relativo: "folderName/8hex.ext")
  mimeType:     String
  size:         Number (byte)
  uploader:     ObjectId → User
  views:        Number
  tags:         [String] (max 20 × 50 caratteri)
  createdAt:    Date
}

Proprietà calcolate:

Algoritmo Short ID:

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

Collection (src/models/Collection.js)

{
  shortId:     String (8 Hex, unico)
  name:        String (max 100)
  description: String (max 500)
  owner:       ObjectId → User
  files:       [ObjectId → File]
  password:    String | null (bcrypt)
  expiresAt:   Date | null
  createdAt:   Date
}
Le raccolte scadute vengono eliminate automaticamente dall'indice TTL di MongoDB 7 giorni dopo la scadenza.
{
  token:         String (32 Hex, unico)
  file:          ObjectId → File
  createdBy:     ObjectId → User
  label:         String (max 100)
  password:      String | null (bcrypt)
  expiresAt:     Date | null
  downloadLimit: Number (-1 = illimitato)
  downloadCount: Number
  createdAt:     Date
}
Come le raccolte: periodo di grazia di 7 giorni dopo la scadenza tramite indice TTL.

SiteSettings (src/models/SiteSettings.js)

Documento singleton (_id: 'singleton'):

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

AuditLog (src/models/AuditLog.js)

{
  timestamp: Date (indice TTL: 90 giorni)
  userId:    ObjectId → User | null
  username:  String | null
  action:    String
  ip:        String | null
  meta:      Mixed (metadati specifici dell'azione)
}

7. API REST

URL base: /api

Tutti gli endpoint JSON restituiscono Content-Type: application/json. Errori: { "error": "messaggio" }.


Autenticazione (/api/auth)

MetodoPercorsoAuthDescrizione
GET/meSessioneUtente attualmente connesso
POST/loginAccesso (limitato: 10/15min)
POST/registerRegistrazione (limitata, il primo utente diventa admin)
POST/logoutDisconnessione
GET/smtp-enabledVerifica se SMTP è configurato
GET/verify-email?token=Verificare l'indirizzo email
GET/verify-reset-token?token=Validare il token di reset
POST/forgot-passwordInviare l'email di reset password (limitato: 5/h)
POST/reset-passwordImpostare la nuova password

Richiesta di accesso:

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

Risposta di accesso:

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

Upload di file

MetodoPercorsoAuthDescrizione
POST/uploadChiave APIUpload ShareX (endpoint legacy in app.js)
POST/api/uploadChiave APIUpload ShareX/API (campo: file)
POST/api/web-uploadSessioneUpload web (campo: files[], max 500)
POST/api/chunk/initSessioneInizializzare upload a blocchi
POST/api/chunk/:uploadIdSessioneCaricare un blocco (campo: chunk)
POST/api/chunk/:uploadId/completeSessioneAssemblare i blocchi
DELETE/api/chunk/:uploadIdSessioneAnnullare upload e pulire

Risposta di upload API:

{
  "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
}

Flusso di upload a blocchi

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

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

In parallelo con 3–5 blocchi simultanei

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


Gestione file

MetodoPercorsoAuthDescrizione
GET/api/gallerySessioneFile propri (admin: tutti), paginato (24/pagina), filtri: q, type, tag, page
GET/api/file/:shortIdMetadati del file (incrementa il contatore visualizzazioni)
PATCH/api/file/:shortIdSessioneAggiornare tag/nome
DELETE/api/file/:shortIdSessioneEliminare file
DELETE/api/delete/:shortIdChiave APIEliminare file (autenticazione chiave API)
POST/api/files/bulkSessioneAzioni in blocco: delete, tag, removeTag, addToCollection, moveToCollection
GET/api/tagsSessioneTutti i suggerimenti di tag dell'utente

Parametri di query della galleria:


Impostazioni utente

MetodoPercorsoAuthDescrizione
GET/api/my-keySessioneMostra il prefisso della chiave API
POST/api/regen-keySessioneRigenerare la chiave API
GET/api/sharex-configSessioneScaricare ShareX .sxcu (rigenera la chiave)
PATCH/api/user/usernameSessioneCambiare nome utente (password richiesta)
PATCH/api/user/passwordSessioneCambiare password
PATCH/api/user/emailSessioneCambiare email (invia email di verifica)
PATCH/api/user/languageSessioneImpostare la lingua dell'interfaccia
PATCH/api/user/embed-modeSessioneImpostare la modalità di incorporazione (embed/raw)
POST/api/user/resend-verificationSessioneReinviare l'email di verifica
GET/api/user/exportSessioneEsportazione dati (GDPR Art. 20) come JSON
DELETE/api/user/accountSessioneEliminare account (GDPR Art. 17, password richiesta)
GET/api/user/predefined-tagsSessioneRecuperare tag predefiniti
PATCH/api/user/predefined-tagsSessioneAggiornare tag predefiniti
POST/api/user/avatarSessioneCaricare avatar (max 2 MB, JPEG/PNG/GIF/WebP)
DELETE/api/user/avatarSessioneEliminare avatar
GET/api/user/avatar/:userIdServire avatar

MetodoPercorsoAuthDescrizione
GET/api/file/:shortId/share-linksSessione (Proprietario/Admin)Tutti i link di condivisione di un file
POST/api/file/:shortId/share-linksSessione (Proprietario/Admin)Creare link di condivisione
DELETE/api/share-links/:tokenSessione (Proprietario/Creatore/Admin)Eliminare link di condivisione
GET/api/share-links/:tokenMetadati del link di condivisione (pubblico)
POST/api/share-links/:token/verifyVerificare la password del link di condivisione

Creare link di condivisione:

POST /api/file/a1b2c3d4/share-links
{
  "label": "Per i colleghi",
  "password": "segreto",
  "expiresAt": "2025-12-31T23:59:59Z",
  "downloadLimit": 10
}

Raccolte

MetodoPercorsoAuthDescrizione
GET/api/collectionsSessioneRaccolte proprie (admin: tutte)
POST/api/collectionsSessioneCreare raccolta
GET/api/collections/:idVisualizzare raccolta (pubblica, password se impostata)
PATCH/api/collections/:idSessione (Proprietario/Admin)Aggiornare raccolta
DELETE/api/collections/:idSessione (Proprietario/Admin)Eliminare raccolta
POST/api/collections/:id/filesSessione (Proprietario/Admin)Aggiungere file alla raccolta
DELETE/api/collections/:id/files/:fileShortIdSessione (Proprietario/Admin)Rimuovere file dalla raccolta
POST/api/collections/:id/verifyVerificare la password della raccolta

Endpoint di amministrazione

MetodoPercorsoAuthDescrizione
GET/api/admin/statsAdminStatistiche del pannello
GET/api/admin/usersAdminTutti gli utenti (con numero di file)
POST/api/admin/usersAdminCreare utente
PATCH/api/admin/users/:id/toggleAdminAttivare/disattivare utente
PATCH/api/admin/users/:id/roleAdminCambiare ruolo utente
DELETE/api/admin/users/:idAdminEliminare utente
POST/api/admin/users/:id/regen-keyAdminRigenerare chiave API
PATCH/api/admin/users/:id/passwordAdminImpostare password
PATCH/api/admin/users/:id/folderAdminCambiare nome cartella (sposta i file)
GET/api/admin/filesAdminTutti i file, paginato (30/pagina)
GET/api/admin/site-settingsAdminLeggere impostazioni operatore
PATCH/api/admin/site-settingsAdminAggiornare impostazioni operatore
GET/api/admin/audit-logAdminLog di audit paginato (50/pagina)
GET/api/admin/audit-log/exportAdminScaricare log di audit come CSV
GET/api/site-settingsInformazioni pubbliche dell'operatore (per la pagina privacy)

Impostazioni del sito (pubbliche)

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

8. API WebSocket

Connessione: wss://example.com/ws (solo per utenti connessi, cookie di sessione richiesto)

Protocollo

Client → Server (Richiesta):

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

Server → Client (Risposta):

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

Server → Client (Errore):

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

Server → Client (Broadcast):

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

Azioni disponibili

AzioneAuthDescrizione
site-settings:getImpostazioni pubbliche del sito
auth:meUtenteDati dell'utente connesso
file:getUtenteDettagli del file (incrementa le visualizzazioni)
file:listUtenteLista file con filtro/paginazione
file:deleteUtenteEliminare file
user:get-keyUtentePrefisso chiave API
user:regen-keyUtenteRigenerare chiave API
user:change-passwordUtenteCambiare password
user:change-usernameUtenteCambiare nome utente
user:change-emailUtenteCambiare email
user:change-languageUtenteImpostare lingua
user:change-embed-modeUtenteImpostare modalità di incorporazione
user:resend-verificationUtenteReinviare email di verifica
user:exportUtenteEsportazione dati
user:delete-accountUtenteEliminare account
admin:statsAdminStatistiche del pannello
admin:settings:getAdminLeggere impostazioni del sito
admin:settings:updateAdminAggiornare impostazioni del sito
admin:users:listAdminTutti gli utenti
admin:users:createAdminCreare utente
admin:users:toggleAdminAttivare/disattivare utente
admin:users:roleAdminCambiare ruolo utente
admin:users:deleteAdminEliminare utente
admin:users:regen-keyAdminRigenerare chiave API
admin:users:passwordAdminImpostare password
admin:users:folderAdminCambiare nome cartella
admin:files:listAdminTutti i file
admin:audit-log:listAdminLog di audit paginato

Eventi broadcast

EventoDestinatariPayload
file:uploadedUploader{ shortId, uploaderId }
file:deletedProprietario del file{ shortId, uploaderId }
file:viewTutti{ shortId, views }
user:createdAdmin{ id, username, role, ... }
user:deletedAdmin{ id }
user:updatedAdmin{ id, ...campi modificati }
audit:logAdminOggetto AuditLog completo
settings:updatedAdminOggetto SiteSettings aggiornato
stats:invalidateAdmin{} (attiva l'aggiornamento delle statistiche)

9. Route di servizio dei file

/f/:shortId — Visualizzatore file

- embedMode = 'embed': HTML OG con reindirizzamento

- embedMode = 'raw' + immagine/video/audio: HTTP 302 → /f/:shortId/raw

/f/:shortId/raw — Accesso diretto

Serve il file come risposta HTTP con supporto delle richieste di intervallo (206 Partial Content). Immagini/video/audio: Content-Disposition: inline, altri: attachment.

/f/:shortId/download — Download forzato

Come /raw, ma sempre Content-Disposition: attachment.

/f/:shortId/thumb — Miniatura

Restituisce la miniatura JPEG (per video e PDF). Cache-Control: public, max-age=86400.

/f/:shortId/delete/:token — Eliminazione ShareX

Elimina il file senza sessione tramite token di eliminazione univoco.

Verifica la password (tramite flag di sessione), la data di scadenza e il limite di download, poi serve il file.


10. Autenticazione e sicurezza

Autenticazione tramite sessione

Autenticazione tramite chiave API

Catena middleware (src/middleware/auth.js)

requireLogin    → verifica req.session.user → 401 se non connesso
requireAdmin    → come requireLogin, in aggiunta role === 'admin' → 403
requireApiKey   → verifica l'header Authorization o req.body.token

Protezione CSRF

requireSameOrigin() in app.js confronta l'header Origin con l'header Host per tutte le route API. Complementa i cookie sameSite: 'strict'.

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';

Header di sicurezza

Rate limiting

EndpointLimiteFinestra
Upload (/upload, /api/upload, /api/web-upload)60 richieste15 minuti
Auth (/api/auth/login, /register, ecc.)10 richieste15 minuti
Reset password5 richieste1 ora

Lista di blocco dei file

I seguenti tipi MIME ed estensioni vengono rifiutati all'upload:

Tipi MIME bloccati: application/x-executable, application/x-sh, application/x-csh, application/x-bat

Estensioni bloccate: .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

Protezione da path traversal

Tutti gli accessi ai file tramite resolveUploadPath() verificano che il percorso risolto si trovi all'interno di UPLOAD_DIR.


11. Sistema di upload

Upload standard (Multer)

Upload a blocchi (>250 MB)

Il client frontend passa automaticamente alla modalità a blocchi per i file di grandi dimensioni.

Struttura delle directory lato server:

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

Dimensione blocco: 10–20 MB (max 51 MB accettato per compatibilità con versioni precedenti)

Parallelismo: 3–5 upload di blocchi simultanei

Assemblaggio: Basato su stream (senza caricamento completo in RAM)

Upload avatar


12. Generazione delle miniature

Le miniature vengono generate in modo asincrono dopo l'upload (.catch(() => {}) — gli errori vengono ignorati silenziosamente).

Miniature video (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

Miniature PDF (Ghostscript)

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

Timeout: 30 secondi per generazione di miniatura

Fallback: Se ffmpeg/ghostscript non è disponibile, la generazione viene ignorata silenziosamente.

Script di backfill

npm run migrate:thumbnails
# o nel container:
docker exec -it <container> npm run migrate:thumbnails

13. Email e SMTP

Configurazione

SMTP viene attivato quando SMTP_HOST è impostato. mailer.isConfigured() verifica questo valore.

Template email

Tutte le email vengono inviate nella lingua dell'utente (8 lingue). I template sono incorporati in src/utils/mailer.js.

Tipi di email inviati:

TipoTriggerValidità token
Verifica emailRegistrazione, cambio email24 ore
Reset passwordPOST /api/auth/forgot-password1 ora

Sicurezza dei token


14. Internazionalizzazione

Libreria: i18next + react-i18next + i18next-browser-languagedetector

Lingue supportate:

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

Selezione della lingua:

1. Rilevamento della lingua del browser (automatico)

2. Preferenza dell'utente nel database (user.language)

3. Persistenza tramite PATCH /api/user/language

File di traduzione: client/src/i18n/locales/{code}.json


15. Frontend (React SPA)

Configurazione del router (client/src/App.jsx)

RouteComponenteAuth
/Reindirizzamento → /galleryNo
/auth/loginLogin.jsxNo
/auth/registerRegister.jsxNo
/auth/forgot-passwordForgotPassword.jsxNo
/auth/reset-passwordResetPassword.jsxNo
/installInstall.jsxNo
/uploadUpload.jsx
/galleryGallery.jsx
/f/:shortIdFileView.jsx
/collectionsCollections.jsx
/c/:idCollectionView.jsxNo (pubblico)
/s/:tokenShareView.jsxNo (pubblico)
/settingsSettings.jsx
/adminDashboard.jsxAdmin
/admin/usersUsers.jsxAdmin
/admin/filesFiles.jsxAdmin
/admin/audit-logAuditLog.jsxAdmin
/admin/site-settingsSiteSettings.jsxAdmin
/admin/importImport.jsxAdmin
/privacyPrivacyPolicy.jsxNo
/termsTermsOfService.jsxNo

Contesto di autenticazione (client/src/context/AuthContext.jsx)

Stato globale per l'utente connesso. Inizializzato all'avvio dell'app tramite GET /api/auth/me.

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

Gestisce la connessione WS persistente. Fornisce sendMessage() e la registrazione dei gestori eventi. Logica di riconnessione in caso di interruzione della connessione.

Componenti UI

Basato su shadcn/ui (Radix UI Primitives + Tailwind CSS):


16. Pannello di amministrazione

Il pannello di amministrazione (/admin/*) è accessibile solo agli utenti con role: 'admin'.

Dashboard (/admin)

Gestione utenti (/admin/users)

Gestione file (/admin/files)

Log di audit (/admin/audit-log)

Impostazioni del sito (/admin/site-settings)

Importazione XBackBone (/admin/import)


17. GDPR / Privacy

FunzionalitàArticolo GDPR
Informativa sulla privacy (configurabile)Art. 13/14 – Trasparenza
Pagina termini di servizio (configurabile)Art. 13/14 – Trasparenza
Esportazione dati (JSON con URL)Art. 20 – Portabilità dei dati
Autoeliminazione account (file + dati)Art. 17 – Diritto alla cancellazione
Log di audit (TTL 90 giorni via MongoDB)Art. 5(2) – Responsabilità
Esportazione CSV del log di auditArt. 5(2) – Responsabilità
Conservazione file configurabileArt. 5(1)(e) – Limitazione della conservazione
Chiavi API come hash SHA-256Art. 32 – Sicurezza
Password come bcrypt (12 round)Art. 32 – Sicurezza
Consenso cookie per Cloudflare AnalyticsArt. 13 – Trasparenza
Anonimizzazione alla cancellazione dell'accountArt. 17 – Diritto alla cancellazione

Flusso di eliminazione GDPR

Alla cancellazione dell'account (user:delete-account / DELETE /api/user/account):

1. Tutti i file dell'utente vengono eliminati dal disco

2. Le miniature vengono eliminate

3. L'avatar viene eliminato

4. Le voci del log di audit vengono anonimizzate (username: '[deleted]', ip: null, userId: null)

5. Il documento utente viene eliminato

6. La sessione viene distrutta


18. Job in background

Pulizia per conservazione (src/jobs/retentionCleanup.js)

Indici TTL MongoDB (automatici)

CollezioneTTLTrigger
AuditLog90 giornitimestamp
Collection7 giorni dopo expiresAtexpiresAt
ShareLink7 giorni dopo expiresAtexpiresAt

19. Deployment

Docker (consigliato)

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

Servizi:

Volumi:

Health check: L'app verifica HTTP 200 su /, MongoDB verifica db.adminCommand('ping').

Reverse proxy Nginx

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

    client_max_body_size 2100M;  # Almeno quanto il blocco più grande + buffer

    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;

        # Supporto WebSocket
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
BASE_URL in .env deve corrispondere al dominio pubblico.

Primo avvio

Il primo utente registrato riceve automaticamente il ruolo admin (User.countDocuments() === 0).


20. Ambiente di sviluppo

Prerequisiti

Configurazione

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

# Frontend
cd client
npm install

Avvio

# Backend (porta 3000, con nodemon)
npm run dev

# Frontend (porta 5173, terminale separato)
cd client
npm run dev

Il server di sviluppo Vite fa il proxy delle richieste API verso localhost:3000 automaticamente.

Build

npm run build   # Compila client/dist/, il backend rimane invariato
npm start       # Avvia il server Express di produzione

21. Migrazioni e script

Migrazioni automatiche (a ogni avvio)

Queste migrazioni vengono eseguite all'avvio dell'app in app.js e sono idempotenti:

MigrazioneFileFunzione
Migrazione cartelle utentesrc/migrations/migrateUserFolders.jsSposta i file da uploads/ a uploads/{folderName}/
Migrazione hash chiavi APIsrc/migrations/migrateApiKeyHashes.jsConverte le chiavi API in testo in chiaro in hash SHA-256

Script manuali

# Generare miniature per i file già esistenti
npm run migrate:thumbnails
# oppure:
node scripts/generate-missing-thumbnails.js

# Spostare gli upload nelle cartelle utente (manuale)
npm run migrate:user-folders
# oppure:
node scripts/migrate-uploads-to-user-folders.js

Inizializzazione del database (Docker)

scripts/mongo-init.js viene eseguito al primo avvio del container MongoDB e crea l'utente applicativo con i permessi corretti.


22. Test end-to-end

Framework: Playwright (@playwright/test)

File di test

FileSuite di test
e2e/upload.spec.jsFlussi di upload
e2e/gallery.spec.jsGalleria e gestione file
e2e/admin.spec.jsPannello di amministrazione
e2e/sharelink.spec.jsCreazione e utilizzo dei link di condivisione
e2e/tags.spec.jsGestione dei tag
e2e/bulk-actions-fixes.spec.jsAzioni in blocco

Esecuzione

# Tutti i test
npm run test:e2e

# Con interfaccia grafica
npm run test:e2e:ui

Configurazione Playwright: playwright.config.js

Setup globale: e2e/global-setup.js (crea utenti di test, admin, ecc.)

Helper: e2e/helpers.js (funzioni helper condivise)


Appendice: Azioni del log di audit

AzioneTrigger
loginAccesso riuscito
logoutDisconnessione
registerRegistrazione
uploadUpload file
delete_fileFile eliminato
delete_accountAccount eliminato
change_passwordPassword modificata
change_usernameNome utente modificato
change_emailEmail modificata
verify_emailEmail verificata
forgot_passwordReset password richiesto
reset_passwordPassword reimpostata
regen_api_keyChiave API rigenerata
sharex_configConfigurazione ShareX scaricata
export_dataEsportazione dati
admin_create_userAdmin: utente creato
admin_delete_userAdmin: utente eliminato
admin_toggle_userAdmin: utente attivato/disattivato
admin_change_roleAdmin: ruolo utente modificato
admin_change_passwordAdmin: password impostata
admin_regen_keyAdmin: chiave API rigenerata

*Documentazione generata dal codice sorgente di sharely v1.0.0*