個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗A commercial-grade SaaS companion for EA FC Ultimate Team transfer-market sniping, built on a private, per-user record of what the market actually did — not a crowd-sourced guess. It ships in three milestones:
Two rules hold across every milestone: the extension only ever drives the
game's own service layer, never forges a request; and the safety
governor sits between every decision the extension makes and the action it
takes, so the product stops you before you look like a bot — it is never sold as "undetectable". See
docs/01-architecture.md for how that is enforced.
The build follows a data-first sequence. All eleven phases are built,
verified (pnpm typecheck && pnpm lint && pnpm build && pnpm test across
seven packages, plus the e2e, security and load suites) and merged into
main via PR #2
with the full CI pipeline green (lint, typecheck, unit, migrations, API
integration, security tests, security scan, CodeQL, extension/dashboard/docker
builds, coverage, and every Playwright e2e suite).
PR #19 hardened
that merge: the server kill switch now reaches EA tabs that are already open,
the staging deploy skips itself with a notice until its secrets exist, and a
trial-start race found by CI is closed. Remaining work is the go-live
checklist in docs/13-roadmap.md, which needs the live market, the PayPal.me payment
setup and the staging/production deploy secrets.
| Phase | Scope | Status | Where |
|---|---|---|---|
| 1 Architecture | Monorepo, shared contracts, diagrams, roadmap | Done | packages/shared, docs/01-architecture.md, docs/13-roadmap.md |
| 2 Database | 35 tables, partitioning, audit, views, seed, tests | Done | packages/db, docs/02-database.md |
| 3 Backend API | Fastify, 110+ routes, WS gateway, jobs, OpenAPI | Done | apps/api, docs/03-api.md |
| 4 Authentication | JWT + rotating refresh, sessions, devices, 2FA, lockout, admin roles | Done | apps/api/src/modules/auth, docs/04-auth.md |
| 5 Subscriptions | Plans, trials with abuse protection, licenses, PayPal claims, coupons, bans, flags | Done | apps/api/src/modules/{subscriptions,licenses,payments,payment-claims,coupons,plans,bans,flags}, docs/05-subscriptions.md |
| 6 Extension | TypeScript port, ranker, governor, assist, gated autobuyer, popup, options | Done | apps/extension, docs/06-extension.md |
| 7 Dashboard | React user + admin dashboard, design system, e2e against the real API | Done | apps/dashboard, packages/ui, docs/07-dashboard.md |
| 8 Analytics | KPI engine, profit analytics, reports, CSV exports, materialisation jobs | Done | apps/api/src/modules/{analytics,admin-analytics}, docs/08-analytics.md |
| 9 Security | Hardening pass, 167 security tests, threat model, controls inventory | Done | docs/09-security.md, docs/threat-model.md |
| 10 UI/UX | Design system, dashboard and extension surfaces, axe-checked | Done | packages/ui, docs/10-design-system.md |
| 11 Testing | Unit, integration, cross-app e2e, k6 load, security suites, coverage | Done | tests/, docs/12-testing.md |
Follow-ups from every phase report and all ten QA defects have been closed
(see docs/12-testing.md §12 for the defect table with statuses). The last
of those, found while driving PR #2's CI to green, was a critical extension
bug: the content script's crash-recovery read of storage.session is
forbidden in MV3 content scripts, which aborted its boot and silently killed
market recording and telemetry on every page load. It is fixed, the e2e
assertions that had hidden it are now real, and every cross-app journey
passes. Two more were fixed after the merge in PR #19: an engine already
running in an open EA tab did not learn about a kill switch flipped
mid-session until the next reload (it now halts within one heartbeat plus
one engine tick, no reload needed; docs/06-extension.md §5), and a trial
started right after login could fail on an insert race in the IP-activity
counter that completeLogin writes in the background.
Extension (apps/extension): two build targets from one codebase.
ledger is the listable build (recorder + assist); ledger-auto adds the
autobuyer, which is excluded from the ledger bundle at build time. The
only EA-aware file is src/main/adapter.ts; it observes passively, probes the
web app's service layer at load and before every action, and hard-stops on a
shape mismatch. The governor enforces actions per hour, session length,
buy-to-search ratio and coin flow, with cooldowns and an unconditional server
kill switch. The kill switch is pushed into every open EA tab after each
bootstrap and heartbeat, and the engine tick pulls the cached flag as a
fallback, so a switch flipped mid-session halts a running engine without a
reload. Raw observations stay in IndexedDB. What the extension sends to the
backend is itemised in docs/06-extension.md and in the options page.
Backend (apps/api): Fastify 5 with Zod validation and generated OpenAPI,
Drizzle over PostgreSQL 16, Redis-backed rate limits and presence, a
WebSocket gateway with Redis pub/sub fan-out, BullMQ workers, PayPal payment claims,
and an audit log with before/after on every admin action.
Database (packages/db): hand-written SQL migrations with monthly
partitioning for activity and audit tables, append-only audit logs, soft
deletes everywhere, KPI views and a materialised daily KPI table.
Dashboard (apps/dashboard + packages/ui): React 19 user and admin
dashboard on the generated OpenAPI client, cookie sessions with CSRF, live
counters and notifications over WebSocket, and a component library in the
dark gaming theme. Deployed to Vercel. The Trades page lists every card
the extension has logged with the tax and net profit the API computed, and
records a sale on a card that is still open — the extension never sees the
trader's own transfer list, so that is how a bought card becomes a closed,
profit-bearing trade.
Where profit numbers come from. The API is the only thing that computes
them. Whatever a client reports for tax and net profit is ignored: both are
derived from the buy and sell prices with @sl/shared's
computeTradeProfit (5% EA tax, integer coins), and every write recomputes
the affected (user, UTC day) rows of the profits table in the same
request, so the dashboard reflects a trade immediately rather than after the
hourly job.
Shared contracts (packages/shared): plan and feature constants, error
codes, the admin permission matrix, every request and response schema, the
WebSocket event union and the extension message contracts.
apps/
api/ Fastify backend: REST, WebSocket, BullMQ workers
dashboard/ React user + admin dashboard (Vite, TanStack, Tailwind)
extension/ MV3 extension, two build targets
packages/
shared/ Zod schemas, DTOs, error codes, plans, permissions, message contracts
db/ SQL migrations, Drizzle schema, seed, test utilities
config/ Shared tsconfig / ESLint / Prettier presets
ui/ Design system: tokens and React components
infra/ Docker, Compose, Caddy, monitoring, backups
docs/ One document per phase; start at docs/01-architecture.md
Requirements: Node 22, pnpm via Corepack, PostgreSQL 16 and Redis 7.
corepack enable
pnpm install
# Postgres 16 + Redis 7. Either use the repo's compose file:
pnpm docker:dev # postgres, redis, mailpit
# …or point at your own instances, creating the role and both databases once:
# createuser sl --login --pwprompt && createdb sniper_ledger -O sl \
# && createdb sniper_ledger_test -O sl
# env: one file serves the API, the worker, the migrator and the seed
cp infra/env/.env.development.example apps/api/.env
node infra/scripts/check-env.mjs apps/api/.env # validates it against the zod schema
# database (defaults: postgres://sl:[email protected]:5432/sniper_ledger)
pnpm --filter @sl/db migrate
[email protected] SEED_ADMIN_PASSWORD='change-me' pnpm --filter @sl/db seed
# build the workspace packages the api and dashboard resolve through dist/
pnpm exec turbo run build --filter=@sl/api^... --filter=@sl/dashboard^...
# API (the example env ships a working dev keypair; regenerate your own with
# `pnpm --filter @sl/api keys:generate` if you would rather not share one)
pnpm --filter @sl/api dev # http://localhost:3000, /health/ready
pnpm --filter @sl/api worker # BullMQ jobs
# dashboard — http://localhost:5173, proxies /api and /ws to the API above
pnpm --filter @sl/dashboard dev
# extension
pnpm --filter @sl/extension build # dist/ledger and dist/ledger-auto
# chrome://extensions → Developer mode → Load unpacked → apps/extension/dist/ledger
# sign in as the seeded dev user (NODE_ENV != production):
# [email protected] / dev-password-123
# A plan's device limit is enforced at login (the trial's is 1), so a second
# browser profile or a curl session needs the first device revoked from
# Settings → Devices, else login returns DEVICE_LIMIT_REACHED.
The extension is the normal source of trades, but every step it drives is a plain authenticated API call, so the whole path can be exercised with curl against the local stack above — useful for checking the dashboard end to end before the live market is available.
API=http://localhost:3000
TOKEN=$(curl -s $API/api/v1/auth/login -H 'content-type: application/json' \
-d '{"email":"[email protected]","password":"dev-password-123",
"device":{"fingerprint":"local-dev-device-0001","name":"curl"}}' \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["accessToken"])')
# 1. report a bought card, exactly as the extension's telemetry flush does
curl -s $API/api/v1/trades/batch -H "authorization: Bearer $TOKEN" \
-H 'content-type: application/json' -d '{"trades":[{
"id":"'"$(python3 -c 'import uuid; print(uuid.uuid4())')"'","tradeId":"demo-1","resourceId":158023,"assetId":null,
"rating":91,"buyPrice":50000,"sellPrice":null,"eaTax":0.05,"netProfit":null,
"status":"bought","boughtAt":"'"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)"'","soldAt":null}]}'
# 2. record the sale — or do it from the dashboard's Trades page, which is
# what a trader actually does. The API computes the tax and the net.
TRADE_ID=$(curl -s "$API/api/v1/trades?limit=1" -H "authorization: Bearer $TOKEN" \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["items"][0]["id"])')
curl -s $API/api/v1/trades/$TRADE_ID/close -H "authorization: Bearer $TOKEN" \
-H 'content-type: application/json' -d '{"sellPrice":60000}'
# -> netProfit 7000: 60,000 sale less 3,000 EA tax less the 50,000 purchase
# 3. the figures are already rolled up — no waiting for the hourly job
curl -s $API/api/v1/analytics/me/overview -H "authorization: Bearer $TOKEN"
# everything (test suites run serially: they share the test database)
pnpm typecheck && pnpm lint && pnpm build && pnpm test
pnpm test:e2e && pnpm test:security && pnpm test:load # see docs/12-testing.md
API, db and security tests need DATABASE_URL, TEST_DATABASE_URL,
REDIS_URL and REDIS_TEST_DB; see apps/api/.env.example and
docs/12-testing.md for per-suite isolation.
The dashboard deploys to Vercel from apps/dashboard (see vercel.json);
set VITE_API_ORIGIN in the Vercel project and DASHBOARD_ORIGIN on the
API, see docs/07-dashboard.md. The API,
worker, PostgreSQL and Redis run on a VM with Docker Compose behind Caddy.
Images, Compose files for dev/staging/prod, monitoring, backups, CI/CD and
the step-by-step deployment guide live under infra/, .github/ and
docs/11-devops.md.
.github/workflows/release.yml builds and pushes :main-tagged images on
every push to main, then migrates and deploys staging only once the four
STAGING_* secrets (STAGING_DATABASE_URL, STAGING_SSH_HOST,
STAGING_SSH_USER, STAGING_SSH_PRIVATE_KEY) are set on the staging
GitHub Environment; until then the staging jobs are skipped with a workflow
notice rather than failing the run. A vX.Y.Z tag builds semver-tagged
images and the extension release zips and deploys production behind the
production environment's required reviewers. See docs/11-devops.md §4.
| Document | Contents |
|---|---|
docs/01-architecture.md | Components, deployment, sequence diagrams, trust boundaries, build targets |
docs/02-database.md | ERD, every table, indexes, partitioning and retention runbooks |
docs/03-api.md | Every route with auth, permission, rate limit and schema |
docs/04-auth.md | Token lifetimes, refresh rotation, devices, 2FA, CSRF, admin roles |
docs/05-subscriptions.md | Plan matrix, state machine, license keys, trial protection, PayPal payment claims |
docs/06-extension.md | Worlds, message flows, governor math, telemetry itemisation, day-one checklist |
docs/07-dashboard.md | Routes and permissions, auth/CSRF/WS handling, tokens, components, Vercel deployment |
docs/08-analytics.md | Every metric formula, source tables, materialisation schedule, export formats |
docs/09-security.md | Controls inventory with file and test references, key rotation, open findings |
docs/threat-model.md | STRIDE per component, attack vectors with mitigations, residual risks, non-goals |
docs/10-design-system.md | Tokens, typography, components, chart rules, page specs, accessibility |
docs/11-devops.md | Local dev, environments, deployment guide, monitoring runbook, backups, readiness checklist |
docs/12-testing.md | Test strategy, commands, isolation rules, coverage, load thresholds, defects found |
docs/13-roadmap.md | Remaining phases, exit criteria, go-live checklist |
docs/06-extension.md.docs/06-extension.md.| DevOps | Docker, Compose, Caddy, CI/CD, monitoring, backups, deploy guides | Done | infra/, .github/, docs/11-devops.md |