尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗Chain-saver duty tracker for the Torn faction EPIC Mafia [40959].
Members enlist as chain savers ("I can save for 3h"), the app tracks their paid availability time, watches the faction chain via the Torn API every ~15s, automatically detects and credits chain saves (who hit, at how many seconds remaining — holds included), rotates turns between simultaneous savers, sends push notifications ("🚨 YOUR TURN — SAVE NOW"), and generates payout reports for the bankers.
| Piece | Where | Why |
|---|---|---|
| Next.js web app + API | Vercel Hobby | UI, auth, shifts, admin, payouts |
| Postgres | Supabase Free | all data; RLS locked to service-role |
| Chain poller | Supabase Edge Function poller | pg_cron (every 15s) → pg_net → function |
| Live updates | Supabase Realtime broadcast | countdown page without polling |
| Alerts | Web Push (VAPID) | works with the site closed |
Members log in with a Limited Torn API key (never full). Keys are stored
AES-256-GCM encrypted; every API call the app makes carries the comment
ChainWatch, so key owners can audit usage in their own Torn key log.
/v2/faction/chain (with a unique timestamp param each
call — Torn caches identical requests for ~30s and this is the documented
bypass).chain counter value.
From the attack's real ended timestamp it computes the true seconds
remaining — a held attack that registered "after zero" still counts.Detection observes actual API values, so warm-up chains, milestone cooldowns and future timer changes don't break it.
npm install
npm test # 29 unit tests: state machine, rotation, crypto
cp .env.example .env.local # fill in values (see below)
npm run dev
npx supabase login
npx supabase link --project-ref <PROJECT_REF>
npx supabase db push # applies migrations (schema + cron jobs)
openssl rand -base64 32 # → API_KEY_ENC_KEY
openssl rand -base64 32 # → POLLER_SECRET
node scripts/generate-vapid.mjs # prints VAPID_KEYS and NEXT_PUBLIC_VAPID_PUBLIC_KEY
npx supabase secrets set POLLER_SECRET=... API_KEY_ENC_KEY=... VAPID_SUBJECT=mailto:[email protected]
npx supabase secrets set VAPID_KEYS='{"publicKey":{...},"privateKey":{...}}'
npx supabase functions deploy poller --no-verify-jwt
select vault.create_secret('https://<PROJECT_REF>.supabase.co/functions/v1/poller', 'poller_url');
select vault.create_secret('<POLLER_SECRET value>', 'poller_secret');
select jobname, status, start_time
from cron.job_run_details
order by start_time desc limit 10;
Rows should appear ~15s apart with status = succeeded. If sub-minute
scheduling is rejected on your project, fall back to a 1-minute schedule
where the function loops internally:
select cron.unschedule('chainwatch-poller');
-- then re-add with schedule '* * * * *' (and ask the poller to loop —
-- see supabase/migrations/0002_cron.sql comments)
Push this repo to GitHub, then import it at vercel.com/new.
Set environment variables (Project → Settings → Environment Variables):
NEXT_PUBLIC_SUPABASE_URL — from Supabase → Settings → APINEXT_PUBLIC_SUPABASE_ANON_KEY — same pageSUPABASE_SERVICE_ROLE_KEY — same page (keep secret!)API_KEY_ENC_KEY — same value as the Supabase secretSESSION_SECRET — openssl rand -base64 32NEXT_PUBLIC_VAPID_PUBLIC_KEY — from the generate-vapid script(The faction id lives in the database: settings.faction_id, seeded to
40959. To track a different faction, update that row in the SQL editor.)
Deploy. The daily /api/keepalive cron (vercel.json) plus the poller's
own traffic keep the Supabase free project from pausing.
/duty, land ~5 hits, then deliberately wait until
the timer is under 90s and have one enlisted member hit (bonus points for
holding the result screen). Within a minute the save should appear
confirmed with the seconds-remaining, the rotation should advance, and the
turn-holder should have received a push.cron.job_run_details and the function logs.For stretches like the Halloween event, when the faction pays for saves but there's no ranked war to hang a war payout on. Admin → Event mode: name, end time (TCT) and a flat $ per save → Start event now.
While it runs:
/duty redirects to /event (enlist, siren, standings); the home page shows
a popup + banner pointing there; the whole site wears the Halloween themewar_report, so nothing is paid twiceAt the end time (or End event now) the poller closes the event's shifts and
everything returns to normal. On /event an admin then freezes the payout
(hourly pay + saves × $ per save); it's published on /payouts.
Deploy order: apply 0042_chain_events.sql first, then restart the VPS poller,
then deploy the site — the new poller and site read the new table/column.
app/ Next.js pages + API routes
lib/ server glue (db, session, crypto, torn, state)
supabase/functions/
_shared/logic/ PURE chain state machine + rotation (unit-tested)
_shared/lib/ torn client + crypto (runs in Node AND Deno)
poller/ the 15s poll cycle (detection, attribution, alerts)
supabase/migrations/ schema + cron schedules
tests/ Vitest suites for the correctness core
docs/superpowers/specs/ the approved design document