also looking at this
umbrella: pet-store storefront fixes (rolling)
#3
586 files
+38082
-7487
| @@ -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 | /** |