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.
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 poroffset) foi aposentado e a rota responde410.
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/offsetno fluxo atual: download interrompido viraerrore precisa ser refeito. O formulário aceita uma URL por vez. - Redirecionamentos. O
DownloadManagernão definemaxRedirections, e oundici.requestnão segue redirects por padrão; links que respondem3xx(comum em hosts de mirror) podem não resultar no arquivo esperado. - Entrega via memória do navegador. O Save to Disk faz
fetchdo arquivo inteiro comoblobantes de salvar; arquivos muito grandes pesam na memória da aba. - Sessão sem validação de formato. O
x-session-idenviado 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.jsestá versionado e é do modelo antigo (só/stream, sem/api/queue): rodepnpm buildantes depnpm 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. OSessionContextusa""(mesma origem) quando a variável falta, e ovite.config.tsnão tem proxy — empnpm devas chamadas a/apibatem no Vite e falham. Já o botão Save to Disk (DownloadItem.tsx) cai emhttp://localhost:3000quando falta. Crieweb/.envcomVITE_API_URL=http://localhost:3000no dev e, em produção, defina a URL pública antes dopnpm 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 oARCHITECTURE.md) e não faz parte da aplicação. - Use apenas para baixar conteúdo que você tem direito de baixar.