cleanyourtv-app
Clean Your TV를 위한 커뮤니티 제작 모바일 앱
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 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 |