also looking at this
umbrella: pet-store storefront fixes (rolling)
#3
586 files
+38082
-7487
Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.
| @@ -89,3 +89,9 @@ FRONTEND_APP_ENV="${APP_ENV}" | ||
| 89 | 89 | FRONTEND_APP_URL="${APP_URL}" |
| 90 | 90 | |
| 91 | 91 | FRONTEND_STRIPE_PUBLIC_KEY= |
| 92 | ||
| 93 | # Admin gate (see app/Gates.ts → 'access-admin'). | |
| 94 | # Either env grants admin access; both are comma-separated lists. | |
| 95 | # Leave both empty to deny all admin access by default. | |
| 96 | ADMIN_EMAILS= | |
| 97 | ADMIN_EMAIL_DOMAINS= | |
| @@ -25,10 +25,37 @@ import type { UserModel } from '@stacksjs/orm' | ||
| 25 | 25 | */ |
| 26 | 26 | export const gates = { |
| 27 | 27 | /** |
| 28 | * Check if user can access admin area | |
| 28 | * Check if a user can access the admin area. | |
| 29 | * | |
| 30 | * Driven by two env vars; either one matching is enough: | |
| 31 | * - ADMIN_EMAILS comma-separated list of exact emails | |
| 32 | * (e.g. `chris@barebowl.com,glenn@example.com`) | |
| 33 | * - ADMIN_EMAIL_DOMAINS comma-separated list of email domains | |
| 34 | * (with or without leading `@`, e.g. | |
| 35 | * `barebowl.com,stacksjs.org`) | |
| 36 | * | |
| 37 | * Default is deny-all when neither is set — safer than carrying a | |
| 38 | * hardcoded `@stacksjs.org` allowlist into apps that aren't run by | |
| 39 | * stacksjs. Comparison is case-insensitive on the email side and | |
| 40 | * matched against the suffix `@<domain>` so a user typing | |
| 41 | * `Chris@BareBowl.com` still matches an `ADMIN_EMAIL_DOMAINS=barebowl.com` | |
| 42 | * entry. | |
| 29 | 43 | */ |
| 30 | 44 | 'access-admin': (user: UserModel | null) => { |
| 31 | return user?.email?.endsWith('@stacksjs.org') ?? false | |
| 45 | const email = user?.email?.toLowerCase() | |
| 46 | if (!email) return false | |
| 47 | ||
| 48 | const explicit = (process.env.ADMIN_EMAILS ?? '') | |
| 49 | .split(',') | |
| 50 | .map(s => s.trim().toLowerCase()) | |
| 51 | .filter(Boolean) | |
| 52 | if (explicit.includes(email)) return true | |
| 53 | ||
| 54 | const domains = (process.env.ADMIN_EMAIL_DOMAINS ?? '') | |
| 55 | .split(',') | |
| 56 | .map(s => s.trim().toLowerCase().replace(/^@/, '')) | |
| 57 | .filter(Boolean) | |
| 58 | return domains.some(domain => email.endsWith(`@${domain}`)) | |
| 32 | 59 | }, |
| 33 | 60 | |
| 34 | 61 | /** |
| @@ -25,10 +25,32 @@ import type { UserModel } from '@stacksjs/orm' | ||
| 25 | 25 | */ |
| 26 | 26 | export const gates = { |
| 27 | 27 | /** |
| 28 | * Check if user can access admin area | |
| 28 | * Check if a user can access the admin area. | |
| 29 | * | |
| 30 | * Driven by two env vars; either one matching is enough: | |
| 31 | * - ADMIN_EMAILS comma-separated list of exact emails | |
| 32 | * - ADMIN_EMAIL_DOMAINS comma-separated list of email domains | |
| 33 | * (with or without leading `@`) | |
| 34 | * | |
| 35 | * Default is deny-all when neither is set — safer than carrying a | |
| 36 | * hardcoded allowlist into every Stacks app that scaffolds from | |
| 37 | * this default. | |
| 29 | 38 | */ |
| 30 | 39 | 'access-admin': (user: UserModel | null) => { |
| 31 | return user?.email?.endsWith('@stacksjs.org') ?? false | |
| 40 | const email = user?.email?.toLowerCase() | |
| 41 | if (!email) return false | |
| 42 | ||
| 43 | const explicit = (process.env.ADMIN_EMAILS ?? '') | |
| 44 | .split(',') | |
| 45 | .map(s => s.trim().toLowerCase()) | |
| 46 | .filter(Boolean) | |
| 47 | if (explicit.includes(email)) return true | |
| 48 | ||
| 49 | const domains = (process.env.ADMIN_EMAIL_DOMAINS ?? '') | |
| 50 | .split(',') | |
| 51 | .map(s => s.trim().toLowerCase().replace(/^@/, '')) | |
| 52 | .filter(Boolean) | |
| 53 | return domains.some(domain => email.endsWith(`@${domain}`)) | |
| 32 | 54 | }, |
| 33 | 55 | |
| 34 | 56 | /** |