Sharely — Documentação Técnica

Versão: 1.0.0 · Licença: MIT · Node.js: ≥ 18

Índice

1. Visão geral do projeto

2. Arquitetura

3. Stack tecnológico

4. Estrutura de diretórios

5. Configuração e variáveis de ambiente

6. Modelos de dados

7. API REST

8. API WebSocket

9. Rotas de serviço de arquivos

10. Autenticação e segurança

11. Sistema de upload

12. Geração de miniaturas

13. E-mail e SMTP

14. Internacionalização

15. Frontend (React SPA)

16. Painel de administração

17. LGPD / Privacidade

18. Jobs em segundo plano

19. Implantação

20. Ambiente de desenvolvimento

21. Migrações e scripts

22. Testes end-to-end


1. Visão geral do projeto

Sharely é uma plataforma de compartilhamento de arquivos auto-hospedada com uma interface web limpa, integração com ShareX e acesso via API. Os usuários fazem upload de capturas de tela, arquivos e mídias e os compartilham instantaneamente por meio de links curtos.

Funcionalidades principais

FuncionalidadeDescrição
Upload webArrastar e soltar, até 500 arquivos simultaneamente
Upload em partesArquivos de até 2 GB via upload multi-part paralelo
Integração ShareXArquivo de configuração .sxcu disponível para download com um clique
Upload via APIAutenticação por token Bearer, compatível com curl/wget
Visualizador de arquivosZoom em imagens, streaming de vídeo/áudio (HTTP Range), PDFs inline, código com destaque de sintaxe
Modos de incorporação*embed* (HTML OG/Twitter Card) ou *raw* (redirecionamento direto)
MiniaturasPré-visualizações JPEG automáticas para vídeos (ffmpeg) e PDFs (ghostscript)
ColeçõesAgrupamentos de arquivos com senha e data de expiração opcionais
Links de compartilhamentoLinks por arquivo com senha, expiração e limite de downloads
Interface em tempo realAtualizações ao vivo via WebSocket (upload, exclusão, contador de visualizações, estatísticas admin)
Multilíngue8 idiomas: EN, DE, FR, ES, IT, PT, JA, ZH
Painel adminEstatísticas, gerenciamento de usuários, gerenciamento de arquivos, log de auditoria (exportação CSV)
Conformidade LGPD/GDPRFuncionalidades de privacidade em conformidade com o GDPR da UE (Art. 17, 20, 32, etc.)
Importação XBackBoneMigração de instalações XBackBone existentes
Pronto para Dockerdocker compose up -d inicia o ambiente completo

2. Arquitetura

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

Fluxo de dados: Upload padrão

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

Fluxo de dados: 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 tecnológico

CamadaTecnologiaVersão
RuntimeNode.js≥ 18
Framework backendExpress.js4.x
Banco de dadosMongoDB + MongooseMongo 7, Mongoose 8.x
Sessõesexpress-session + connect-mongo
Tempo realWebSocket (ws)8.x
Upload de arquivosMulter1.x
E-mailNodemailer8.x
Hash de senhasbcryptjs2.x (12 rounds)
Hash de chaves APISHA-256 (Node Crypto)
Limitação de taxaexpress-rate-limit8.x
Importação XBackBonesql.js1.x
Framework frontendReact 1818.3.x
Roteamento (frontend)React Router v66.x
Ferramenta de buildVite6.x
EstilizaçãoTailwind CSS + Radix UI3.x
Componentes UIshadcn/ui (Radix primitives)
ÍconesFontAwesome6.x
i18ni18next + react-i18next
Destaque de sintaxehighlight.js11.x
ContêineresDocker + Docker Compose
TestesPlaywright (E2E)1.60.x

4. Estrutura de diretórios

sharely/
├── app.js                          # Ponto de entrada Express, sequência de inicialização
├── package.json
├── .env.example                    # Modelo para todas as variáveis de ambiente
├── Dockerfile
├── docker-compose.yml
│
├── src/
│   ├── config/
│   │   └── db.js                   # Conexão MongoDB (Mongoose)
│   ├── middleware/
│   │   ├── auth.js                 # requireLogin / requireAdmin / requireApiKey
│   │   └── upload.js               # Configuração Multer, lista de bloqueio
│   ├── models/
│   │   ├── AuditLog.js             # Eventos de auditoria (TTL 90 dias)
│   │   ├── Collection.js           # Coleções de arquivos
│   │   ├── File.js                 # Metadados de arquivos
│   │   ├── ShareLink.js            # Links de compartilhamento por arquivo
│   │   ├── SiteSettings.js         # Singleton: configurações do operador
│   │   └── User.js                 # Contas de usuário + chaves API
│   ├── routes/
│   │   ├── api.js                  # API principal (upload, galeria, admin, ...)
│   │   ├── auth.js                 # Login / Registro / Redefinição de senha
│   │   ├── files.js                # Serviço de arquivos, embeds OG, requisições de intervalo
│   │   ├── import.js               # Migração XBackBone
│   │   ├── install.js              # Endpoint de instalação inicial
│   │   └── shares.js               # Serviço de arquivos via link de compartilhamento
│   ├── jobs/
│   │   └── retentionCleanup.js     # Exclusão diária de arquivos expirados
│   ├── migrations/
│   │   ├── migrateApiKeyHashes.js  # Única vez: textos simples → hashes SHA-256
│   │   └── migrateUserFolders.js   # Única vez: mover arquivos para pastas de usuário
│   ├── utils/
│   │   ├── audit.js                # Função auxiliar logAudit()
│   │   ├── generateThumbnail.js    # Integração ffmpeg / ghostscript
│   │   ├── mailer.js               # Wrapper Nodemailer + templates de e-mail i18n
│   │   └── sanitizeFilename.js     # Sanitização do nome de arquivo
│   └── ws.js                       # Servidor WebSocket + despachador de ações
│
├── client/                         # Frontend React
│   ├── index.html
│   ├── package.json
│   ├── vite.config.js
│   ├── tailwind.config.js
│   └── src/
│       ├── main.jsx                # Ponto de entrada React
│       ├── App.jsx                 # Configuração do roteador
│       ├── index.css               # Estilos globais
│       ├── context/
│       │   └── AuthContext.jsx     # Estado de autenticação global
│       ├── hooks/
│       │   ├── use-toast.js        # Hook de notificações toast
│       │   └── useWebSocket.js     # Conexão WS + manipuladores de eventos
│       ├── components/
│       │   ├── Layout.jsx          # Shell do app (barra de navegação, sidebar)
│       │   ├── ProtectedRoute.jsx  # Guarda de autenticação
│       │   ├── ShareLinkDialog.jsx # Diálogo de criação de link de compartilhamento
│       │   ├── AddToCollectionDialog.jsx
│       │   ├── CookieBanner.jsx
│       │   ├── LanguageSelector.jsx
│       │   ├── RequireEmailDialog.jsx
│       │   ├── UserAvatar.jsx
│       │   └── ui/                 # Componentes base shadcn/ui
│       ├── pages/
│       │   ├── Upload.jsx          # Página de upload
│       │   ├── Gallery.jsx         # Galeria de arquivos
│       │   ├── FileView.jsx        # Visualização detalhada de arquivo
│       │   ├── Collections.jsx     # Visão geral de coleções
│       │   ├── CollectionView.jsx  # Coleção individual
│       │   ├── ShareView.jsx       # Página pública do link de compartilhamento
│       │   ├── Settings.jsx        # Configurações do usuário
│       │   ├── Login.jsx
│       │   ├── Register.jsx
│       │   ├── ForgotPassword.jsx
│       │   ├── ResetPassword.jsx
│       │   ├── Install.jsx         # Instalação inicial
│       │   ├── PrivacyPolicy.jsx
│       │   ├── TermsOfService.jsx
│       │   └── admin/
│       │       ├── Dashboard.jsx   # Página inicial do admin
│       │       ├── Users.jsx       # Gerenciamento de usuários
│       │       ├── Files.jsx       # Gerenciamento de arquivos
│       │       ├── AuditLog.jsx    # Visualização do log de auditoria
│       │       ├── SiteSettings.jsx# Configurações do operador
│       │       └── Import.jsx      # Importação XBackBone
│       ├── i18n/
│       │   ├── index.js            # Configuração i18next
│       │   └── locales/
│       │       ├── de.json
│       │       ├── en.json
│       │       ├── es.json
│       │       ├── fr.json
│       │       ├── it.json
│       │       ├── ja.json
│       │       ├── pt.json
│       │       └── zh.json
│       └── lib/
│           └── utils.js            # Utilitário Tailwind (cn())
│
├── scripts/
│   ├── setup-db.js
│   ├── mongo-init.js               # Script de inicialização MongoDB
│   ├── migrate-uploads-to-user-folders.js
│   └── generate-missing-thumbnails.js
│
├── e2e/                            # Testes 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/                        # Arquivos enviados (runtime)
│   ├── .thumbnails/
│   ├── .avatars/
│   └── .chunks/                    # Partes temporárias
│
└── docs/assets/                    # Capturas de tela e logotipo

5. Configuração e variáveis de ambiente

Todas as variáveis são carregadas de .env (via dotenv). O arquivo .env.example contém o modelo completo.

Campos obrigatórios

VariávelDescrição
SESSION_SECRETSegredo para criptografia de sessões — string aleatória longa, ex. openssl rand -hex 32
MONGO_ROOT_PASSWORDSenha root do MongoDB (necessária apenas para Docker Compose)
MONGO_APP_PASSWORDSenha do usuário de aplicação do MongoDB

Todas as variáveis de ambiente

VariávelPadrãoDescrição
PORT3000Porta TCP do servidor HTTP
MONGODB_URI_(construída pelo Docker Compose)_URI de conexão MongoDB completa
MONGO_ROOT_PASSWORDSenha root do MongoDB
MONGO_APP_USERappuserNome de usuário de aplicação MongoDB
MONGO_APP_PASSWORDSenha do usuário de aplicação MongoDB
MONGO_DB_NAMEsharelyNome do banco de dados MongoDB
SESSION_SECRETObrigatório — segredo de criptografia de sessões
BASE_URLhttp://localhost:3000URL base pública para os links de compartilhamento gerados (sem / final)
SITE_NAMEsharelyNome do site nos embeds Open Graph
MAX_FILE_SIZE_MB100Tamanho máximo de arquivo para uploads padrão em MB (uploads em partes até 2 GB independentemente)
ALLOW_REGISTRATIONtruefalse desativa o registro público
SMTP_HOSTHostname do servidor SMTP; deixar vazio para desativar as funcionalidades de e-mail
SMTP_PORT587Porta SMTP
SMTP_SECUREfalsetrue para TLS implícito (porta 465), false para STARTTLS
SMTP_USERNome de usuário SMTP
SMTP_PASSSenha SMTP
SMTP_FROM_(SMTP_USER)_Endereço do remetente nos e-mails enviados
UPLOAD_DIR./uploadsCaminho absoluto para o diretório de upload
NODE_ENVproduction ativa os cookies seguros

6. Modelos de dados

User (src/models/User.js)

{
  username:                    String (3–32, único, alfanumérico + _-)
  password:                    String (bcrypt, 12 rounds)
  role:                        'admin' | 'user'
  apiKey:                      String (legado, vazio após migração)
  apiKeyHash:                  String (SHA-256, único, sparse)
  apiKeyPrefix:                String (primeiros 8 caracteres do texto simples)
  folderName:                  String (único, sparse, máx. 64)
  avatarExt:                   String | null (.jpg/.png/.gif/.webp)
  embedMode:                   'embed' | 'raw'
  isActive:                    Boolean
  email:                       String (minúsculas, único, sparse)
  emailVerified:               Boolean
  emailVerificationToken:      String | null (hash SHA-256 do texto simples)
  emailVerificationExpires:    Date | null
  passwordResetToken:          String | null (hash SHA-256)
  passwordResetExpires:        Date | null
  language:                    'en'|'de'|'fr'|'es'|'it'|'pt'|'ja'|'zh'
  predefinedTags:              [String] (máx. 100 tags × 50 caracteres)
  createdAt:                   Date
}

Métodos importantes:

File (src/models/File.js)

{
  shortId:      String (8 caracteres hexadecimais: 6 timestamp + 2 aleatórios, único)
  deleteToken:  String (32 caracteres hexadecimais, único)
  originalName: String (sanitizado)
  storedName:   String (caminho relativo: "folderName/8hex.ext")
  mimeType:     String
  size:         Number (bytes)
  uploader:     ObjectId → User
  views:        Number
  tags:         [String] (máx. 20 × 50 caracteres)
  createdAt:    Date
}

Propriedades virtuais:

Algoritmo Short ID:

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

Collection (src/models/Collection.js)

{
  shortId:     String (8 Hex, único)
  name:        String (máx. 100)
  description: String (máx. 500)
  owner:       ObjectId → User
  files:       [ObjectId → File]
  password:    String | null (bcrypt)
  expiresAt:   Date | null
  createdAt:   Date
}
As coleções expiradas são excluídas automaticamente pelo índice TTL do MongoDB 7 dias após a expiração.
{
  token:         String (32 Hex, único)
  file:          ObjectId → File
  createdBy:     ObjectId → User
  label:         String (máx. 100)
  password:      String | null (bcrypt)
  expiresAt:     Date | null
  downloadLimit: Number (-1 = ilimitado)
  downloadCount: Number
  createdAt:     Date
}
Como as coleções: período de carência de 7 dias após expiração via índice TTL.

SiteSettings (src/models/SiteSettings.js)

Documento singleton (_id: 'singleton'):

{
  operatorName:        String
  operatorAddress:     String
  operatorEmail:       String
  cloudflareAnalytics: Boolean
  fileRetentionDays:   Number (0 = desativado)
  encryptionAtRest:    Boolean
  sessionDurationDays: Number (padrão: 7)
  allowRegistration:   Boolean
}

AuditLog (src/models/AuditLog.js)

{
  timestamp: Date (índice TTL: 90 dias)
  userId:    ObjectId → User | null
  username:  String | null
  action:    String
  ip:        String | null
  meta:      Mixed (metadados específicos da ação)
}

7. API REST

URL base: /api

Todos os endpoints JSON retornam Content-Type: application/json. Erros: { "error": "mensagem" }.


Autenticação (/api/auth)

MétodoCaminhoAuthDescrição
GET/meSessãoUsuário atualmente conectado
POST/loginFazer login (limitado: 10/15min)
POST/registerRegistrar-se (limitado, primeiro usuário torna-se admin)
POST/logoutFazer logout
GET/smtp-enabledVerificar se SMTP está configurado
GET/verify-email?token=Verificar endereço de e-mail
GET/verify-reset-token?token=Validar token de redefinição
POST/forgot-passwordEnviar e-mail de redefinição de senha (limitado: 5/h)
POST/reset-passwordDefinir nova senha

Requisição de login:

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

Resposta de login:

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

Upload de arquivos

MétodoCaminhoAuthDescrição
POST/uploadChave APIUpload ShareX (endpoint legado em app.js)
POST/api/uploadChave APIUpload ShareX/API (campo: file)
POST/api/web-uploadSessãoUpload web (campo: files[], máx. 500)
POST/api/chunk/initSessãoInicializar upload em partes
POST/api/chunk/:uploadIdSessãoEnviar uma parte (campo: chunk)
POST/api/chunk/:uploadId/completeSessãoMontar as partes
DELETE/api/chunk/:uploadIdSessãoCancelar upload e limpar

Resposta de upload via 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
}

Fluxo de upload em partes

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

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

Em paralelo com 3–5 partes simultâneas

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


Gerenciamento de arquivos

MétodoCaminhoAuthDescrição
GET/api/gallerySessãoArquivos próprios (admin: todos), paginado (24/página), filtros: q, type, tag, page
GET/api/file/:shortIdMetadados do arquivo (incrementa o contador de visualizações)
PATCH/api/file/:shortIdSessãoAtualizar tags/nome
DELETE/api/file/:shortIdSessãoExcluir arquivo
DELETE/api/delete/:shortIdChave APIExcluir arquivo (autenticação por chave API)
POST/api/files/bulkSessãoAções em lote: delete, tag, removeTag, addToCollection, moveToCollection
GET/api/tagsSessãoTodas as sugestões de tags do usuário

Parâmetros de consulta da galeria:


Configurações do usuário

MétodoCaminhoAuthDescrição
GET/api/my-keySessãoExibir prefixo da chave API
POST/api/regen-keySessãoRegenerar chave API
GET/api/sharex-configSessãoBaixar ShareX .sxcu (regenera a chave)
PATCH/api/user/usernameSessãoAlterar nome de usuário (senha obrigatória)
PATCH/api/user/passwordSessãoAlterar senha
PATCH/api/user/emailSessãoAlterar e-mail (envia e-mail de verificação)
PATCH/api/user/languageSessãoDefinir idioma da interface
PATCH/api/user/embed-modeSessãoDefinir modo de incorporação (embed/raw)
POST/api/user/resend-verificationSessãoReenviar e-mail de verificação
GET/api/user/exportSessãoExportação de dados (LGPD/GDPR Art. 20) como JSON
DELETE/api/user/accountSessãoExcluir conta (LGPD/GDPR Art. 17, senha obrigatória)
GET/api/user/predefined-tagsSessãoRecuperar tags predefinidas
PATCH/api/user/predefined-tagsSessãoAtualizar tags predefinidas
POST/api/user/avatarSessãoFazer upload de avatar (máx. 2 MB, JPEG/PNG/GIF/WebP)
DELETE/api/user/avatarSessãoExcluir avatar
GET/api/user/avatar/:userIdServir avatar

MétodoCaminhoAuthDescrição
GET/api/file/:shortId/share-linksSessão (Proprietário/Admin)Todos os links de compartilhamento de um arquivo
POST/api/file/:shortId/share-linksSessão (Proprietário/Admin)Criar link de compartilhamento
DELETE/api/share-links/:tokenSessão (Proprietário/Criador/Admin)Excluir link de compartilhamento
GET/api/share-links/:tokenMetadados do link de compartilhamento (público)
POST/api/share-links/:token/verifyVerificar senha do link de compartilhamento

Criar link de compartilhamento:

POST /api/file/a1b2c3d4/share-links
{
  "label": "Para colegas",
  "password": "secreto",
  "expiresAt": "2025-12-31T23:59:59Z",
  "downloadLimit": 10
}

Coleções

MétodoCaminhoAuthDescrição
GET/api/collectionsSessãoColeções próprias (admin: todas)
POST/api/collectionsSessãoCriar coleção
GET/api/collections/:idVisualizar coleção (pública, senha se definida)
PATCH/api/collections/:idSessão (Proprietário/Admin)Atualizar coleção
DELETE/api/collections/:idSessão (Proprietário/Admin)Excluir coleção
POST/api/collections/:id/filesSessão (Proprietário/Admin)Adicionar arquivo à coleção
DELETE/api/collections/:id/files/:fileShortIdSessão (Proprietário/Admin)Remover arquivo da coleção
POST/api/collections/:id/verifyVerificar senha da coleção

Endpoints de administração

MétodoCaminhoAuthDescrição
GET/api/admin/statsAdminEstatísticas do painel
GET/api/admin/usersAdminTodos os usuários (com contagem de arquivos)
POST/api/admin/usersAdminCriar usuário
PATCH/api/admin/users/:id/toggleAdminAtivar/desativar usuário
PATCH/api/admin/users/:id/roleAdminAlterar papel do usuário
DELETE/api/admin/users/:idAdminExcluir usuário
POST/api/admin/users/:id/regen-keyAdminRegenerar chave API
PATCH/api/admin/users/:id/passwordAdminDefinir senha
PATCH/api/admin/users/:id/folderAdminAlterar nome da pasta (move arquivos)
GET/api/admin/filesAdminTodos os arquivos, paginado (30/página)
GET/api/admin/site-settingsAdminLer configurações do operador
PATCH/api/admin/site-settingsAdminAtualizar configurações do operador
GET/api/admin/audit-logAdminLog de auditoria paginado (50/página)
GET/api/admin/audit-log/exportAdminBaixar log de auditoria como CSV
GET/api/site-settingsInformações públicas do operador (para a página de privacidade)

Configurações do site (públicas)

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

Conexão: wss://example.com/ws (somente para usuários conectados, cookie de sessão obrigatório)

Protocolo

Cliente → Servidor (Requisição):

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

Servidor → Cliente (Resposta):

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

Servidor → Cliente (Erro):

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

Servidor → Cliente (Broadcast):

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

Ações disponíveis

AçãoAuthDescrição
site-settings:getConfigurações públicas do site
auth:meUsuárioDados do usuário conectado
file:getUsuárioDetalhes do arquivo (incrementa visualizações)
file:listUsuárioLista de arquivos com filtro/paginação
file:deleteUsuárioExcluir arquivo
user:get-keyUsuárioPrefixo da chave API
user:regen-keyUsuárioRegenerar chave API
user:change-passwordUsuárioAlterar senha
user:change-usernameUsuárioAlterar nome de usuário
user:change-emailUsuárioAlterar e-mail
user:change-languageUsuárioDefinir idioma
user:change-embed-modeUsuárioDefinir modo de incorporação
user:resend-verificationUsuárioReenviar e-mail de verificação
user:exportUsuárioExportação de dados
user:delete-accountUsuárioExcluir conta
admin:statsAdminEstatísticas do painel
admin:settings:getAdminLer configurações do site
admin:settings:updateAdminAtualizar configurações do site
admin:users:listAdminTodos os usuários
admin:users:createAdminCriar usuário
admin:users:toggleAdminAtivar/desativar usuário
admin:users:roleAdminAlterar papel do usuário
admin:users:deleteAdminExcluir usuário
admin:users:regen-keyAdminRegenerar chave API
admin:users:passwordAdminDefinir senha
admin:users:folderAdminAlterar nome da pasta
admin:files:listAdminTodos os arquivos
admin:audit-log:listAdminLog de auditoria paginado

Eventos de broadcast

EventoDestinatáriosPayload
file:uploadedQuem fez o upload{ shortId, uploaderId }
file:deletedProprietário do arquivo{ shortId, uploaderId }
file:viewTodos{ shortId, views }
user:createdAdmins{ id, username, role, ... }
user:deletedAdmins{ id }
user:updatedAdmins{ id, ...campos alterados }
audit:logAdminsObjeto AuditLog completo
settings:updatedAdminsObjeto SiteSettings atualizado
stats:invalidateAdmins{} (aciona atualização de estatísticas)

9. Rotas de serviço de arquivos

/f/:shortId — Visualizador de arquivos

- embedMode = 'embed': HTML OG com redirecionamento

- embedMode = 'raw' + imagem/vídeo/áudio: HTTP 302 → /f/:shortId/raw

/f/:shortId/raw — Acesso direto

Serve o arquivo como resposta HTTP com suporte a requisições de intervalo (206 Partial Content). Imagens/vídeos/áudio: Content-Disposition: inline, outros: attachment.

/f/:shortId/download — Download forçado

Como /raw, mas sempre Content-Disposition: attachment.

/f/:shortId/thumb — Miniatura

Retorna a miniatura JPEG (para vídeos e PDFs). Cache-Control: public, max-age=86400.

/f/:shortId/delete/:token — Exclusão ShareX

Exclui o arquivo sem sessão usando um token de exclusão único.

Verifica a senha (via flag de sessão), a data de expiração e o limite de downloads, e então serve o arquivo.


10. Autenticação e segurança

Autenticação por sessão

Autenticação por chave API

Cadeia de middleware (src/middleware/auth.js)

requireLogin    → verifica req.session.user → 401 se não conectado
requireAdmin    → como requireLogin, adicionalmente role === 'admin' → 403
requireApiKey   → verifica o cabeçalho Authorization ou req.body.token

Proteção CSRF

requireSameOrigin() em app.js compara o cabeçalho Origin com o cabeçalho Host para todas as rotas da API. Complementa os cookies sameSite: 'strict'.

Política de Segurança de Conteúdo

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

Cabeçalhos de segurança

Limitação de taxa

EndpointLimiteJanela
Upload (/upload, /api/upload, /api/web-upload)60 requisições15 minutos
Auth (/api/auth/login, /register, etc.)10 requisições15 minutos
Redefinição de senha5 requisições1 hora

Lista de bloqueio de arquivos

Os seguintes tipos MIME e extensões são rejeitados no upload:

Tipos MIME bloqueados: application/x-executable, application/x-sh, application/x-csh, application/x-bat

Extensões bloqueadas: .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

Proteção contra path traversal

Todos os acessos a arquivos via resolveUploadPath() verificam se o caminho resolvido está dentro de UPLOAD_DIR.


11. Sistema de upload

Upload padrão (Multer)

Upload em partes (>250 MB)

O cliente frontend muda automaticamente para o modo de partes para arquivos grandes.

Estrutura de diretórios no servidor:

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

Tamanho da parte: 10–20 MB (máx. 51 MB aceito para compatibilidade com versões anteriores)

Paralelismo: 3–5 uploads de partes simultâneos

Montagem: Baseada em streams (sem carregamento completo na RAM)

Upload de avatar


12. Geração de miniaturas

As miniaturas são geradas de forma assíncrona após o upload (.catch(() => {}) — erros são ignorados silenciosamente).

Miniaturas de vídeo (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

Miniaturas de PDF (Ghostscript)

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

Tempo limite: 30 segundos por geração de miniatura

Fallback: Se ffmpeg/ghostscript não estiver disponível, a geração é ignorada silenciosamente.

Script de preenchimento retroativo

npm run migrate:thumbnails
# ou no contêiner:
docker exec -it <container> npm run migrate:thumbnails

13. E-mail e SMTP

Configuração

O SMTP é ativado quando SMTP_HOST está definido. mailer.isConfigured() verifica esse valor.

Templates de e-mail

Todos os e-mails são enviados no idioma do usuário (8 idiomas). Os templates estão incorporados em src/utils/mailer.js.

Tipos de e-mails enviados:

TipoGatilhoValidade do token
Verificação de e-mailRegistro, alteração de e-mail24 horas
Redefinição de senhaPOST /api/auth/forgot-password1 hora

Segurança dos tokens


14. Internacionalização

Biblioteca: i18next + react-i18next + i18next-browser-languagedetector

Idiomas suportados:

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

Seleção de idioma:

1. Detecção do idioma do navegador (automática)

2. Preferência do usuário no banco de dados (user.language)

3. Persistência via PATCH /api/user/language

Arquivos de tradução: client/src/i18n/locales/{code}.json


15. Frontend (React SPA)

Configuração do roteador (client/src/App.jsx)

RotaComponenteAuth
/Redirecionamento → /galleryNão
/auth/loginLogin.jsxNão
/auth/registerRegister.jsxNão
/auth/forgot-passwordForgotPassword.jsxNão
/auth/reset-passwordResetPassword.jsxNão
/installInstall.jsxNão
/uploadUpload.jsxSim
/galleryGallery.jsxSim
/f/:shortIdFileView.jsxSim
/collectionsCollections.jsxSim
/c/:idCollectionView.jsxNão (público)
/s/:tokenShareView.jsxNão (público)
/settingsSettings.jsxSim
/adminDashboard.jsxAdmin
/admin/usersUsers.jsxAdmin
/admin/filesFiles.jsxAdmin
/admin/audit-logAuditLog.jsxAdmin
/admin/site-settingsSiteSettings.jsxAdmin
/admin/importImport.jsxAdmin
/privacyPrivacyPolicy.jsxNão
/termsTermsOfService.jsxNão

Contexto de autenticação (client/src/context/AuthContext.jsx)

Estado global para o usuário conectado. Inicializado na inicialização do app via GET /api/auth/me.

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

Gerencia a conexão WS persistente. Fornece sendMessage() e registro de manipuladores de eventos. Lógica de reconexão em caso de queda da conexão.

Componentes UI

Baseado em shadcn/ui (Radix UI Primitives + Tailwind CSS):


16. Painel de administração

O painel de administração (/admin/*) é acessível apenas para usuários com role: 'admin'.

Painel de controle (/admin)

Gerenciamento de usuários (/admin/users)

Gerenciamento de arquivos (/admin/files)

Log de auditoria (/admin/audit-log)

Configurações do site (/admin/site-settings)

Importação XBackBone (/admin/import)


17. LGPD / Privacidade

FuncionalidadeArtigo GDPR/LGPD
Política de privacidade (configurável)Art. 13/14 – Transparência
Página de termos de serviço (configurável)Art. 13/14 – Transparência
Exportação de dados (JSON com URLs)Art. 20 – Portabilidade de dados
Autoexclusão de conta (arquivos + dados)Art. 17 – Direito ao apagamento
Log de auditoria (TTL 90 dias via MongoDB)Art. 5(2) – Responsabilidade
Exportação CSV do log de auditoriaArt. 5(2) – Responsabilidade
Retenção de arquivos configurávelArt. 5(1)(e) – Limitação da conservação
Chaves API como hash SHA-256Art. 32 – Segurança
Senhas como bcrypt (12 rounds)Art. 32 – Segurança
Consentimento de cookies para Cloudflare AnalyticsArt. 13 – Transparência
Anonimização na exclusão de contaArt. 17 – Direito ao apagamento

Fluxo de exclusão LGPD/GDPR

Na exclusão da conta (user:delete-account / DELETE /api/user/account):

1. Todos os arquivos do usuário são excluídos do disco

2. As miniaturas são excluídas

3. O avatar é excluído

4. As entradas do log de auditoria são anonimizadas (username: '[deleted]', ip: null, userId: null)

5. O documento de usuário é excluído

6. A sessão é destruída


18. Jobs em segundo plano

Limpeza por retenção (src/jobs/retentionCleanup.js)

Índices TTL do MongoDB (automáticos)

ColeçãoTTLGatilho
AuditLog90 diastimestamp
Collection7 dias após expiresAtexpiresAt
ShareLink7 dias após expiresAtexpiresAt

19. Implantação

Docker (recomendado)

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

Serviços:

Volumes:

Verificações de saúde: O app verifica HTTP 200 em /, o MongoDB verifica db.adminCommand('ping').

Proxy reverso Nginx

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

    client_max_body_size 2100M;  # Pelo menos tão grande quanto a maior parte + 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;

        # Suporte WebSocket
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
BASE_URL em .env deve corresponder ao domínio público.

Primeira inicialização

O primeiro usuário registrado recebe automaticamente o papel admin (User.countDocuments() === 0).


20. Ambiente de desenvolvimento

Pré-requisitos

Configuração

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

# Frontend
cd client
npm install

Inicialização

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

# Frontend (porta 5173, terminal separado)
cd client
npm run dev

O servidor de desenvolvimento Vite faz proxy das requisições da API para localhost:3000 automaticamente.

Build

npm run build   # Compila client/dist/, o backend permanece inalterado
npm start       # Inicia o servidor Express de produção

21. Migrações e scripts

Migrações automáticas (a cada inicialização)

Essas migrações são executadas na inicialização do app em app.js e são idempotentes:

MigraçãoArquivoFunção
Migração de pastas de usuáriosrc/migrations/migrateUserFolders.jsMove arquivos de uploads/ para uploads/{folderName}/
Migração de hashes de chaves APIsrc/migrations/migrateApiKeyHashes.jsConverte chaves API em texto simples para hashes SHA-256

Scripts manuais

# Gerar miniaturas para arquivos já existentes
npm run migrate:thumbnails
# ou:
node scripts/generate-missing-thumbnails.js

# Mover uploads para pastas de usuário (manual)
npm run migrate:user-folders
# ou:
node scripts/migrate-uploads-to-user-folders.js

Inicialização do banco de dados (Docker)

scripts/mongo-init.js é executado na primeira inicialização do contêiner MongoDB e cria o usuário de aplicação com as permissões corretas.


22. Testes end-to-end

Framework: Playwright (@playwright/test)

Arquivos de teste

ArquivoSuite de testes
e2e/upload.spec.jsFluxos de upload
e2e/gallery.spec.jsGaleria e gerenciamento de arquivos
e2e/admin.spec.jsPainel de administração
e2e/sharelink.spec.jsCriação e uso de links de compartilhamento
e2e/tags.spec.jsGerenciamento de tags
e2e/bulk-actions-fixes.spec.jsAções em lote

Execução

# Todos os testes
npm run test:e2e

# Com interface gráfica
npm run test:e2e:ui

Configuração Playwright: playwright.config.js

Configuração global: e2e/global-setup.js (cria usuários de teste, admin, etc.)

Utilitários: e2e/helpers.js (funções auxiliares compartilhadas)


Apêndice: Ações do log de auditoria

AçãoGatilho
loginLogin bem-sucedido
logoutLogout
registerRegistro
uploadUpload de arquivo
delete_fileArquivo excluído
delete_accountConta excluída
change_passwordSenha alterada
change_usernameNome de usuário alterado
change_emailE-mail alterado
verify_emailE-mail verificado
forgot_passwordRedefinição de senha solicitada
reset_passwordSenha redefinida
regen_api_keyChave API regenerada
sharex_configConfiguração ShareX baixada
export_dataExportação de dados
admin_create_userAdmin: usuário criado
admin_delete_userAdmin: usuário excluído
admin_toggle_userAdmin: usuário ativado/desativado
admin_change_roleAdmin: papel do usuário alterado
admin_change_passwordAdmin: senha definida
admin_regen_keyAdmin: chave API regenerada

*Documentação gerada a partir do código-fonte de sharely v1.0.0*