Repository navigation
feat(sponsors): track impressions and clicks for every sponsored slot - #644
Merged
Merged
Conversation
Nothing recorded sponsor or hiring clicks before: /api/sponsors/:id/visit only redirected and GA carried growth-funnel events. This adds a daily rollup so sponsor reports can be cut by day, month, and advertiser. - migrations/0034: ad_daily_stats (Beijing day x kind x id x placement, impressions/clicks) plus a stable sponsorships.advertiser_id, so a sponsor's strip, row, and job ads roll up under one key across renames. - POST /api/sponsors/pulse: same-site beacon sink. Upserts resolve the advertiser from sponsorships/jobs/job_companies, so unknown ids write nothing; crawler UAs and cross-site requests are dropped. - SlotMetrics: one tracker per shell (Next layout and Astro Shell). An impression is >=50% visible for 1s, once per slot per path; every click counts. Slots opt in with a data-track-slot attribute, so server-rendered hiring cards ship no extra JS. - Tagged: top strip, homepage sponsor row, model credit, footer, pill, /sponsor holders, homepage hiring cards, /jobs cards and careers links, /jobs/<id> apply and company links. - docs/operations/sponsor-metrics.md: field reference and report queries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hikariming
requested review from
AsperforMias and
KurosawaGeeker
as code owners
November 5, 2026 15:39
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Nothing recorded sponsor or hiring clicks until now.
/api/sponsors/:id/visitonly 302s, and GA carries only growth-funnel events. Sponsor reports need impressions, clicks, and CTR by day, month, and sponsor. Counting starts at deploy; there is no history to backfill.What
migrations/0034_ad_daily_stats.sql: a rollup table with one row per Beijing day ×ad_kind×ad_id×placement, holdingimpressionsandclicks. It also addssponsorships.advertiser_id, a stable key filled for all 7 existing rows. Sponsor names get retitled (Vibe42, three times), and ModaLeap's sponsor cell, job cards, and careers link should all roll up undermodaleap.POST /api/sponsors/pulse: the beacon endpoint.INSERT … SELECTfrom sponsorships/jobs/job_companies, so unknown ids write nothing.Sec-Fetch-Site: cross-sitegets 403.SlotMetrics: mounted once in the Next locale layout and the AstroShell.data-track-slot, so server-rendered hiring cards ship no extra JS./sponsorholders, homepage hiring cards,/jobscards and careers links,/jobs/<id>apply and company links.docs/operations/sponsor-metrics.md: field reference and ready-made day / month / sponsor / placement queries.Every wrangler env points
GHFIND_D1at the sameghfinddatabase, and both deploy workflows rund1 migrations apply ghfind --remote. The migration therefore lands on the shared production D1 at deploy. It is additive only: one new table, one nullable column, sevenUPDATEs.Verification
tsc(root, release, apps/api),astro check,frontend-stack:check, and ESLint all pass. On top ofmain, vitest passes 237 files / 2506 tests.advertiserresolves across kinds.pnpm dev:localon a local D1 (guest, no login needed). Playwright with a desktop UA loaded/zhand/zh/jobs, scrolled, and clicked a sponsor cell and a job card. Rows landed with the right Beijing day, kinds, and advertisers: impressions only for slots that were on screen, one click on each of the two clicked slots. A headless UA was dropped, as intended.Originvsrequest.urlcomparison fails when the request is proxied (dev, and the router → api hop). It is fixed to useSec-Fetch-Siteonly, and a regression test is added.Frontend checklist
Known limits
Counts are operational, not billing-grade: a scripted client can still inflate them. Visitors who block the beacon are missing from both impressions and clicks, so CTR stays consistent. README-embedded share cards (SVG/PNG images) can't report clicks.
🤖 Generated with Claude Code