Skip to content

feat: add demo app, SQL migrations, and Edge Function improvements#74

Merged
tomusdrw merged 9 commits intomainfrom
td-demo
Mar 31, 2026
Merged

feat: add demo app, SQL migrations, and Edge Function improvements#74
tomusdrw merged 9 commits intomainfrom
td-demo

Conversation

@tomusdrw
Copy link
Copy Markdown
Member

Summary

  • Demo app (demo/) — minimal Vite + React app that tests the full Supabase integration end-to-end: login, user menu, settings with subscription status, pricing card, and quota-gated features. Deployed alongside Storybook at fluffylabs.dev/shared-ui/demo/
  • SQL migrationsuser_data, subscriptions, usage tables with RLS policies and increment_usage RPC function
  • Edge Function improvements — extracted shared auth helper, fixed unknown error handling in catch blocks
  • Security fixincrement_usage RPC validates auth.uid() = p_user_id to prevent users from incrementing other users' quotas
  • GitHub Actions — workflow updated to build demo app with Supabase env vars from secrets

GitHub Secrets required

Secret Description
VITE_SUPABASE_URL Supabase project URL
VITE_SUPABASE_ANON_KEY Supabase anon/public key

Test plan

  • TypeScript, ESLint, tests all pass
  • Demo app builds successfully
  • SQL migrations applied to remote database
  • Edge Functions deployed and working
  • increment_usage RPC validates caller identity
  • Manual: register, login, change theme, test quota gate, test checkout flow

🤖 Generated with Claude Code

@netlify
Copy link
Copy Markdown

netlify bot commented Mar 31, 2026

Deploy Preview for fluffy-ui ready!

Name Link
🔨 Latest commit edca90b
🔍 Latest deploy log https://app.netlify.com/projects/fluffy-ui/deploys/69cbdd17f3dee70008326bd8
😎 Deploy Preview https://deploy-preview-74--fluffy-ui.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 31, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 46541883-20c6-4bdb-9f96-6dfcc618c7f5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a shared Supabase authentication helper used by edge functions, updates handlers to use it and improve error handling, introduces authorization checks to usage-related DB functions, adjusts demo behavior and docs, and updates build scripts and .gitignore.

Changes

Cohort / File(s) Summary
Configuration & Docs
/.gitignore, demo/README.md
Added ignore entries for env files and supabase/.temp; added a demo README describing the Shared UI Demo, local setup, and validated UI areas.
Demo App Behavior
demo/src/App.tsx
useManageSubscription now alerts "Please log in first" when no access token is present before returning.
Shared Auth Helper
supabase/functions/_shared/auth.ts
New authenticateUser(req) exported helper that reads Authorization header, constructs a Supabase client with forwarded header, calls auth.getUser(), and returns either { user } or a 401 Response.
Edge Functions (refactor)
supabase/functions/create-checkout/index.ts, supabase/functions/create-portal/index.ts, supabase/functions/stripe-webhook/index.ts
Replaced inline auth logic with authenticateUser(req) usage; unified early-return on auth error; improved catch handlers to safely derive error messages for non-Error throwables; simplified checkout line_items structure.
Database & Docs (auth guard)
supabase/migrations/20260331000000_init.sql, supabase/migrations/20260331000001_fix_increment_usage_auth.sql, lib/supabase/Supabase.mdx
Added/updated increment_usage to enforce that p_user_id equals auth.uid() and raise on mismatch; documentation updated to reflect the authorization guard.
Build Scripts
package.json
Split Storybook build into build:storybook and changed build to run Storybook then demo build.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant Edge as Edge Function Handler
    participant Auth as authenticateUser()
    participant SupAuth as Supabase Auth

    Client->>Edge: Request (Authorization header)
    Edge->>Auth: authenticateUser(req)
    Auth->>Auth: read Authorization header
    alt header missing
        Auth->>Edge: Return 401 Response
        Edge->>Client: 401 Unauthorized
    else header present
        Auth->>SupAuth: create client + auth.getUser()
        alt user valid
            SupAuth-->>Auth: user
            Auth-->>Edge: { user }
            Edge->>Client: continue processing (authorized)
        else user invalid
            SupAuth-->>Auth: error
            Auth-->>Edge: Return 401 Response
            Edge->>Client: 401 Unauthorized
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~28 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: demo app, SQL migrations, and Edge Function improvements are all reflected in the changeset with examples in the raw_summary.
Description check ✅ Passed The description is directly related to the changeset, covering the demo app, SQL migrations, Edge Function improvements, security fix, and GitHub Actions updates mentioned in the raw_summary.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch td-demo

Comment @coderabbitai help to get the list of available commands and usage tips.

tomusdrw and others added 3 commits March 31, 2026 15:33
- Add initial migration with user_data, subscriptions, usage tables,
  RLS policies, and increment_usage RPC function
- Add env.stripe and .env* to .gitignore

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tions

- Extract duplicated auth code from create-checkout and create-portal
  into _shared/auth.ts
- Fix catch blocks to handle non-Error thrown values (use instanceof
  check instead of accessing .message directly)
- Add demo/README.md with local development instructions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Security: add auth.uid() validation to increment_usage RPC function
  to prevent users from incrementing other users' quotas
- UX: add "Please log in first" alert to useManageSubscription for
  consistency with useCheckout
- Update docs (Supabase.mdx) with the secured RPC function

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Netlify runs `npm run build` which only built Storybook. Now it
also builds the demo app into storybook-static/demo/.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
supabase/migrations/20260331000000_init.sql (1)

37-45: Consider adding a primary key to the usage table.

The usage table relies solely on the unique constraint for row identity. While this works functionally, adding an explicit primary key (either a surrogate id uuid or making the composite columns a primary key) is generally better practice for:

  • Clearer row identity semantics
  • Better tooling support (ORMs, admin UIs)
  • Potential future foreign key references
💡 Option: Use composite primary key
 create table if not exists usage (
   user_id uuid not null references auth.users(id) on delete cascade,
   app_id text not null default '',
   action text not null,
   period text not null,
   count int not null default 0,
-  unique (user_id, app_id, action, period)
+  primary key (user_id, app_id, action, period)
 );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@supabase/migrations/20260331000000_init.sql` around lines 37 - 45, The usage
table currently only has a unique constraint for identity; add an explicit
primary key to improve row identity and tooling compatibility: either replace
the unique(...) constraint with a composite primary key on (user_id, app_id,
action, period) or add a surrogate id (e.g., id uuid primary key default
gen_random_uuid()) and keep the unique constraint; update the CREATE TABLE
statement for table usage and ensure any FK references or inserts use the chosen
primary key (referencing the table name usage and the columns user_id, app_id,
action, period or id as appropriate).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@package.json`:
- Around line 99-100: The package's "build" script currently only runs
"build:storybook" and "build:demo", but package exports point to dist/* so the
repository must produce library artifacts; rename the current "build" script to
"build:site" (preserving its "npm run build:storybook && npm run build:demo"
behavior) and restore "build" to run the library build first by making it call
"npm run build:lib && npm run build:site"; update the "scripts" section so
"build:site" holds the former build value and "build" composes "build:lib" then
"build:site" (ensure "build:lib" exists and produces dist).

In `@supabase/migrations/20260331000001_fix_increment_usage_auth.sql`:
- Around line 12-15: The IF guard currently uses "p_user_id != auth.uid()" which
yields NULL when auth.uid() is NULL and therefore can bypass the check; change
the condition to explicitly reject unauthenticated calls by checking for
auth.uid() IS NULL or using a NULL-safe comparison (e.g., IS DISTINCT FROM) so
the block raises the exception when there's no authenticated user or the IDs
differ; update the same pattern in the other migration (init.sql) at the lines
mentioned.

---

Nitpick comments:
In `@supabase/migrations/20260331000000_init.sql`:
- Around line 37-45: The usage table currently only has a unique constraint for
identity; add an explicit primary key to improve row identity and tooling
compatibility: either replace the unique(...) constraint with a composite
primary key on (user_id, app_id, action, period) or add a surrogate id (e.g., id
uuid primary key default gen_random_uuid()) and keep the unique constraint;
update the CREATE TABLE statement for table usage and ensure any FK references
or inserts use the chosen primary key (referencing the table name usage and the
columns user_id, app_id, action, period or id as appropriate).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a95321e0-e10d-421b-9959-1847925666c5

📥 Commits

Reviewing files that changed from the base of the PR and between 541fff1 and 77f0d51.

📒 Files selected for processing (11)
  • .gitignore
  • demo/README.md
  • demo/src/App.tsx
  • lib/supabase/Supabase.mdx
  • package.json
  • supabase/functions/_shared/auth.ts
  • supabase/functions/create-checkout/index.ts
  • supabase/functions/create-portal/index.ts
  • supabase/functions/stripe-webhook/index.ts
  • supabase/migrations/20260331000000_init.sql
  • supabase/migrations/20260331000001_fix_increment_usage_auth.sql

tomusdrw and others added 5 commits March 31, 2026 15:44
Asset paths were /shared-ui/demo/assets/... which only works on
GitHub Pages. Using relative base (./) makes it work on any host.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- use `IS DISTINCT FROM` instead of `!=` for auth.uid() comparison,
  since NULL != NULL evaluates to NULL and bypasses the guard
- add explicit `auth.uid() IS NULL` check for unauthenticated calls
- replace unique constraint with composite primary key on usage table
- update docs to match

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The demo's styles.css was importing Tailwind granularly (preflight,
theme, utilities) AND importing global.css which does @import
"tailwindcss" (the full thing). This caused CSS conflicts. Now just
imports global.css which already handles all Tailwind setup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Without this, Tailwind only scanned demo/src/ for utility classes,
so all classes used by shared-ui components (Header, Button, etc.)
were missing from the CSS output. CSS went from 14KB to 76KB.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
QuotaGate and its children were each calling useQuota independently,
causing inconsistent state — the gate showed stale data while the
child incremented its own separate counter. Actions could continue
past the limit because the gate never saw the updated count.

Now QuotaGate supports render props: children can be a function
receiving the quota state, so there's a single useQuota instance.
Also disables the action button when remaining hits 0.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tomusdrw tomusdrw merged commit a6be41d into main Mar 31, 2026
6 checks passed
@tomusdrw tomusdrw deleted the td-demo branch March 31, 2026 15:45
@github-actions github-actions bot mentioned this pull request Mar 31, 2026
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