Skip to content

feat(nexus): add live slides with auth, other improvements#272

Open
sreetamdas wants to merge 116 commits into
mainfrom
dev
Open

feat(nexus): add live slides with auth, other improvements#272
sreetamdas wants to merge 116 commits into
mainfrom
dev

Conversation

@sreetamdas

@sreetamdas sreetamdas commented May 29, 2026

Copy link
Copy Markdown
Owner

add better-auth, slides presenter auth, tailwind v4 migration, env refactors, and CI hardening

auth

  • add better-auth foundation with Google + Cloudflare OAuth
  • require presenter login for slide deck live controls
  • co-locate provider sign-in handlers in the auth domain
  • fail closed on a missing secret and restore baseURL
  • return generic errors instead of leaking config in 5xx responses
  • pin kysely for better-auth compat
  • cover the allowlist, cloudflare user-response shape, and the getUserInfo fetch path in tests

slides

  • add presenter login prompt before live controls
  • split the live-session transport hook out of the overlay UI
  • share reaction emoji list across durable object and client
  • reject out-of-range slide indices and non-allowlisted reactions in the wire protocol
  • stop polling snapshots while the websocket is connected
  • avoid direct window access in live controls
  • cover presenter fail-closed and the viewer poll guard in tests

durable objects

  • share the origin allowlist across the presence and slide-session DOs
  • give the slide-session DO a hibernation event timeout

stats + likes

  • migrate likes to D1 batch and return the public counter from the write batch
  • make post_likes idempotency a composite primary key
  • move the like/view counters into the domains/PageInteraction layer
  • drop the test-only DI from the counter server boundaries
  • log read failures instead of swallowing them; warn once when the like-hash salt is missing
  • stream dashboard data from Plausible instead of blocking; tolerate null metrics from the query api
  • extract dashboard sections into components
  • cover likes SQL, the serverFn boundary, normalizePathname, null metrics, and real-D1 batch behavior in tests
  • dedup helpers and drop dead code

env + config

  • move Cloudflare env to cloudflare:workers imports
  • align server modules with import protection
  • refactor tailwind to v4 config in CSS
  • move config files to .config/
  • extract shared route and protocol helpers
  • add tunnel config and zed settings

CI

  • fail preview deploy when deploy fails
  • build fresh in E2E job instead of downloading cross-machine artifact
  • restore CI/debug detection via vite env
  • drop the unused build-output artifact upload
  • add content-collections caching to E2E job
  • increase preview server timeout for Worker runtime init
  • mock Cloudflare workers in unit tests
  • keep Plausible server code out of client graph

presence

  • return a generic error instead of leaking the binding name

maintenance

  • upgrade deps
  • skip hidden gist files in rwc
  • remove impeccable config

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented May 29, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
sreetamdas-com-staging 1143c83 Jun 15 2026, 06:08 AM

@github-actions

github-actions Bot commented May 29, 2026

Copy link
Copy Markdown

✅ Preview deploy for 26bcaf6

🔗 Preview URL

sreetamdas and others added 26 commits May 29, 2026 08:53
Wire up the previously-unused page_details.likes column as a like button on
blog posts. Idempotency is enforced server-side via a new post_likes table
keyed by (slug, visitor_hash), where visitor_hash = SHA-256(LIKES_IP_SALT:slug:ip)
from cf-connecting-ip — no raw IP stored, slug folded in to prevent cross-post
correlation. Writes are gated by an allowlist of published blog posts so forged
slugs can't pollute D1. Optimistic UI reconciles by refetch on error.

Requires: set LIKES_IP_SALT (local .env + CF secret) and run db:migrate:local /
db:migrate:remote to apply the post_likes migration.
The /stats loader returned an awaited getStats() call, so the whole page
blocked on the Plausible round-trip. Return the stats as an unawaited promise
and render the data sections behind <Suspense>/<Await> with layout-matched
skeletons, so the shell paints immediately. The hero 'Window' fact now reads
the validated period search param (not resolved data) to stay synchronous, and
the deferred promise is made non-rejecting (.catch -> unavailable stats) so a
transport failure renders the degraded state rather than the root error page.
- share normalizePathname via helpers/utils instead of three copies
- reuse LikeCount type and createEmptyStats instead of re-declaring
- remove write-only localStorage from LikeButton (server hasLiked is source of truth)
- hoist stats Intl formatters out of render loops
- drive stats skeleton and sections from one DASHBOARD_SECTIONS const
- exercise real getLikes/incrementLikes against in-memory sqlite (idempotency, per-visitor hasLiked, slug isolation, trailing-slash)
- add LikeButton.serverFns boundary tests with stub deps (allowlist, fail-open, disabled/no-hash reads)
- add normalizePathname edge cases
@sreetamdas sreetamdas changed the title nexus prep feat(nexus): add live slides with auth, other improvements Jun 9, 2026
sreetamdas added 25 commits June 9, 2026 17:14
- restore the snapshot on mutation error instead of a blanket invalidate
- surface a read-only state (disabled button, neutral copy) when no visitor hash can be derived
- add aria-pressed to the button and rename the badge to LikeCountBadge to free the LikeCount type name
- warn once per 5-minute window so a sustained counter outage keeps surfacing
- replace the inline bottom-of-page sentence with a fixed pill (fa6 heart + count)
- float it in the content's right gutter on desktop, bottom-right on smaller screens
- pop the heart on like via the shared reactionCountPulse keyframe, motion-safe gated
- keep aria-label/aria-pressed, the read-only state, and an accessible loading label
Route files containing createServerFn get pulled into the rsc graph
(?tss-serverfn-split), which drops the route component client Fast Refresh
boundary, so editing the page never updates the DOM in dev. Moving the
server fn into -index.server.tsx keeps the route as a client-refreshable
component (?tsr-split=component).
Applies the same extraction as the home route across about, $slug, keebs,
blog, and newsletter routes: each createServerFn/renderServerComponent loader
moves into a colocated -<name>.server.tsx so the route file keeps its client
Fast Refresh boundary (?tsr-split=component) instead of being pulled into the
rsc graph. Verified HMR on /about and /blog/$slug; loader data unaffected.
- Convert flat route files to folder+route.tsx pattern
- Move blog/index/route.tsx → blog/route.tsx and rename server file
- Move newsletter/index/route.tsx → newsletter/route.tsx and rename server file
- Move foobar/foobar/index.tsx → foobar/route.tsx
- Move foobar/foobar/$slug.tsx → foobar/$slug/route.tsx
- Move slides/json-schema-form/index.tsx → slides/json-schema-form/route.tsx

- Keep existing colocated routes: about, keebs, rwc, slides, slides/tanstack-start
- Left index.tsx as-is for home page (uses explicit path in createFileRoute)

All routes now follow consistent colocated folder structure with route.tsx for routes and -x.server.tsx for server logic.
- Convert stats.tsx → stats/route.tsx
- Convert karma.tsx → karma/route.tsx
- Convert version.tsx → version/route.tsx
- Convert fancy-pants.tsx → fancy-pants/route.tsx
- Convert resume.tsx → resume/route.tsx

Continuing the refactor from b66b130 which extracted server functions to -x.server.tsx files.
The 659233e commit deleted the flat route files and updated routeTree.gen.ts
to expect folder/route.tsx paths, but never committed the new files, leaving
the repo broken. Recreates all routes at their expected locations:

- Flat routes (about, karma, keebs, stats, , index) moved into
  per-route folders as route.tsx with colocated server files
- blog/index.tsx and newsletter/index.tsx become blog/route.tsx and
  newsletter/route.tsx; their index server files stay in the same dir
- blog/ and newsletter/ server files move into / subfolders
  to stay colocated with the route
- layout route.tsx, rwc, foobar, and newsletter components restored
- rwc server fn extracted to -rwc.server.tsx (HMR fix pattern)
In the folder+route.tsx structure, route.tsx acts as a layout parent and
$slug/route.tsx is its child. With no <Outlet /> in the layout, child routes
never render - causing /blog/$slug to show nothing (broken CI).

Convert each to a minimal layout (route.tsx with no component) and move the
archive/index page content to index.tsx, which renders as an exact-match
child inside the layout's Outlet.
post_likes records the salt_version its visitor hash was derived under, and
incrementLikes recomputes the public counter from current-era rows only, so
rotating LIKES_IP_SALT (with a bumped LIKES_SALT_VERSION) can't inflate counts
or let prior visitors re-like. The version is also folded into the visitor hash
so a version bump alone yields fresh identities.

Also add CHECK (view_count >= 0) and CHECK (likes >= 0) to page_details, and a
scripts/likes-recount.mjs helper (pnpm db:likes:recount) to recompute/repair the
denormalized counters on demand.
Add a README tidbit on the IP-hash likes and self-healing counter, list
Cloudflare Workers + D1 in the tech stack, and document the salt-rotation
runbook, recount script and pre-migration check in the contributing guide.
Baseline the loader cost (content lookup + MDX render) ahead of server-rendering
the like count, so we can measure what the prefetch adds. Surfaced as a
Server-Timing response header on the blog document; CPU segments can read coarse
in the Workers runtime, so client-side TTFB/Web Vitals remain the primary signal.
On blog posts the view counter and like button each fired their own server fn
(two round-trips, two Worker invocations). Add a shared fetchPageMetricsServerFn
that runs both reads concurrently, and a usePageMetrics query both components
share, so they dedupe to a single request and both numbers land together.

Scoped to posts: ViewsCounter opts in via useMetrics (set on the blog route);
the other pages that render ViewsCounter keep their standalone views fetch.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant