From 10752413c2d98abb4bff233ca2b986244889cb35 Mon Sep 17 00:00:00 2001 From: Evandro Fonseca Junior Date: Tue, 13 Oct 2026 21:44:31 -0300 Subject: [PATCH] =?UTF-8?q?docs:=20redesign=20do=20README=20(padr=C3=A3o?= =?UTF-8?q?=20TwitterBR-MOD)=20=E2=80=94=20dados=20atualizados?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 229 ++++++++++++++++++++++++------------------------------ 1 file changed, 100 insertions(+), 129 deletions(-) diff --git a/README.md b/README.md index 46e3a52..c0ee2d0 100644 --- a/README.md +++ b/README.md @@ -1,153 +1,124 @@ -## Ephemeral Downloader +
-Um proxy de download efêmero para você usar a **sua VPS como ponte de alta velocidade** entre servidores lentos e a sua máquina local. +# Ephemeral Downloader -Em vez de baixar direto de um host congestionado, você aponta o link para o Ephemeral Downloader, que: +**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.** -- Faz o download via VPS (geralmente com rota/peering melhor). -- Transmite o arquivo por **streaming** para o seu navegador. -- Libera os recursos assim que o download termina. +![Fastify](https://img.shields.io/badge/Fastify-5-informational) +![React](https://img.shields.io/badge/React-19-informational) +![Node.js](https://img.shields.io/badge/Node.js-%E2%89%A5%2020-informational) -Simples, focado e pensado para quem vive baixando ISOs, ROMs e arquivos grandes. +[Como funciona](#como-funciona) · [Execução local](#execu%C3%A7%C3%A3o-local) · [Produção](#produ%C3%A7%C3%A3o) · [Limitações](#limita%C3%A7%C3%B5es-conhecidas) ---- +
-## Como funciona (visão rápida) +> [!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`. -- **Frontend (React + Vite)** +> [!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. - - Interface SPA com campo de texto para múltiplos links (um por linha). - - Lista de downloads recentes com: - - Progresso em tempo real. - - Tamanho total e já baixado. - - Velocidade em **MB/s** e **Mbps**. - - Botão para **retomar downloads** que falharam dentro da mesma sessão. +## Como funciona -- **Backend (Fastify + Streams)** - - Rota `GET /stream?url=...&offset=...` que: - - Faz `HEAD` na origem para validar o link e obter `Content-Length`. - - Faz `GET` (ou `Range: bytes=offset-` para retomada). - - Faz o **streaming direto** para o cliente, sem guardar arquivo em disco. - - Em produção, também serve o build do frontend (Vite) na própria porta do servidor. +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 | -## Requisitos +```mermaid +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 +``` -- Node.js **>= 20** -- `pnpm` **>= 8** +
+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`) | -## Instalação +| 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 | -Clone o repositório e instale as dependências: +
+ +## 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**. ```bash -git clone Downloader -cd Downloader 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). -## Scripts principais +## Produção -Na raiz do projeto: +O [`nixpacks.toml`](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`. -- **Desenvolvimento (backend + frontend juntos)**: +## Avisos -```bash -pnpm dev -``` - -Isso sobe: - -- Fastify em `http://localhost:3000` -- Vite em `http://localhost:5173` - -- **Build de produção (server + web)**: - -```bash -pnpm build -``` - -- **Start de produção (tudo pela porta 3000)**: - -```bash -pnpm start -``` - -O servidor Fastify: - -- Servirá o build do Vite na raiz (`/`). -- Continuará expondo `GET /stream` para o proxy de download. - ---- - -## Configuração de ambiente - -### Desenvolvimento - -Por padrão o frontend conversa com o backend em: - -```ts -const API_URL = import.meta.env.VITE_API_URL ?? "http://localhost:3000"; -``` - -Se você mudar a porta ou rodar o backend em outro host, basta criar um arquivo `.env` dentro de `web/`: - -```bash -VITE_API_URL=http://seu-backend:3000 -``` - -### Produção (Nixpacks ou similar) - -- Defina a variável `PORT` (a maioria das plataformas faz isso automaticamente). -- O servidor Fastify faz fallback para `3000` se `PORT` não estiver definida. -- O arquivo `nixpacks.toml` já descreve: - - Comando de build: `pnpm build` - - Comando de start: `pnpm start` - ---- - -## Fluxo de uso típico - -1. Você tem uma **VPS bem localizada** (por exemplo, no mesmo continente do servidor de origem). -2. No seu desktop/notebook, abre o Ephemeral Downloader. -3. Cola um ou mais links HTTP/HTTPS diretos (por exemplo, ROMs no SourceForge). -4. Clica em **“Iniciar download”**. -5. A VPS baixa em alta velocidade e o app: - - Mostra o progresso. - - Indica tamanho total / baixado. - - Mostra velocidade agregada em MB/s e Mbps. -6. Se a conexão cair, você pode **retomar** dentro da mesma sessão. - ---- - -## Limitações intencionais - -- **Retomada entre sessões** (fechar aba / desligar máquina) **não é suportada**. - - O projeto é focado em uso efêmero, sem manter arquivos em disco no servidor. -- O servidor realiza apenas: - - Validação básica de URL (somente `http`/`https`). - - Proxy por streaming com **backpressure** nativo do Node.js. - - Nenhuma autenticação ou controle de cota por usuário (idealmente coloque atrás de VPN, auth reverso ou firewall). - ---- - -## Ideias futuras - -- Autenticação simples para restringir o painel. -- Limite de banda por IP/usuário. -- Modo “linha de comando” usando a mesma API do servidor. - ---- - -## Por que esse projeto existe? - -Porque às vezes o problema não é a sua internet nem o servidor de origem, e sim o **caminho entre eles**. - -Colocando uma VPS no meio do caminho, você “puxa” o arquivo por uma rota melhor até a VPS e, de lá, faz o streaming até você com muito mais previsibilidade. - -O Ephemeral Downloader encapsula essa ideia em uma interface simples, focada em quem só quer **colar o link, apertar um botão e acompanhar o progresso**. +- A pasta [`.agent/`](.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.