O README descrevia um proxy de streaming com `GET /stream?url=&offset=` que não existe mais (a rota responde 410 desde a reescrita do backend), falava em "retomar downloads" e não mencionava sessão, storage temporário, polling de tasks nem o endpoint de entrega do arquivo. O novo texto segue o padrão da auditoria: one-liner, badges reais (sem licença e sem CI, que não existem no repo), Sobre, Como funciona com diagrama, Stack, Requisitos, Início rápido testado, Uso, API, Produção, Estrutura do projeto, Verificação, Estado atual e limitações, Documentação e Licença. O que está escrito corresponde ao que foi executado nesta auditoria: install, build, lint, 13 checagens de API, download de 10 MB com sha256 idêntico ao da origem e a interface rodando em Chrome headless.
151 lines
8.3 KiB
Markdown
151 lines
8.3 KiB
Markdown
# Ephemeral Downloader
|
|
|
|
**Proxy de download efêmero: a sua VPS baixa o arquivo por uma rota melhor e entrega para o seu navegador, com os arquivos apagados automaticamente depois.**
|
|
|
|

|
|

|
|

|
|

|
|

|
|

|
|
|
|
## Sobre
|
|
|
|
Baixar um arquivo grande direto de um host congestionado costuma ser lento, e muitas vezes o gargalo não é a sua internet nem o servidor de origem, e sim o caminho entre eles. O Ephemeral Downloader coloca a sua VPS no meio: o servidor faz o download (normalmente com peering melhor), mostra o progresso em tempo real e entrega o arquivo para você pelo navegador.
|
|
|
|
O projeto é focado em uso efêmero. O arquivo fica em um diretório temporário da sessão no servidor e é apagado quando a sessão expira (1 hora sem interação). Não é um proxy de streaming puro: o download passa pelo disco do servidor antes de chegar até você.
|
|
|
|
## Como funciona
|
|
|
|
```
|
|
navegador (React + Vite)
|
|
│ POST /api/queue {url} GET /api/tasks (polling de 2 s)
|
|
▼ ▲
|
|
Fastify (server/) ──undici GET──▶ host de origem │
|
|
│ │
|
|
▼ │
|
|
server/storage/<id-da-sessao>/<task>_<arquivo> ────────┘
|
|
▲
|
|
│ GET /api/download/:taskId (arquivo concluído)
|
|
navegador
|
|
```
|
|
|
|
- `POST /api/queue` enfileira a URL e o backend baixa com `undici`, gravando em `server/storage/<id-da-sessão>/` por um pipeline (respeita backpressure e só marca a task como concluída depois do arquivo em disco).
|
|
- O frontend faz polling de `GET /api/tasks` a cada 2 segundos e mostra status, progresso, bytes recebidos, total e velocidade em MB/s e Mbps.
|
|
- Quando a task conclui, o botão "Save to Disk" busca `GET /api/download/:taskId` e salva o arquivo.
|
|
- Em produção o próprio Fastify serve o build do Vite na raiz, com fallback da SPA.
|
|
- `GET /stream` é o proxy antigo, mantido apenas respondendo 410 (descontinuado).
|
|
|
|
## Stack
|
|
|
|
| Camada | Escolha |
|
|
|---|---|
|
|
| Runtime | Node.js 20.19 ou superior |
|
|
| Backend | Fastify 5, undici 7 (cliente HTTP), zod (validação) |
|
|
| Frontend | React 19, Vite 7, Tailwind CSS 4, lucide-react |
|
|
| Linguagem | TypeScript 5.9 |
|
|
| Monorepo | pnpm workspaces (`server` e `web`) |
|
|
| Deploy | nixpacks (`nixpacks.toml`) |
|
|
|
|
## Requisitos
|
|
|
|
- Node.js 20.19 ou superior (exigência do Vite 7 e do undici 7; verificado no Node 24.20.0)
|
|
- pnpm 9 ou superior (verificado no pnpm 12.4.1; o lockfile é o formato 9.0)
|
|
- Portas livres: backend (padrão 3000) e Vite (5173) em desenvolvimento
|
|
|
|
Observação sobre pnpm: a seção `overrides` do `pnpm-workspace.yaml` (fastify e undici com versão mínima de segurança) só é aplicada pelo pnpm 10 ou superior. Com pnpm 9 ela é ignorada.
|
|
|
|
## Início rápido
|
|
|
|
```bash
|
|
pnpm install
|
|
|
|
# desenvolvimento: Fastify (3000) + Vite (5173) juntos
|
|
pnpm dev
|
|
# abra http://localhost:5173
|
|
```
|
|
|
|
Em desenvolvimento o Vite encaminha `/api` para o backend (proxy configurado em `web/vite.config.ts`), então não é preciso arquivo `.env`. Se a porta 3000 estiver ocupada:
|
|
|
|
```bash
|
|
PORT=3100 VITE_API_PROXY=http://localhost:3100 pnpm dev
|
|
```
|
|
|
|
## Uso
|
|
|
|
1. Cole a URL direta (http ou https) de um arquivo e clique em **Download**.
|
|
2. A lista de downloads mostra status, progresso, tamanho e velocidade, atualizada a cada 2 segundos.
|
|
3. Quando o download conclui, use **Save to Disk** para salvar o arquivo na sua máquina.
|
|
4. A sessão dura 1 hora de inatividade (o contador reinicia a cada interação) e depois os arquivos são apagados do servidor.
|
|
|
|
## API
|
|
|
|
| Método | Rota | O que faz |
|
|
|---|---|---|
|
|
| GET | `/health` | Devolve `{"status":"ok"}`. |
|
|
| GET | `/` | Serve a SPA (build do Vite). |
|
|
| GET | `/api/session` | Cria ou recupera a sessão e devolve `sessionId`, `expiresAt` e `remainingMs`. O id também vem no header `x-session-id`. |
|
|
| POST | `/api/queue` | Corpo `{"url":"https://..."}`. Enfileira e inicia o download, devolvendo a task criada. |
|
|
| GET | `/api/tasks` | Lista as tasks da sessão (usado no polling da interface). |
|
|
| GET | `/api/download/:taskId` | Entrega o arquivo concluído da task. |
|
|
| GET | `/stream` | Descontinuado: responde 410. |
|
|
|
|
A sessão é identificada pelo header `x-session-id` (a interface guarda o id em `localStorage`). Erros observados: 400 para URL inválida (`{"error":"Invalid URL"}`) ou download não pronto, 403 para task de outra sessão e 404 para task inexistente. Se a origem falhar (por exemplo, 404 no host de origem), a task fica com `status: "error"` e a mensagem em `error`.
|
|
|
|
## Produção
|
|
|
|
```bash
|
|
pnpm build # server: tsc | web: tsc -b && vite build
|
|
PORT=3000 pnpm start # node server/dist/index.js: API + build do front na mesma porta
|
|
```
|
|
|
|
O `nixpacks.toml` descreve esse fluxo: build em `pnpm install --frozen-lockfile && pnpm build` e start em `pnpm start`.
|
|
|
|
Variáveis de ambiente: `PORT` (padrão 3000) e `STORAGE_ROOT` (padrão `server/storage`) no backend; `VITE_API_URL` (host da API no frontend) e `VITE_API_PROXY` (alvo do proxy em dev).
|
|
|
|
## Estrutura do projeto
|
|
|
|
```
|
|
server/ backend Fastify
|
|
├── src/index.ts rotas (/health, /api/*), CORS e static do front
|
|
└── src/lib/
|
|
├── DownloadManager.ts download da origem com undici e gravação na sessão
|
|
└── SessionManager.ts sessões, expiração (1 h) e limpeza dos arquivos
|
|
web/ frontend React + Vite
|
|
├── src/context/ SessionContext (sessão e polling das tasks)
|
|
├── src/components/ ui/ (Input, Button, Card, Alert...) e feature/ (form, item, timer)
|
|
└── vite.config.ts proxy de /api em desenvolvimento
|
|
.agent/ scaffolding de agentes de IA (não faz parte do runtime)
|
|
docs/PLAN.md vazio (placeholder nunca preenchido)
|
|
nixpacks.toml comandos de build e start do deploy
|
|
pnpm-workspace.yaml workspaces, allowBuilds e overrides
|
|
```
|
|
|
|
## Verificação
|
|
|
|
Não há testes automatizados neste repositório. O que existe de fato:
|
|
|
|
- `pnpm build` roda o `tsc` do server e `tsc -b && vite build` do web, e é o gate atual;
|
|
- `pnpm --filter web lint` roda o eslint (passa com 1 warning de `exhaustive-deps` em `SessionContext.tsx`);
|
|
- verificação manual executada em 14/09/2026 (Linux, Node 24.20.0, pnpm 12.4.1): `pnpm install` e `pnpm build` com exit 0; 13 checagens de API passando (`/health`, `/`, `/api/session`, `/api/queue` com URL inválida e válida, `/api/tasks`, `/api/download/:id`, 400/403/404 e `/stream` respondendo 410); download de 10 MB conferido por sha256 idêntico ao da origem; interface verificada em Chrome headless (sessão inicializada, task concluída e "Save to Disk" baixando o arquivo com o mesmo sha256).
|
|
|
|
Adicionar testes automatizados é o próximo passo natural do projeto.
|
|
|
|
## Estado atual e limitações
|
|
|
|
- Sessões e tasks ficam em memória: reiniciar o servidor perde a lista de tasks (os arquivos em disco são removidos pelo coletor depois de 1 hora).
|
|
- Sem autenticação: qualquer um que alcance o servidor pode enfileirar downloads. Coloque atrás de VPN, autenticação reversa ou firewall.
|
|
- Não há retomada de download nem uso de `Range` no fluxo atual (o parâmetro `offset` do antigo `/stream` não existe mais).
|
|
- Não há limite de tamanho, banda ou cota: um link muito grande ocupa o disco do servidor até a sessão expirar.
|
|
- URLs que não sejam http/https são aceitas na fila e falham depois com `status: "error"`.
|
|
- Sem CI configurada e sem arquivo de licença.
|
|
- O diretório `.agent/` é scaffolding de ferramentas de IA e não é usado pela aplicação.
|
|
|
|
## Documentação
|
|
|
|
Não há documentação interna além deste README: `docs/PLAN.md` está vazio e `web/README.md` é o texto padrão do template do Vite.
|
|
|
|
## Licença
|
|
|
|
Sem licença definida no repositório: não existe arquivo `LICENSE` e o `package.json` está marcado como `private`. Defina uma licença antes de reutilizar ou publicar o código.
|