diff --git a/.dockerignore b/.dockerignore index 21f091af..7166685e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,13 +1,27 @@ .git +.gitignore .DS_Store .env -.env.local +.env.* +.snow +# docs & markdown (top-level only, don't exclude subservice READMEs) +docs/ +*.md +apps/*/*.md + +# build artifacts apps/*/node_modules apps/*/dist apps/*/.vite + +# backend runtime data apps/backend/data/*.db apps/backend/data/*.db-shm apps/backend/data/*.db-wal apps/backend/data/browser-sessions apps/backend/data/redeem-screenshots +apps/backend/.npm-cache + +# deploy data (server only) +deploy/data/ diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index ea225a8e..00000000 --- a/CLAUDE.md +++ /dev/null @@ -1,118 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Project Overview - -订单自动兑换系统 (Order Auto-Redemption System). 接收电商平台的 webhook 订单,通过 Playwright 浏览器自动化完成腾讯/QQ 等平台的兑换码兑换,生成用户领取链接。 - -## Architecture - -**Monorepo**: `apps/backend` + `apps/frontend` + `deploy/` - -### Backend (`apps/backend/`) -- **Stack**: Node.js (ESM), Express 5, PostgreSQL (pg), Playwright 1.42.1, Python OCR worker (ddddocr) -- **Entry**: `src/index.js` — Express app bootstrap with health checks and graceful shutdown -- **Layering**: `routes/` → `services/` → `repositories/` → `db/client.js` (pg Pool singleton) -- **Config**: `config/default.cjs` as base, `.env` overrides parsed in `src/config/runtime.js`. No dotenv library — custom env file parser. -- **DB migrations**: `src/db/migrations/` — SQL files run automatically on startup via `src/db/migrate.js` (tracks applied migrations in `schema_migrations` table) -- **Types**: Gradual TypeScript migration in progress. `tsconfig.json` with `allowJs: true, checkJs: false, strict: false` — JSDoc type annotations on selected files, types defined in `src/types/` -- **Test runner**: Node.js built-in `node --test` - -**Key service domains**: -- `services/session/` — Playwright browser session management (login, redeem, screenshots), the core automation engine -- `services/claim/` — Claim token generation and user-facing claim page API -- `services/admin/` — Admin panel CRUD (split into read/write helpers, auth, dashboard, etc.) -- `services/order/` — Webhook processing, order/inventory management, Agiso platform integration -- `services/platforms/` — Third-party platform adapters (currently Agiso for 闲鱼/Xianyu) -- `subservices/ocr-worker/` — Python (uv) microservice for CAPTCHA OCR, invoked via subprocess - -**Runtime data** (`data/` dir, mounted in Docker dev): logs, browser session artifacts, screenshots, `order-fulfillment-bindings.json` - -### Frontend (`apps/frontend/`) -- **Stack**: Vue 3, Vite 8, Element Plus, vue-router (hash mode), TypeScript -- **Routes**: `/tx/browser` (browser session control), `/claim/:token` (user claim page), `/admin/*` (admin panel with role-based guards) -- **Auto-imports**: `unplugin-auto-import` + `unplugin-vue-components` for Element Plus - -### Deployment (`deploy/`) -- Docker Compose: `docker-compose.yml` (production), `docker-compose.dev.yml` (hot-reload dev) -- Caddy reverse proxy: `/api/v1/*` → backend, `/*` → frontend -- Production Dockerfiles use Chinese mirrors (Tencent Cloud) for apt/npm/pip - -## Common Commands - -### Local Development (without Docker) -```bash -# Backend -cp .env.mac-docker.example .env -cd apps/backend -npm install -npm run dev # node --watch restart on src/ changes - -# Frontend -cd apps/frontend -npm install -npm run dev # Vite dev server - -# OCR worker (Python) -cd apps/backend/subservices/ocr-worker -uv sync -``` - -### Docker Development (recommended) -```bash -cp .env.mac-docker.example .env -docker compose -f docker-compose.dev.yml up -d --build -# Access: http://localhost, noVNC: http://localhost:6080/vnc.html -``` - -### Testing -```bash -# Run all backend tests -cd apps/backend && npm test - -# Run a single test file -cd apps/backend && node --test src/services/session/session-redeem.test.js - -# Run tests matching a pattern -cd apps/backend && node --test --test-name-pattern="redeem" -``` - -### Type Checking -```bash -cd apps/backend && npm run typecheck # tsc --noEmit (selected files) -cd apps/frontend && npm run typecheck # vue-tsc --noEmit -``` - -### Database -```bash -cd apps/backend && npm run db:migrate # Run migrations manually -cd apps/backend && npm run seed:dev-data # Seed dev data -cd apps/backend && npm run cleanup:dev-data # Clean dev data -``` - -### Browser (Playwright) -```bash -cd apps/backend && npm run browser:install # macOS -cd apps/backend && npm run browser:install:linux # Linux (with deps) -``` - -## API Routes - -All backend routes are prefixed with `/api/v1/`: -- `/api/v1/tencent/browser/*` — Browser session CRUD and control -- `/api/v1/webhooks/agiso/*` — Agiso platform webhook receiver -- `/api/v1/claim/*` — Claim token validation and redemption -- `/api/v1/admin/*` — Admin panel API (auth, orders, tasks, inventory, etc.) - -Health checks (no prefix): `/health`, `/health/live`, `/health/ready` - -## Important Conventions - -- Backend uses ESM throughout (`"type": "module"`) — always use `.js` extensions in imports -- Express route handlers use `buildSuccessPayload` / `sendRouteError` from `src/utils/http.js` for consistent response format -- Database queries go through `src/db/client.js` (`query()` / `withTransaction()`) — no raw pool usage in services -- Runtime config is accessed via `runtimeConfig` from `src/config/runtime.js` — never read `.env` directly outside that module -- Admin routes are split into separate files under `src/routes/admin/` with shared middleware in `shared.js` -- Frontend uses hash-based routing (`createWebHashHistory`) — claim links include `/#/claim/:token` -- Playwright version is pinned at `1.42.1` — do not upgrade without testing browser automation flows diff --git a/deploy/docker/backend.Dockerfile b/deploy/docker/backend.Dockerfile index 64f8ff81..6f9ed857 100644 --- a/deploy/docker/backend.Dockerfile +++ b/deploy/docker/backend.Dockerfile @@ -12,22 +12,27 @@ ENV NPM_CONFIG_REGISTRY=${NPM_REGISTRY} WORKDIR /app -RUN sed -i "s|http://deb.debian.org/debian|https://${DEBIAN_MIRROR}/debian|g" /etc/apt/sources.list.d/debian.sources \ - && sed -i "s|http://security.debian.org/debian-security|https://${DEBIAN_MIRROR}/debian-security|g" /etc/apt/sources.list.d/debian.sources - -RUN apt-get update \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + sed -i "s|http://deb.debian.org/debian|https://${DEBIAN_MIRROR}/debian|g" /etc/apt/sources.list.d/debian.sources \ + && sed -i "s|http://security.debian.org/debian-security|https://${DEBIAN_MIRROR}/debian-security|g" /etc/apt/sources.list.d/debian.sources \ + && apt-get update \ && apt-get install -y --no-install-recommends curl ca-certificates python3 python3-pip tzdata xauth x11vnc novnc websockify \ && ln -snf /usr/share/zoneinfo/${TZ} /etc/localtime \ - && echo ${TZ} > /etc/timezone \ - && rm -rf /var/lib/apt/lists/* + && echo ${TZ} > /etc/timezone COPY apps/backend/package.json apps/backend/package-lock.json ./ RUN --mount=type=cache,target=/root/.npm npm ci -RUN PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright/ npx playwright install --with-deps chromium +RUN --mount=type=cache,target=/ms-playwright-cache,sharing=locked \ + PLAYWRIGHT_BROWSERS_PATH=/ms-playwright-cache \ + PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright/ \ + npx playwright install --with-deps chromium \ + && cp -r /ms-playwright-cache /ms-playwright COPY apps/backend/subservices/ocr-worker/ ./subservices/ocr-worker/ -RUN python3 -m pip install --no-cache-dir --break-system-packages ./subservices/ocr-worker +RUN --mount=type=cache,target=/root/.cache/pip,sharing=locked \ + python3 -m pip install --break-system-packages ./subservices/ocr-worker COPY apps/backend/ ./ diff --git a/deploy/docker/frontend.Dockerfile b/deploy/docker/frontend.Dockerfile index 4c1a1521..d3a92744 100644 --- a/deploy/docker/frontend.Dockerfile +++ b/deploy/docker/frontend.Dockerfile @@ -8,7 +8,8 @@ ENV NPM_CONFIG_REGISTRY=${NPM_REGISTRY} WORKDIR /app COPY apps/frontend/package.json apps/frontend/package-lock.json ./ -RUN npm install +RUN --mount=type=cache,target=/root/.npm,sharing=locked \ + npm ci COPY apps/frontend/ ./ RUN npm run build diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index ce1e5601..d87d709d 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -67,12 +67,12 @@ services: - "${TENCENT_BROWSER_NOVNC_PORT:-6080}:6080" frontend: - image: node:22-bookworm + image: node:22-bookworm-slim working_dir: /app restart: unless-stopped command: >- sh -lc - "npm install --no-fund --no-audit && + "[ -f node_modules/.install_done ] || (npm install --no-fund --no-audit && touch node_modules/.install_done); npm run dev -- --host 0.0.0.0 --port 5173" environment: TZ: ${TZ:-Asia/Shanghai} @@ -84,9 +84,7 @@ services: - frontend_node_modules:/app/node_modules web: - build: - context: . - dockerfile: deploy/docker/dev-caddy.Dockerfile + image: caddy:2-alpine restart: unless-stopped depends_on: backend: