Agregador de preços de hardware (Kabum, Terabyte, Pichau) com montador de PC guiado por engine de compatibilidade — o "PCPartPicker brasileiro".
- Catálogo unificado de peças com menor preço por loja e histórico de preço.
- Montador de PC com filtragem automática de peças incompatíveis (socket, chipset, DDR, dimensões, potência da fonte) e avisos contextuais.
- Preço total do build otimizado por loja + link direto para cada peça.
- Ofertas (maiores quedas em 24 h/7 dias) e alertas de preço por e-mail (magic link, sem conta).
- Builds compartilháveis por link público.
Especificação: docs/specs.md ·
Roadmap: docs/roadmap.md ·
Findings de scraping: docs/scraping-findings.md
| Camada | Tecnologia |
|---|---|
apps/web |
Angular 22 (zoneless, signals, standalone) + Tailwind v4 |
apps/api |
Cloudflare Worker + Hono + Zod |
packages/db |
Drizzle ORM + Supabase Postgres (sa-east-1) + Hyperdrive |
packages/domain |
Engine de compatibilidade (TypeScript puro) |
packages/shared |
Schemas Zod compartilhados |
scraper |
Python (httpx + Playwright) → POST no Worker |
| imagens | R2 (openpc-images) servidas em /images/* |
Monorepo npm workspaces + Turborepo. Sem Docker, sem Actions, sem VPS.
apps/web SPA Angular
apps/api Worker (API + assets em produção)
packages/db schema Drizzle + migrations Postgres (Supabase)
packages/domain regras de compatibilidade + insights de preço
packages/shared Zod
scraper/ coleta Kabum/Terabyte/Pichau
flowchart LR
USER[Usuário] --> CF[Cloudflare Worker]
CF --> WEB[Static Assets\nAngular]
CF --> API["/api/v1/*"]
API --> PG[(Supabase Postgres)]
API --> R2[(R2 imagens)]
SCRAPER[scraper Python] -->|"POST /internal/v1/*"| API
KABUM[Kabum] & TERA[Terabyte] & PICHAU[Pichau] --> SCRAPER
npm install
cp apps/api/.dev.vars.example apps/api/.dev.vars # preencha DATABASE_URL (pooler Supabase)
npm run db:migrate # schema + seed no Supabase (via DATABASE_URL)
npm run dev # web :4200 + api :8787| Serviço | URL |
|---|---|
| web (ng serve) | http://localhost:4200 (/api → :8787) |
| api (wrangler) | http://localhost:8787 (/api/v1/health) |
Só a API:
npm run dev -w @openpc/apiSó o front:
npm run dev -w @openpc/webO scraper não sobe sozinho e não deve ser rodado sem pedido explícito. Fluxo: coleta direto no Postgres via Worker (Supabase).
# 1. API local (Supabase)
cp apps/api/.dev.vars.example apps/api/.dev.vars # DATABASE_URL + SCRAPER_TOKEN
npm run db:migrate
npm run dev -w @openpc/api # http://localhost:8787
# 2. coleta (quando autorizado) — grava no Supabase
export OPENPC_API_URL=http://localhost:8787
export OPENPC_SCRAPER_TOKEN=dev-scraper-token
cd scraper && uv run python -m openpc_scraper run-once kabum cpu
# 3. migração legado D1 local → Supabase (se já tinha catálogo em D1)
DATABASE_URL=postgres://... ./scripts/d1-to-supabase.sh # pede confirmação; use -y para pular
# 4. fotos no R2 (URLs locais viram /images/* após sync)
export OPENPC_API_URL=https://openpc.felipebrgs.workers.dev
export OPENPC_SCRAPER_TOKEN=... # secret do Worker
uv run python -m openpc_scraper sync-imagesTestes do scraper (sem rede, sem coleta):
cd scraper && uv run pytest| Comando | O quê |
|---|---|
npm run dev |
turbo: Angular + Worker |
npm run build |
build de todos os pacotes |
npm test |
testes (domínio + Angular) |
npm run typecheck |
tsc / ng build |
npm run db:migrate |
aplica migrations no Supabase (via DATABASE_URL) |
DATABASE_URL=... ./scripts/d1-to-supabase.sh |
migra catálogo legado D1 local → Supabase |
Criar o Supabase (uma vez):
# 1. Crie projeto em supabase.com (região sa-east-1 São Paulo)
# 2. Copie a connection string pooler (6543) em Database > Connect > Transaction pooler
# 3. Em Cloudflare: wrangler hyperdrive create openpc-supabase --connection-string="postgres://...pooler...:6543/postgres"
# 4. Cole o hyperdrive id em apps/api/wrangler.jsonc e defina DATABASE_URL em .dev.vars
# 5. Aplique schema:
DATABASE_URL=postgres://... pnpm run db:migratePublicar (manual, sem Actions):
npm run build -w @openpc/web
npx wrangler deploy --config apps/api/wrangler.jsoncnpm test # turbo: domain (vitest) + web (vitest)
cd scraper && uv run pytest # normalize/collect do scraper