Files

6.3 KiB

Ephemeral Downloader

Use a sua VPS como ponte de alta velocidade: ela baixa o arquivo do host lento, guarda por até 1 hora e entrega a você — tudo por uma SPA com progresso em tempo real.

Fastify React Node.js

Como funciona · Execução local · Produção · Limitações

Note

Estado do projeto: funcional, sem testes automatizados e sem CI (7 commits, o último em 2026-02-14). O fluxo atual baixa primeiro para o disco da VPS e só depois entrega o arquivo ao navegador — o modelo antigo de streaming direto (GET /stream, retomada por offset) foi aposentado e a rota responde 410.

Important

Não há autenticação, cota nem lista de destinos permitidos. Qualquer cliente que alcance a porta pode mandar o servidor buscar uma URL e ocupar disco. Coloque atrás de VPN, proxy com autenticação ou firewall.

Como funciona

Por que existe: às vezes o gargalo não é a sua internet nem o servidor de origem, e sim o caminho entre eles. Com uma VPS no meio, o arquivo vem por uma rota melhor até ela, e dela até você.

Etapa O que acontece
1. Sessão A SPA pede /api/session; o servidor cria uma sessão (UUID + pasta própria) e o navegador guarda o id em localStorage
2. Fila Você cola um link e o servidor começa o download na hora (queued → downloading → completed ou error)
3. Progresso A SPA consulta /api/tasks a cada 2 s: bytes recebidos/total, porcentagem e velocidade em MB/s e Mbps
4. Entrega Com a tarefa concluída, Save to Disk baixa o arquivo da VPS para a sua máquina
5. Limpeza Sem atividade por 1 hora, a sessão expira e a pasta com os arquivos é apagada; um cronômetro na tela mostra o tempo restante
sequenceDiagram
    participant N as Navegador (SPA React)
    participant V as Fastify (VPS)
    participant O as Servidor de origem
    N->>V: GET /api/session
    V-->>N: x-session-id + expiresAt
    N->>V: POST /api/queue { url }
    V->>O: GET url (undici)
    O-->>V: bytes
    V->>V: grava em storage/{sessão}/{id}_{arquivo}
    loop a cada 2 s
        N->>V: GET /api/tasks
        V-->>N: progresso, velocidade
    end
    N->>V: GET /api/download/{taskId}
    V-->>N: arquivo
    Note over V: 1 h sem atividade → sessão e arquivos apagados
API do servidor e detalhes técnicos
Rota Função
GET /health { "status": "ok" }
GET /api/session Cria ou recupera a sessão (cabeçalho x-session-id); devolve sessionId, expiresAt e remainingMs
POST /api/queue Corpo { "url": "..." }; 400 se a URL for inválida; devolve a tarefa
GET /api/tasks Tarefas da sessão
GET /api/download/:taskId 404 se não existe, 403 se é de outra sessão, 400 se não concluída; senão envia o arquivo e renova o prazo da sessão
GET /stream Descontinuada: 410
GET / e demais GET index.html do build do Vite (web/dist)
Item Implementação
Servidor Fastify 5 + @fastify/cors (origin: true) + @fastify/static, validação com zod
Cliente HTTP undici com agente global (até 500 conexões, keepAliveTimeout 10 s, connect 30 s) e User-Agent: Ephemeral-Downloader/2.0
Nome do arquivo Da URL; sobrescrito por Content-Disposition quando existir
Armazenamento server/storage/{sessão}/{id}_{nome}, relativo ao diretório de trabalho (process.cwd()); coletor a cada 1 min remove sessões expiradas e pastas órfãs com mais de 1 h
Frontend React 19, Vite 7, Tailwind CSS 4, lucide-react; componentes em web/src/components/{ui,feature}
Estado das tarefas Em memória (Map) — um restart do servidor perde a lista de tarefas

Limitações conhecidas

  • Sem retomada e sem lote de links. Não há Range/offset no fluxo atual: download interrompido vira error e precisa ser refeito. O formulário aceita uma URL por vez.
  • Redirecionamentos. O DownloadManager não define maxRedirections, e o undici.request não segue redirects por padrão; links que respondem 3xx (comum em hosts de mirror) podem não resultar no arquivo esperado.
  • Entrega via memória do navegador. O Save to Disk faz fetch do arquivo inteiro como blob antes de salvar; arquivos muito grandes pesam na memória da aba.
  • Sessão sem validação de formato. O x-session-id enviado pelo cliente é usado direto para montar o caminho da pasta da sessão.
  • Sem limite de tamanho, de downloads simultâneos ou de espaço em disco — todo link enfileirado começa imediatamente.
  • server/dist/index.js está versionado e é do modelo antigo (só /stream, sem /api/queue): rode pnpm build antes de pnpm start.

Execução local

Requisitos: Node.js ≥ 20 e pnpm ≥ 8.

pnpm install
pnpm dev          # servidor em :3000 (tsx) e Vite em :5173
pnpm build        # compila server (tsc) e web (tsc -b + vite build)
pnpm start        # Fastify serve a API e o build do front pela porta 3000

Warning

Defina VITE_API_URL. O SessionContext usa "" (mesma origem) quando a variável falta, e o vite.config.ts não tem proxy — em pnpm dev as chamadas a /api batem no Vite e falham. Já o botão Save to Disk (DownloadItem.tsx) cai em http://localhost:3000 quando falta. Crie web/.env com VITE_API_URL=http://localhost:3000 no dev e, em produção, defina a URL pública antes do pnpm build (o valor é embutido no bundle).

Produção

O nixpacks.toml descreve o build (pnpm install --frozen-lockfile e pnpm build) e o start (pnpm start). O servidor lê PORT e usa 3000 se a variável não existir; escuta em 0.0.0.0.

Avisos

  • A pasta .agent/ contém um kit de agentes, skills e workflows para assistentes de IA (Antigravity Kit, segundo o ARCHITECTURE.md) e não faz parte da aplicação.
  • Use apenas para baixar conteúdo que você tem direito de baixar.