TreeMap
A free, open-source disk-space visualizer. Squarified treemap, duplicate hunting, disk-full forecasting, and cleanup that only ever moves files to the Trash.
Your debate tournament history, verified and ready to share.
ProductivityTypeScript · JavaScriptGitHub @DrftingWoodOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Gmail → Tabbycat scrape → Postgres → personal debate CV. Deploys to Vercel.
You sign in with Google. The app reads your Gmail (read-only), pulls Tabbycat private URLs
(*.calicotab.com / *.herokuapp.com), scrapes each tournament's public tabs
(team / speaker / round results / break / participants), and compiles a debate CV you can view at /cv.
app/ Next.js App Router — routes + API handlers
page.tsx Landing (unauth) / redirect to /dashboard (auth)
dashboard/ Scan Gmail, ingest status, identity review
cv/ Personal CV with roster picker fallback
settings/ Disconnect, delete, export JSON
privacy/, terms/ Consent-screen-required legal pages
api/
auth/[...nextauth]/ NextAuth v5 handler
ingest/gmail POST — discover URLs from Gmail, enqueue jobs
ingest/url POST — ingest a single URL synchronously
ingest/drain POST — user-scoped queue drainer (~50 s budget)
cron/process-queue GET/POST — daily cron safety net
persons/[id]/claim | reject POST/DELETE — identity review actions
account/disconnect | delete | export Privacy controls
lib/
auth.ts NextAuth config (Google, Prisma adapter)
db.ts Prisma client singleton
crypto.ts AES-256-GCM for token encryption
queue.ts FOR UPDATE SKIP LOCKED job helpers
gmail/
client.ts OAuth client factory + token encrypt/decrypt
extract.ts PRIVATE_URL_RE regex, MIME walker, dedupe
run.ts Bounded-concurrency Gmail list+get
calicotab/
fetch.ts Rate-limited fetch + SourceDocument provenance
parseNav.ts Private-URL landing (nav + registration)
parseTabs.ts team/speaker/round/break/participants
fingerprint.ts Tournament fingerprint + name normalization
provenance.ts ParserRun records + warnings
version.ts PARSER_VERSION (bump to invalidate cache)
ingest.ts Orchestrator: URL → full upsert in one tx
components/
ui/* Button / Card / Badge / Toast / Skeleton / ...
features DashboardActions, IdentityReview, ClaimPersonButton
prisma/
schema.prisma Auth + queue + calicotab + provenance
migrations/ Four migrations tracked in git
vercel.json Daily cron
openid email profile gmail.readonly)openid, .../auth/userinfo.email, .../auth/userinfo.profile, .../auth/gmail.readonly.https://<your-domain>/privacy. Terms: https://<your-domain>/terms.https://<your-vercel-domain>/api/auth/callback/googlehttp://localhost:3000/api/auth/callback/google (for local dev)DrftingWood/debate_cv.POSTGRES_PRISMA_URL (pooled, used at runtime) and POSTGRES_URL_NON_POOLING (direct, used by prisma migrate deploy) — no manual DB env vars to copy.AUTH_GOOGLE_ID – from GCP step 1.AUTH_GOOGLE_SECRET – from GCP step 1.AUTH_SECRET – generate with openssl rand -base64 32.CRON_SECRET – any long random string required for /api/cron/process-queue; Vercel sends it as a Bearer token to scheduled cron requests.prisma generate && next build. The DB is migrated automatically on the first request via prisma migrate deploy; you can also run it once manually from the Vercel CLI:
vercel env pull .env.production.local
pnpm prisma migrate deploy
pending to done (up to ~50 seconds of ingestion per click).pending after that, click Process queued to continue, or Ingest now on a single row./cv).A daily cron at 0 3 * * * also drains anything left in the queue as a safety net (Hobby-tier compatible; Pro users can tighten the schedule in vercel.json).
# 1. Install
pnpm install
# 2. Environment
cp .env.example .env
# Fill in AUTH_GOOGLE_ID, AUTH_GOOGLE_SECRET, AUTH_SECRET, POSTGRES_PRISMA_URL,
# POSTGRES_URL_NON_POOLING, CRON_SECRET. Any Postgres works — local Docker,
# Neon branch DB, Supabase project. For local dev you can set both URLs to the
# same connection string.
# 3. Migrate
pnpm prisma migrate dev
# 4. Run
pnpm dev # http://localhost:3000
pnpm test # vitest (parser + fingerprint + Gmail extract + crypto + contracts)
pnpm typecheck # tsc --noEmit
pnpm lint # eslint .
pnpm build # next build
Set TOKEN_ENCRYPTION_KEY in your Vercel env (openssl rand -base64 32). Access +
refresh tokens in the GmailToken table are stored as
v1:<iv>:<tag>:<ciphertext> under AES-256-GCM. Rows written before the key was
configured stay readable as plaintext and are marked encryptionVersion = null.
To rotate: generate a new key, set it alongside the old one, add a new v2
branch to lib/crypto.ts, and run a one-time re-encrypt script over the table.
Every landing-page fetch records a SourceDocument row (url + content hash +
status) and a ParserRun row stamped with PARSER_VERSION
(lib/calicotab/version.ts). When you change parsers, bump PARSER_VERSION.
The ingest orchestrator checks the latest ParserRun against the current
version; if it's older, the 30-day freshness cache is bypassed for that URL and
a fresh parse is performed on the next scan.
app/ Routes (UI + API)
page.tsx Landing (unauth) / redirect to dashboard (auth)
dashboard/page.tsx Extracted URLs, job status, manual ingest
cv/page.tsx Personal CV rendered from claimed Person rows
privacy/, terms/ Required for Google OAuth consent
api/auth/[...nextauth]/ NextAuth handler
api/ingest/gmail/ POST: scan Gmail, enqueue URLs
api/ingest/url/ POST: ingest a single URL (sync)
api/ingest/jobs/ GET: user's job list
api/cv/ GET: CV JSON
api/cron/process-queue/ Vercel cron; drains up to 5 pending jobs
lib/
auth.ts NextAuth v5 + Prisma adapter + Gmail token persist
db.ts Prisma client singleton
queue.ts FOR UPDATE SKIP LOCKED job claim
gmail/
extract.ts PRIVATE_URL_RE, MIME walker, dedupe
run.ts Gmail list+get with bounded concurrency
client.ts OAuth client from stored refresh token
calicotab/
fetch.ts Rate-limited HTTP with User-Agent
parseNav.ts Private-URL landing page (nav + registration)
parseTabs.ts team / speaker / round / break / participants
fingerprint.ts Tournament fingerprint, year extraction
ingest.ts Orchestrator: URL → full upsert in one tx
prisma/
schema.prisma Auth + ingest queue + Calicotab schema
migrations/ Prisma migrations
tests/ Vitest
vercel.json Cron schedule */2 * * * *
GmailToken).gmail.readonly. The app never writes to Gmail.GMAIL_OAUTH_WEBSITE_GUIDE.md — OAuth walkthroughdocs/CALICOTAB_DATA_MODEL.md — entities and extraction contractdocs/CALICOTAB_TAB_STRUCTURE.md — Tabbycat URL conventionsdocs/INGESTION_WORKFLOW.md — pipeline pseudo-code