@simple-auth-kit/cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -0
- package/bin/simple-auth-kit.mjs +18 -0
- package/lib/copy.ts +190 -0
- package/lib/rename-native.ts +173 -0
- package/package.json +31 -0
- package/registry/README.md +312 -0
- package/registry/admin-apps/README.md +71 -0
- package/registry/admin-apps/nextjs/shared/components.json +25 -0
- package/registry/admin-apps/nextjs/shared/next.config.ts +9 -0
- package/registry/admin-apps/nextjs/shared/postcss.config.mjs +7 -0
- package/registry/admin-apps/nextjs/shared/src/app/globals.css +159 -0
- package/registry/admin-apps/nextjs/shared/src/app/signup/page.tsx +72 -0
- package/registry/admin-apps/nextjs/shared/src/components/alert-modal.tsx +32 -0
- package/registry/admin-apps/nextjs/shared/src/components/breadcrumb.tsx +48 -0
- package/registry/admin-apps/nextjs/shared/src/components/form-error-alert.tsx +24 -0
- package/registry/admin-apps/nextjs/shared/src/components/loader/skeleton.tsx +39 -0
- package/registry/admin-apps/nextjs/shared/src/components/loader/table-skeleton-loader.tsx +57 -0
- package/registry/admin-apps/nextjs/shared/src/components/permission-group-select.tsx +101 -0
- package/registry/admin-apps/nextjs/shared/src/components/permission-group-view.tsx +53 -0
- package/registry/admin-apps/nextjs/shared/src/components/role-multi-select.tsx +71 -0
- package/registry/admin-apps/nextjs/shared/src/components/theme-toggle.tsx +26 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/alert.tsx +78 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/avatar.tsx +53 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/badge.tsx +51 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/breadcrumb.tsx +109 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/button.tsx +67 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/card.tsx +103 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/checkbox.tsx +32 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/collapsible.tsx +33 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/data-table.tsx +107 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/dialog.tsx +143 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/dropdown-menu.tsx +234 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/heading.tsx +16 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/input.tsx +19 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/label.tsx +24 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/modal.tsx +29 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/select.tsx +192 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/separator.tsx +28 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/sheet.tsx +135 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/sidebar.tsx +567 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/skeleton.tsx +13 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/sonner.tsx +34 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/table.tsx +116 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/tooltip.tsx +61 -0
- package/registry/admin-apps/nextjs/shared/src/hooks/use-live-audit-feed.ts +51 -0
- package/registry/admin-apps/nextjs/shared/src/hooks/use-mobile.ts +17 -0
- package/registry/admin-apps/nextjs/shared/src/lib/error.ts +23 -0
- package/registry/admin-apps/nextjs/shared/src/lib/query-client.tsx +10 -0
- package/registry/admin-apps/nextjs/shared/src/lib/token-storage.ts +36 -0
- package/registry/admin-apps/nextjs/shared/src/lib/utils.ts +6 -0
- package/registry/admin-apps/nextjs/shared/tsconfig.json +41 -0
- package/registry/admin-apps/nextjs/variants/base/.env.example +9 -0
- package/registry/admin-apps/nextjs/variants/base/Dockerfile +32 -0
- package/registry/admin-apps/nextjs/variants/base/package.json +58 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/account/change-password/page.tsx +163 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/account/page.tsx +569 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/audit-log/page.tsx +180 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/countries/[id]/edit/page.tsx +357 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/countries/[id]/page.tsx +191 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/countries/country-schema.ts +18 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/countries/new/page.tsx +305 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/countries/page.tsx +367 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/customers/[id]/edit/page.tsx +386 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/customers/[id]/page.tsx +213 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/customers/customer-schema.ts +35 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/customers/new/page.tsx +334 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/customers/page.tsx +400 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/dashboard/page.tsx +263 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/languages/[id]/edit/page.tsx +275 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/languages/[id]/page.tsx +183 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/languages/language-schema.ts +20 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/languages/new/page.tsx +226 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/languages/page.tsx +342 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/layout.tsx +50 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/permissions/[id]/edit/page.tsx +309 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/permissions/[id]/page.tsx +119 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/permissions/new/page.tsx +275 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/permissions/page.tsx +311 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/permissions/permission-schema.ts +44 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/roles/[id]/edit/page.tsx +276 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/roles/[id]/page.tsx +179 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/roles/new/page.tsx +241 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/roles/page.tsx +325 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/roles/role-schema.ts +20 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/users/[id]/edit/page.tsx +411 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/users/[id]/page.tsx +253 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/users/new/page.tsx +444 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/users/page.tsx +410 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/users/user-schema.ts +37 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/api/auth/[...nextauth]/route.ts +3 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/icon.svg +5 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/layout.tsx +31 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/login/page.tsx +162 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/page.tsx +22 -0
- package/registry/admin-apps/nextjs/variants/base/src/auth.ts +105 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/app-sidebar.tsx +182 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/nav-user.tsx +99 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/permission-required.tsx +32 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/photo-upload.tsx +203 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/accordion.tsx +66 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/alert-dialog.tsx +157 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/button-group.tsx +83 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/calendar.tsx +218 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/carousel.tsx +241 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/combobox.tsx +243 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/command.tsx +184 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/context-menu.tsx +252 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/date-picker.tsx +323 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/datetime-picker.tsx +896 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/drawer.tsx +135 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/empty.tsx +104 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/field.tsx +248 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/form.tsx +168 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/hover-card.tsx +44 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/input-group.tsx +170 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/input-otp.tsx +77 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/item.tsx +193 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/kbd.tsx +28 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/menubar.tsx +276 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/multi-select.tsx +381 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/multi-selector.tsx +641 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/native-select.tsx +48 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/navigation-menu.tsx +168 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/pagination.tsx +127 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/popover.tsx +48 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/progress.tsx +31 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/radio-group.tsx +45 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/resizable.tsx +55 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/scroll-area.tsx +58 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/spinner.tsx +27 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/switch.tsx +31 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/tabs.tsx +66 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/textarea.tsx +18 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/toggle-group.tsx +83 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/toggle.tsx +47 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/user-menu.tsx +61 -0
- package/registry/admin-apps/nextjs/variants/base/src/lib/ability.ts +90 -0
- package/registry/admin-apps/nextjs/variants/base/src/lib/auth-client.ts +10 -0
- package/registry/admin-apps/nextjs/variants/base/src/lib/env.ts +10 -0
- package/registry/admin-apps/nextjs/variants/base/src/lib/server-token-storage.ts +33 -0
- package/registry/admin-apps/nextjs/variants/base/src/lib/stores/auth-store.ts +117 -0
- package/registry/admin-apps/nextjs/variants/base/src/lib/stores/store-context.tsx +50 -0
- package/registry/admin-apps/nextjs/variants/base/src/proxy.ts +81 -0
- package/registry/admin-apps/nextjs/variants/base/src/routes.ts +7 -0
- package/registry/admin-apps/nextjs/variants/workspaces/.env.example +7 -0
- package/registry/admin-apps/nextjs/variants/workspaces/Dockerfile +32 -0
- package/registry/admin-apps/nextjs/variants/workspaces/package.json +48 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/account/page.tsx +360 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/audit-log/page.tsx +185 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/countries/[id]/edit/page.tsx +296 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/countries/[id]/page.tsx +194 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/countries/country-schema.ts +27 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/countries/new/page.tsx +242 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/countries/page.tsx +373 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/customers/[id]/edit/page.tsx +306 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/customers/[id]/page.tsx +219 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/customers/customer-schema.ts +36 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/customers/new/page.tsx +251 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/customers/page.tsx +406 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/dashboard/page.tsx +146 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/languages/[id]/edit/page.tsx +240 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/languages/[id]/page.tsx +186 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/languages/language-schema.ts +26 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/languages/new/page.tsx +189 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/languages/page.tsx +348 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/layout.tsx +96 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/members/page.tsx +263 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/permissions/[id]/edit/page.tsx +264 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/permissions/[id]/page.tsx +126 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/permissions/new/page.tsx +238 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/permissions/page.tsx +294 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/permissions/permission-schema.ts +46 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/roles/[id]/edit/page.tsx +248 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/roles/[id]/page.tsx +186 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/roles/new/page.tsx +205 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/roles/page.tsx +485 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/roles/role-schema.ts +23 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/users/[id]/page.tsx +521 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/users/new/page.tsx +342 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/users/page.tsx +414 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/users/user-schema.ts +44 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/workspaces/new/page.tsx +66 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/layout.tsx +31 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/login/page.tsx +110 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/page.tsx +33 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/components/app-sidebar.tsx +180 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/components/change-password-dialog.tsx +119 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/components/nav-user.tsx +105 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/components/permission-required.tsx +32 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/components/photo-upload.tsx +226 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/components/ui/calendar.tsx +218 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/components/ui/datetime-picker.tsx +896 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/components/ui/popover.tsx +48 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/components/user-menu.tsx +67 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/components/workspace-switcher.tsx +57 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/lib/ability.ts +101 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/lib/active-workspace.ts +38 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/lib/auth-client.ts +17 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/lib/env.ts +4 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/lib/stores/auth-store.ts +139 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/lib/stores/store-context.tsx +66 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/lib/stores/workspace-store.ts +89 -0
- package/registry/admin-apps/react/shared/components.json +25 -0
- package/registry/admin-apps/react/shared/postcss.config.js +5 -0
- package/registry/admin-apps/react/shared/src/components/alert-modal.tsx +30 -0
- package/registry/admin-apps/react/shared/src/components/loader/skeleton.tsx +39 -0
- package/registry/admin-apps/react/shared/src/components/loader/table-skeleton-loader.tsx +57 -0
- package/registry/admin-apps/react/shared/src/components/nav-user.tsx +103 -0
- package/registry/admin-apps/react/shared/src/components/role-multi-select.tsx +69 -0
- package/registry/admin-apps/react/shared/src/components/theme-toggle.tsx +24 -0
- package/registry/admin-apps/react/shared/src/components/ui/accordion.tsx +66 -0
- package/registry/admin-apps/react/shared/src/components/ui/alert-dialog.tsx +157 -0
- package/registry/admin-apps/react/shared/src/components/ui/alert.tsx +78 -0
- package/registry/admin-apps/react/shared/src/components/ui/avatar.tsx +53 -0
- package/registry/admin-apps/react/shared/src/components/ui/badge.tsx +51 -0
- package/registry/admin-apps/react/shared/src/components/ui/breadcrumb.tsx +109 -0
- package/registry/admin-apps/react/shared/src/components/ui/button-group.tsx +83 -0
- package/registry/admin-apps/react/shared/src/components/ui/button.tsx +67 -0
- package/registry/admin-apps/react/shared/src/components/ui/calendar.tsx +218 -0
- package/registry/admin-apps/react/shared/src/components/ui/card.tsx +103 -0
- package/registry/admin-apps/react/shared/src/components/ui/carousel.tsx +241 -0
- package/registry/admin-apps/react/shared/src/components/ui/checkbox.tsx +32 -0
- package/registry/admin-apps/react/shared/src/components/ui/collapsible.tsx +33 -0
- package/registry/admin-apps/react/shared/src/components/ui/context-menu.tsx +252 -0
- package/registry/admin-apps/react/shared/src/components/ui/data-table.tsx +107 -0
- package/registry/admin-apps/react/shared/src/components/ui/datetime-picker.tsx +896 -0
- package/registry/admin-apps/react/shared/src/components/ui/dialog.tsx +143 -0
- package/registry/admin-apps/react/shared/src/components/ui/drawer.tsx +135 -0
- package/registry/admin-apps/react/shared/src/components/ui/dropdown-menu.tsx +234 -0
- package/registry/admin-apps/react/shared/src/components/ui/empty.tsx +104 -0
- package/registry/admin-apps/react/shared/src/components/ui/field.tsx +248 -0
- package/registry/admin-apps/react/shared/src/components/ui/form.tsx +168 -0
- package/registry/admin-apps/react/shared/src/components/ui/heading.tsx +16 -0
- package/registry/admin-apps/react/shared/src/components/ui/hover-card.tsx +44 -0
- package/registry/admin-apps/react/shared/src/components/ui/input-group.tsx +170 -0
- package/registry/admin-apps/react/shared/src/components/ui/input-otp.tsx +77 -0
- package/registry/admin-apps/react/shared/src/components/ui/input.tsx +19 -0
- package/registry/admin-apps/react/shared/src/components/ui/item.tsx +193 -0
- package/registry/admin-apps/react/shared/src/components/ui/kbd.tsx +28 -0
- package/registry/admin-apps/react/shared/src/components/ui/label.tsx +24 -0
- package/registry/admin-apps/react/shared/src/components/ui/menubar.tsx +276 -0
- package/registry/admin-apps/react/shared/src/components/ui/modal.tsx +29 -0
- package/registry/admin-apps/react/shared/src/components/ui/multi-select.tsx +381 -0
- package/registry/admin-apps/react/shared/src/components/ui/multi-selector.tsx +641 -0
- package/registry/admin-apps/react/shared/src/components/ui/native-select.tsx +48 -0
- package/registry/admin-apps/react/shared/src/components/ui/navigation-menu.tsx +168 -0
- package/registry/admin-apps/react/shared/src/components/ui/pagination.tsx +127 -0
- package/registry/admin-apps/react/shared/src/components/ui/popover.tsx +48 -0
- package/registry/admin-apps/react/shared/src/components/ui/progress.tsx +31 -0
- package/registry/admin-apps/react/shared/src/components/ui/radio-group.tsx +45 -0
- package/registry/admin-apps/react/shared/src/components/ui/resizable.tsx +55 -0
- package/registry/admin-apps/react/shared/src/components/ui/scroll-area.tsx +58 -0
- package/registry/admin-apps/react/shared/src/components/ui/select.tsx +192 -0
- package/registry/admin-apps/react/shared/src/components/ui/separator.tsx +28 -0
- package/registry/admin-apps/react/shared/src/components/ui/sheet.tsx +135 -0
- package/registry/admin-apps/react/shared/src/components/ui/sidebar.tsx +567 -0
- package/registry/admin-apps/react/shared/src/components/ui/skeleton.tsx +13 -0
- package/registry/admin-apps/react/shared/src/components/ui/spinner.tsx +27 -0
- package/registry/admin-apps/react/shared/src/components/ui/switch.tsx +31 -0
- package/registry/admin-apps/react/shared/src/components/ui/table.tsx +116 -0
- package/registry/admin-apps/react/shared/src/components/ui/tabs.tsx +66 -0
- package/registry/admin-apps/react/shared/src/components/ui/textarea.tsx +18 -0
- package/registry/admin-apps/react/shared/src/components/ui/toggle-group.tsx +83 -0
- package/registry/admin-apps/react/shared/src/components/ui/toggle.tsx +47 -0
- package/registry/admin-apps/react/shared/src/components/ui/tooltip.tsx +61 -0
- package/registry/admin-apps/react/shared/src/hooks/use-mobile.ts +17 -0
- package/registry/admin-apps/react/shared/src/lib/cn.ts +6 -0
- package/registry/admin-apps/react/shared/src/lib/error.ts +23 -0
- package/registry/admin-apps/react/shared/src/lib/query-client.tsx +8 -0
- package/registry/admin-apps/react/shared/src/lib/utils.ts +1 -0
- package/registry/admin-apps/react/shared/src/vite-env.d.ts +9 -0
- package/registry/admin-apps/react/shared/tsconfig.json +24 -0
- package/registry/admin-apps/react/variants/base/.env.example +3 -0
- package/registry/admin-apps/react/variants/base/Dockerfile +32 -0
- package/registry/admin-apps/react/variants/base/index.html +12 -0
- package/registry/admin-apps/react/variants/base/package.json +61 -0
- package/registry/admin-apps/react/variants/base/src/App.tsx +103 -0
- package/registry/admin-apps/react/variants/base/src/components/app-sidebar.tsx +181 -0
- package/registry/admin-apps/react/variants/base/src/components/breadcrumb.tsx +47 -0
- package/registry/admin-apps/react/variants/base/src/components/change-password-dialog.tsx +102 -0
- package/registry/admin-apps/react/variants/base/src/components/form-error-alert.tsx +23 -0
- package/registry/admin-apps/react/variants/base/src/components/layout/AppShell.tsx +36 -0
- package/registry/admin-apps/react/variants/base/src/components/layout/RequireAuth.tsx +66 -0
- package/registry/admin-apps/react/variants/base/src/components/permission-group-select.tsx +99 -0
- package/registry/admin-apps/react/variants/base/src/components/permission-group-view.tsx +53 -0
- package/registry/admin-apps/react/variants/base/src/components/photo-upload.tsx +221 -0
- package/registry/admin-apps/react/variants/base/src/components/ui/combobox.tsx +243 -0
- package/registry/admin-apps/react/variants/base/src/components/ui/command.tsx +177 -0
- package/registry/admin-apps/react/variants/base/src/components/ui/date-picker.tsx +322 -0
- package/registry/admin-apps/react/variants/base/src/components/ui/sonner.tsx +34 -0
- package/registry/admin-apps/react/variants/base/src/components/user-menu.tsx +65 -0
- package/registry/admin-apps/react/variants/base/src/hooks/use-live-audit-feed.ts +50 -0
- package/registry/admin-apps/react/variants/base/src/index.css +161 -0
- package/registry/admin-apps/react/variants/base/src/lib/ability.ts +72 -0
- package/registry/admin-apps/react/variants/base/src/lib/auth-client.ts +9 -0
- package/registry/admin-apps/react/variants/base/src/lib/theme.tsx +64 -0
- package/registry/admin-apps/react/variants/base/src/lib/token-storage.ts +34 -0
- package/registry/admin-apps/react/variants/base/src/main.tsx +24 -0
- package/registry/admin-apps/react/variants/base/src/pages/AccountPage.tsx +368 -0
- package/registry/admin-apps/react/variants/base/src/pages/AddCountryPage.tsx +227 -0
- package/registry/admin-apps/react/variants/base/src/pages/AddCustomerPage.tsx +220 -0
- package/registry/admin-apps/react/variants/base/src/pages/AddLanguagePage.tsx +167 -0
- package/registry/admin-apps/react/variants/base/src/pages/AddRolePage.tsx +187 -0
- package/registry/admin-apps/react/variants/base/src/pages/AddUserPage.tsx +328 -0
- package/registry/admin-apps/react/variants/base/src/pages/AuditLogPage.tsx +180 -0
- package/registry/admin-apps/react/variants/base/src/pages/CountriesPage.tsx +371 -0
- package/registry/admin-apps/react/variants/base/src/pages/CountryDetailPage.tsx +209 -0
- package/registry/admin-apps/react/variants/base/src/pages/CustomerDetailPage.tsx +231 -0
- package/registry/admin-apps/react/variants/base/src/pages/CustomersPage.tsx +404 -0
- package/registry/admin-apps/react/variants/base/src/pages/DashboardPage.tsx +190 -0
- package/registry/admin-apps/react/variants/base/src/pages/EditCountryPage.tsx +279 -0
- package/registry/admin-apps/react/variants/base/src/pages/EditCustomerPage.tsx +272 -0
- package/registry/admin-apps/react/variants/base/src/pages/EditLanguagePage.tsx +216 -0
- package/registry/admin-apps/react/variants/base/src/pages/EditRolePage.tsx +232 -0
- package/registry/admin-apps/react/variants/base/src/pages/EditUserPage.tsx +308 -0
- package/registry/admin-apps/react/variants/base/src/pages/LanguageDetailPage.tsx +201 -0
- package/registry/admin-apps/react/variants/base/src/pages/LanguagesPage.tsx +346 -0
- package/registry/admin-apps/react/variants/base/src/pages/LoginPage.tsx +149 -0
- package/registry/admin-apps/react/variants/base/src/pages/PermissionDetailPage.tsx +135 -0
- package/registry/admin-apps/react/variants/base/src/pages/PermissionsPage.tsx +331 -0
- package/registry/admin-apps/react/variants/base/src/pages/RoleDetailPage.tsx +167 -0
- package/registry/admin-apps/react/variants/base/src/pages/RolesPage.tsx +613 -0
- package/registry/admin-apps/react/variants/base/src/pages/UserDetailPage.tsx +268 -0
- package/registry/admin-apps/react/variants/base/src/pages/UsersPage.tsx +423 -0
- package/registry/admin-apps/react/variants/base/src/pages/country-schema.ts +29 -0
- package/registry/admin-apps/react/variants/base/src/pages/customer-schema.ts +37 -0
- package/registry/admin-apps/react/variants/base/src/pages/language-schema.ts +28 -0
- package/registry/admin-apps/react/variants/base/src/pages/user-schema.ts +36 -0
- package/registry/admin-apps/react/variants/base/src/stores/auth-store.ts +144 -0
- package/registry/admin-apps/react/variants/base/src/stores/store-context.tsx +15 -0
- package/registry/admin-apps/react/variants/base/vite.config.ts +15 -0
- package/registry/admin-apps/react/variants/workspaces/.env.example +5 -0
- package/registry/admin-apps/react/variants/workspaces/Dockerfile +32 -0
- package/registry/admin-apps/react/variants/workspaces/index.html +12 -0
- package/registry/admin-apps/react/variants/workspaces/package.json +61 -0
- package/registry/admin-apps/react/variants/workspaces/src/App.tsx +118 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/app-sidebar.tsx +186 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/breadcrumb.tsx +39 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/change-password-dialog.tsx +111 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/form-error-alert.tsx +23 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/layout/AppShell.tsx +37 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/layout/CreateWorkspaceDialog.tsx +66 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/layout/RequireAuth.tsx +107 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/layout/WorkspaceSwitcher.tsx +52 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/permission-group-select.tsx +99 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/permission-group-view.tsx +53 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/photo-upload.tsx +223 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/ui/combobox.tsx +243 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/ui/command.tsx +184 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/ui/date-picker.tsx +323 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/ui/sonner.tsx +34 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/user-menu.tsx +66 -0
- package/registry/admin-apps/react/variants/workspaces/src/hooks/use-live-audit-feed.ts +50 -0
- package/registry/admin-apps/react/variants/workspaces/src/index.css +162 -0
- package/registry/admin-apps/react/variants/workspaces/src/lib/ability.ts +78 -0
- package/registry/admin-apps/react/variants/workspaces/src/lib/auth-client.ts +27 -0
- package/registry/admin-apps/react/variants/workspaces/src/lib/permission-groups.ts +22 -0
- package/registry/admin-apps/react/variants/workspaces/src/lib/theme.tsx +64 -0
- package/registry/admin-apps/react/variants/workspaces/src/lib/token-storage.ts +37 -0
- package/registry/admin-apps/react/variants/workspaces/src/lib/user-fields.ts +13 -0
- package/registry/admin-apps/react/variants/workspaces/src/main.tsx +24 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/AccountPage.tsx +351 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/AddCountryPage.tsx +230 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/AddCustomerPage.tsx +224 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/AddLanguagePage.tsx +170 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/AddUserPage.tsx +297 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/AuditLogPage.tsx +195 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/CountriesPage.tsx +378 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/CountryDetailPage.tsx +215 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/CustomerDetailPage.tsx +237 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/CustomersPage.tsx +411 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/DashboardPage.tsx +191 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/EditCountryPage.tsx +285 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/EditCustomerPage.tsx +279 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/EditLanguagePage.tsx +222 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/LanguageDetailPage.tsx +207 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/LanguagesPage.tsx +353 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/LoginPage.tsx +149 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/MembersPage.tsx +349 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/PermissionDetailPage.tsx +123 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/PermissionsPage.tsx +317 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/RoleDetailPage.tsx +207 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/RolesPage.tsx +617 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/UserDetailPage.tsx +405 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/UsersPage.tsx +425 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/country-schema.ts +29 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/customer-schema.ts +34 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/language-schema.ts +27 -0
- package/registry/admin-apps/react/variants/workspaces/src/stores/auth-store.ts +169 -0
- package/registry/admin-apps/react/variants/workspaces/src/stores/store-context.tsx +43 -0
- package/registry/admin-apps/react/variants/workspaces/src/stores/workspace-store.ts +120 -0
- package/registry/admin-apps/react/variants/workspaces/vite.config.ts +15 -0
- package/registry/combos/express-drizzle/package.json +32 -0
- package/registry/combos/express-drizzle/scripts/materialize.mjs +104 -0
- package/registry/combos/express-drizzle/scripts/migrate.mjs +41 -0
- package/registry/combos/express-drizzle/scripts/prove-cycle.mjs +29 -0
- package/registry/combos/express-drizzle/scripts/seed.mjs +25 -0
- package/registry/combos/express-drizzle/shared/drizzle.config.ts +67 -0
- package/registry/combos/express-drizzle/shared/src/ability.ts +44 -0
- package/registry/combos/express-drizzle/shared/src/auth-core-error.middleware.ts +31 -0
- package/registry/combos/express-drizzle/shared/src/auth.config.ts +63 -0
- package/registry/combos/express-drizzle/shared/src/auth.middleware.ts +38 -0
- package/registry/combos/express-drizzle/shared/src/auth.router.ts +266 -0
- package/registry/combos/express-drizzle/shared/src/db.ts +12 -0
- package/registry/combos/express-drizzle/shared/src/http-error.ts +18 -0
- package/registry/combos/express-drizzle/shared/src/id.helper.ts +22 -0
- package/registry/combos/express-drizzle/shared/src/key-provider.ts +52 -0
- package/registry/combos/express-drizzle/shared/src/oauth.repository.ts +47 -0
- package/registry/combos/express-drizzle/shared/src/openapi-fragment.ts +49 -0
- package/registry/combos/express-drizzle/shared/src/openapi-spec.ts +493 -0
- package/registry/combos/express-drizzle/shared/src/pagination.ts +30 -0
- package/registry/combos/express-drizzle/shared/src/password-reset.repository.ts +35 -0
- package/registry/combos/express-drizzle/shared/src/permission-cache.ts +191 -0
- package/registry/combos/express-drizzle/shared/src/rate-limit.store.ts +24 -0
- package/registry/combos/express-drizzle/shared/src/request-context.ts +35 -0
- package/registry/combos/express-drizzle/shared/src/response-envelope.middleware.ts +46 -0
- package/registry/combos/express-drizzle/shared/src/route-tiers.ts +148 -0
- package/registry/combos/express-drizzle/shared/src/session.repository.ts +130 -0
- package/registry/combos/express-drizzle/shared/src/two-factor.repository.ts +38 -0
- package/registry/combos/express-drizzle/test/bootstrap.ts +40 -0
- package/registry/combos/express-drizzle/test/harness.ts +187 -0
- package/registry/combos/express-drizzle/test/prove-cycle.ts +789 -0
- package/registry/combos/express-drizzle/tsconfig.json +20 -0
- package/registry/combos/express-drizzle/variants/base/.env.example +25 -0
- package/registry/combos/express-drizzle/variants/base/drizzle/0000_init.sql +219 -0
- package/registry/combos/express-drizzle/variants/base/drizzle/0001_add_is_active_field.sql +1 -0
- package/registry/combos/express-drizzle/variants/base/drizzle/meta/0000_snapshot.json +1861 -0
- package/registry/combos/express-drizzle/variants/base/drizzle/meta/0001_snapshot.json +1868 -0
- package/registry/combos/express-drizzle/variants/base/drizzle/meta/_journal.json +20 -0
- package/registry/combos/express-drizzle/variants/base/src/admin.router.ts +307 -0
- package/registry/combos/express-drizzle/variants/base/src/audit-log.repository.ts +93 -0
- package/registry/combos/express-drizzle/variants/base/src/auth.service.ts +528 -0
- package/registry/combos/express-drizzle/variants/base/src/authz.middleware.ts +58 -0
- package/registry/combos/express-drizzle/variants/base/src/create-auth-app.ts +78 -0
- package/registry/combos/express-drizzle/variants/base/src/openapi-admin.ts +565 -0
- package/registry/combos/express-drizzle/variants/base/src/rbac.defaults.ts +218 -0
- package/registry/combos/express-drizzle/variants/base/src/rbac.repository.ts +606 -0
- package/registry/combos/express-drizzle/variants/base/src/schema.ts +425 -0
- package/registry/combos/express-drizzle/variants/base/src/seed.ts +138 -0
- package/registry/combos/express-drizzle/variants/base/test/variant-hooks.ts +179 -0
- package/registry/combos/express-drizzle/variants/workspaces/.env.example +30 -0
- package/registry/combos/express-drizzle/variants/workspaces/drizzle/0000_init.sql +251 -0
- package/registry/combos/express-drizzle/variants/workspaces/drizzle/0001_add_is_active_field.sql +1 -0
- package/registry/combos/express-drizzle/variants/workspaces/drizzle/meta/0000_snapshot.json +2155 -0
- package/registry/combos/express-drizzle/variants/workspaces/drizzle/meta/0001_snapshot.json +2162 -0
- package/registry/combos/express-drizzle/variants/workspaces/drizzle/meta/_journal.json +20 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/admin.router.ts +323 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/audit-log.repository.ts +105 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/auth.service.ts +545 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/authz.middleware.ts +94 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/create-auth-app.ts +87 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/openapi-admin.ts +596 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/openapi-workspace.ts +183 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/rbac.defaults.ts +248 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/rbac.repository.ts +698 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/schema.ts +487 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/seed.ts +173 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/workspace.repository.ts +254 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/workspace.router.ts +123 -0
- package/registry/combos/express-drizzle/variants/workspaces/test/variant-hooks.ts +370 -0
- package/registry/combos/express-prisma/package.json +32 -0
- package/registry/combos/express-prisma/scripts/materialize.mjs +84 -0
- package/registry/combos/express-prisma/scripts/migrate.mjs +32 -0
- package/registry/combos/express-prisma/scripts/prove-cycle.mjs +30 -0
- package/registry/combos/express-prisma/scripts/seed.mjs +24 -0
- package/registry/combos/express-prisma/shared/prisma.config.ts +38 -0
- package/registry/combos/express-prisma/shared/src/ability.ts +44 -0
- package/registry/combos/express-prisma/shared/src/auth-core-error.middleware.ts +31 -0
- package/registry/combos/express-prisma/shared/src/auth.config.ts +63 -0
- package/registry/combos/express-prisma/shared/src/auth.middleware.ts +44 -0
- package/registry/combos/express-prisma/shared/src/auth.router.ts +269 -0
- package/registry/combos/express-prisma/shared/src/http-error.ts +18 -0
- package/registry/combos/express-prisma/shared/src/id.helper.ts +22 -0
- package/registry/combos/express-prisma/shared/src/key-provider.ts +52 -0
- package/registry/combos/express-prisma/shared/src/oauth.repository.ts +33 -0
- package/registry/combos/express-prisma/shared/src/openapi-fragment.ts +49 -0
- package/registry/combos/express-prisma/shared/src/openapi-spec.ts +487 -0
- package/registry/combos/express-prisma/shared/src/pagination.ts +30 -0
- package/registry/combos/express-prisma/shared/src/password-reset.repository.ts +29 -0
- package/registry/combos/express-prisma/shared/src/permission-cache.ts +191 -0
- package/registry/combos/express-prisma/shared/src/rate-limit.store.ts +21 -0
- package/registry/combos/express-prisma/shared/src/request-context.ts +35 -0
- package/registry/combos/express-prisma/shared/src/response-envelope.middleware.ts +46 -0
- package/registry/combos/express-prisma/shared/src/route-tiers.ts +148 -0
- package/registry/combos/express-prisma/shared/src/session.repository.ts +140 -0
- package/registry/combos/express-prisma/shared/src/two-factor.repository.ts +30 -0
- package/registry/combos/express-prisma/test/bootstrap.ts +45 -0
- package/registry/combos/express-prisma/test/harness.ts +187 -0
- package/registry/combos/express-prisma/test/prove-cycle.ts +793 -0
- package/registry/combos/express-prisma/tsconfig.json +21 -0
- package/registry/combos/express-prisma/variants/base/.env.example +25 -0
- package/registry/combos/express-prisma/variants/base/prisma/migrations/20260810165456_init/migration.sql +370 -0
- package/registry/combos/express-prisma/variants/base/prisma/migrations/20260811151048_add_is_active_field/migration.sql +2 -0
- package/registry/combos/express-prisma/variants/base/prisma/migrations/migration_lock.toml +3 -0
- package/registry/combos/express-prisma/variants/base/prisma/schema/simple-auth-kit.prisma +332 -0
- package/registry/combos/express-prisma/variants/base/src/admin.router.ts +314 -0
- package/registry/combos/express-prisma/variants/base/src/audit-log.repository.ts +91 -0
- package/registry/combos/express-prisma/variants/base/src/auth.service.ts +521 -0
- package/registry/combos/express-prisma/variants/base/src/authz.middleware.ts +58 -0
- package/registry/combos/express-prisma/variants/base/src/create-auth-app.ts +93 -0
- package/registry/combos/express-prisma/variants/base/src/openapi-admin.ts +569 -0
- package/registry/combos/express-prisma/variants/base/src/rbac.defaults.ts +195 -0
- package/registry/combos/express-prisma/variants/base/src/rbac.repository.ts +484 -0
- package/registry/combos/express-prisma/variants/base/src/seed.ts +115 -0
- package/registry/combos/express-prisma/variants/base/test/variant-hooks.ts +159 -0
- package/registry/combos/express-prisma/variants/workspaces/.env.example +30 -0
- package/registry/combos/express-prisma/variants/workspaces/prisma/migrations/20260810165502_init/migration.sql +443 -0
- package/registry/combos/express-prisma/variants/workspaces/prisma/migrations/20260811151051_add_is_active_field/migration.sql +2 -0
- package/registry/combos/express-prisma/variants/workspaces/prisma/migrations/migration_lock.toml +3 -0
- package/registry/combos/express-prisma/variants/workspaces/prisma/schema/simple-auth-kit.prisma +394 -0
- package/registry/combos/express-prisma/variants/workspaces/src/admin.router.ts +330 -0
- package/registry/combos/express-prisma/variants/workspaces/src/audit-log.repository.ts +102 -0
- package/registry/combos/express-prisma/variants/workspaces/src/auth.service.ts +542 -0
- package/registry/combos/express-prisma/variants/workspaces/src/authz.middleware.ts +94 -0
- package/registry/combos/express-prisma/variants/workspaces/src/create-auth-app.ts +99 -0
- package/registry/combos/express-prisma/variants/workspaces/src/openapi-admin.ts +601 -0
- package/registry/combos/express-prisma/variants/workspaces/src/openapi-workspace.ts +183 -0
- package/registry/combos/express-prisma/variants/workspaces/src/rbac.defaults.ts +222 -0
- package/registry/combos/express-prisma/variants/workspaces/src/rbac.repository.ts +527 -0
- package/registry/combos/express-prisma/variants/workspaces/src/seed.ts +147 -0
- package/registry/combos/express-prisma/variants/workspaces/src/workspace.repository.ts +188 -0
- package/registry/combos/express-prisma/variants/workspaces/src/workspace.router.ts +123 -0
- package/registry/combos/express-prisma/variants/workspaces/test/variant-hooks.ts +358 -0
- package/registry/combos/nestjs-drizzle/package.json +36 -0
- package/registry/combos/nestjs-drizzle/scripts/materialize.mjs +104 -0
- package/registry/combos/nestjs-drizzle/scripts/migrate.mjs +34 -0
- package/registry/combos/nestjs-drizzle/scripts/prove-cycle.mjs +29 -0
- package/registry/combos/nestjs-drizzle/scripts/seed.mjs +24 -0
- package/registry/combos/nestjs-drizzle/shared/drizzle.config.ts +67 -0
- package/registry/combos/nestjs-drizzle/shared/src/ability.guard.ts +46 -0
- package/registry/combos/nestjs-drizzle/shared/src/ability.ts +44 -0
- package/registry/combos/nestjs-drizzle/shared/src/auth-core-error.filter.ts +32 -0
- package/registry/combos/nestjs-drizzle/shared/src/auth.config.ts +65 -0
- package/registry/combos/nestjs-drizzle/shared/src/auth.controller.ts +299 -0
- package/registry/combos/nestjs-drizzle/shared/src/auth.guard.ts +34 -0
- package/registry/combos/nestjs-drizzle/shared/src/db.ts +12 -0
- package/registry/combos/nestjs-drizzle/shared/src/dto/auth.dto.ts +429 -0
- package/registry/combos/nestjs-drizzle/shared/src/id.helper.ts +22 -0
- package/registry/combos/nestjs-drizzle/shared/src/key-provider.ts +51 -0
- package/registry/combos/nestjs-drizzle/shared/src/oauth.repository.ts +36 -0
- package/registry/combos/nestjs-drizzle/shared/src/pagination.ts +30 -0
- package/registry/combos/nestjs-drizzle/shared/src/password-reset.repository.ts +29 -0
- package/registry/combos/nestjs-drizzle/shared/src/permission-cache.ts +196 -0
- package/registry/combos/nestjs-drizzle/shared/src/rate-limit.store.ts +26 -0
- package/registry/combos/nestjs-drizzle/shared/src/request-context.ts +25 -0
- package/registry/combos/nestjs-drizzle/shared/src/response.interceptor.ts +48 -0
- package/registry/combos/nestjs-drizzle/shared/src/route-tiers.ts +144 -0
- package/registry/combos/nestjs-drizzle/shared/src/session.repository.ts +132 -0
- package/registry/combos/nestjs-drizzle/shared/src/two-factor.repository.ts +35 -0
- package/registry/combos/nestjs-drizzle/test/bootstrap.ts +49 -0
- package/registry/combos/nestjs-drizzle/test/harness.ts +187 -0
- package/registry/combos/nestjs-drizzle/test/prove-cycle.ts +795 -0
- package/registry/combos/nestjs-drizzle/tsconfig.json +20 -0
- package/registry/combos/nestjs-drizzle/variants/base/.env.example +25 -0
- package/registry/combos/nestjs-drizzle/variants/base/drizzle/0000_init.sql +219 -0
- package/registry/combos/nestjs-drizzle/variants/base/drizzle/0001_add_is_active_field.sql +1 -0
- package/registry/combos/nestjs-drizzle/variants/base/drizzle/0002_colossal_true_believers.sql +3 -0
- package/registry/combos/nestjs-drizzle/variants/base/drizzle/meta/0000_snapshot.json +1861 -0
- package/registry/combos/nestjs-drizzle/variants/base/drizzle/meta/0001_snapshot.json +1868 -0
- package/registry/combos/nestjs-drizzle/variants/base/drizzle/meta/0002_snapshot.json +1887 -0
- package/registry/combos/nestjs-drizzle/variants/base/drizzle/meta/_journal.json +27 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/admin.controller.ts +361 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/audit-log.repository.ts +90 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/auth.module.ts +83 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/auth.service.ts +544 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/authz.guard.ts +53 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/dto/admin.dto.ts +130 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/rbac.defaults.ts +218 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/rbac.repository.ts +633 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/schema.ts +427 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/seed.ts +138 -0
- package/registry/combos/nestjs-drizzle/variants/base/test/variant-hooks.ts +179 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/.env.example +30 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/0000_init.sql +251 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/0001_add_is_active_field.sql +1 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/0002_calm_tyger_tiger.sql +3 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/meta/0000_snapshot.json +2155 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/meta/0001_snapshot.json +2162 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/meta/0002_snapshot.json +2181 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/meta/_journal.json +27 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/admin.controller.ts +383 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/audit-log.repository.ts +102 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/auth.module.ts +87 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/auth.service.ts +553 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/authz.guard.ts +87 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/dto/admin.dto.ts +137 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/dto/workspace.dto.ts +60 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/rbac.defaults.ts +248 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/rbac.repository.ts +719 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/schema.ts +489 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/seed.ts +174 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/workspace.controller.ts +123 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/workspace.repository.ts +255 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/test/variant-hooks.ts +370 -0
- package/registry/combos/nestjs-prisma/package.json +41 -0
- package/registry/combos/nestjs-prisma/scripts/materialize.mjs +84 -0
- package/registry/combos/nestjs-prisma/scripts/migrate.mjs +32 -0
- package/registry/combos/nestjs-prisma/scripts/prove-cycle.mjs +30 -0
- package/registry/combos/nestjs-prisma/scripts/seed.mjs +24 -0
- package/registry/combos/nestjs-prisma/shared/prisma.config.ts +38 -0
- package/registry/combos/nestjs-prisma/shared/src/ability.guard.ts +29 -0
- package/registry/combos/nestjs-prisma/shared/src/ability.ts +23 -0
- package/registry/combos/nestjs-prisma/shared/src/auth-core-error.filter.ts +32 -0
- package/registry/combos/nestjs-prisma/shared/src/auth.config.ts +71 -0
- package/registry/combos/nestjs-prisma/shared/src/auth.controller.ts +303 -0
- package/registry/combos/nestjs-prisma/shared/src/auth.guard.ts +31 -0
- package/registry/combos/nestjs-prisma/shared/src/dto/auth.dto.ts +438 -0
- package/registry/combos/nestjs-prisma/shared/src/id.helper.ts +22 -0
- package/registry/combos/nestjs-prisma/shared/src/key-provider.ts +42 -0
- package/registry/combos/nestjs-prisma/shared/src/oauth.repository.ts +32 -0
- package/registry/combos/nestjs-prisma/shared/src/pagination.ts +30 -0
- package/registry/combos/nestjs-prisma/shared/src/password-reset.repository.ts +29 -0
- package/registry/combos/nestjs-prisma/shared/src/permission-cache.ts +140 -0
- package/registry/combos/nestjs-prisma/shared/src/rate-limit.store.ts +26 -0
- package/registry/combos/nestjs-prisma/shared/src/request-context.ts +19 -0
- package/registry/combos/nestjs-prisma/shared/src/response.interceptor.ts +48 -0
- package/registry/combos/nestjs-prisma/shared/src/route-tiers.ts +102 -0
- package/registry/combos/nestjs-prisma/shared/src/session.repository.ts +129 -0
- package/registry/combos/nestjs-prisma/shared/src/two-factor.repository.ts +29 -0
- package/registry/combos/nestjs-prisma/test/bootstrap.ts +54 -0
- package/registry/combos/nestjs-prisma/test/harness.ts +186 -0
- package/registry/combos/nestjs-prisma/test/prove-cycle.ts +795 -0
- package/registry/combos/nestjs-prisma/tsconfig.json +21 -0
- package/registry/combos/nestjs-prisma/variants/base/.env.example +31 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260810084937_init/migration.sql +269 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260810122546_add_profile_audit_soft_delete/migration.sql +96 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260810123620_add_display_name/migration.sql +3 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260810131421_drop_signing_keys/migration.sql +3 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260810161515_bigint_ids/migration.sql +305 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260810162120_drop_name_unique/migration.sql +6 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260811145755_add_is_active_field/migration.sql +2 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260811183529_add_countries_languages_customers/migration.sql +146 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260812121128_add_user_profile_details/migration.sql +4 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/migration_lock.toml +3 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/schema/simple-auth-kit.prisma +437 -0
- package/registry/combos/nestjs-prisma/variants/base/src/admin.controller.ts +684 -0
- package/registry/combos/nestjs-prisma/variants/base/src/audit-log.gateway.ts +51 -0
- package/registry/combos/nestjs-prisma/variants/base/src/audit-log.repository.ts +111 -0
- package/registry/combos/nestjs-prisma/variants/base/src/auth.module.ts +87 -0
- package/registry/combos/nestjs-prisma/variants/base/src/auth.service.ts +618 -0
- package/registry/combos/nestjs-prisma/variants/base/src/authz.guard.ts +34 -0
- package/registry/combos/nestjs-prisma/variants/base/src/country.repository.ts +149 -0
- package/registry/combos/nestjs-prisma/variants/base/src/customer.repository.ts +169 -0
- package/registry/combos/nestjs-prisma/variants/base/src/docs.ts +60 -0
- package/registry/combos/nestjs-prisma/variants/base/src/dto/admin.dto.ts +130 -0
- package/registry/combos/nestjs-prisma/variants/base/src/dto/country.dto.ts +120 -0
- package/registry/combos/nestjs-prisma/variants/base/src/dto/customer.dto.ts +147 -0
- package/registry/combos/nestjs-prisma/variants/base/src/dto/language.dto.ts +93 -0
- package/registry/combos/nestjs-prisma/variants/base/src/language.repository.ts +135 -0
- package/registry/combos/nestjs-prisma/variants/base/src/rbac.defaults.ts +193 -0
- package/registry/combos/nestjs-prisma/variants/base/src/rbac.repository.ts +514 -0
- package/registry/combos/nestjs-prisma/variants/base/src/seed.ts +89 -0
- package/registry/combos/nestjs-prisma/variants/base/test/variant-hooks.ts +135 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/.env.example +30 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810084938_init/migration.sql +308 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810123450_add_profile_audit_soft_delete/migration.sql +96 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810123632_add_display_name/migration.sql +3 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810124243_workspace_audit_soft_delete/migration.sql +25 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810131432_drop_signing_keys/migration.sql +3 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810161551_bigint_ids/migration.sql +393 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810162131_drop_name_unique/migration.sql +3 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260811145805_add_is_active_field/migration.sql +2 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260813120000_add_countries_languages_customers/migration.sql +158 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/migration_lock.toml +3 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/schema/simple-auth-kit.prisma +521 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/admin.controller.ts +733 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/audit-log.repository.ts +98 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/auth.module.ts +83 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/auth.service.ts +509 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/authz.guard.ts +69 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/country.repository.ts +158 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/customer.repository.ts +179 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/dto/admin.dto.ts +128 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/dto/country.dto.ts +120 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/dto/customer.dto.ts +147 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/dto/language.dto.ts +93 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/dto/workspace.dto.ts +60 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/language.repository.ts +143 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/rbac.defaults.ts +211 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/rbac.repository.ts +505 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/seed.ts +122 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/workspace.controller.ts +111 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/workspace.repository.ts +177 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/test/variant-hooks.ts +333 -0
- package/registry/core/crypto.ts +43 -0
- package/registry/core/oauth.ts +159 -0
- package/registry/core/package.json +20 -0
- package/registry/core/password-reset.ts +42 -0
- package/registry/core/rate-limit.ts +33 -0
- package/registry/core/rbac.ts +4 -0
- package/registry/core/session-policy.ts +96 -0
- package/registry/core/test/crypto.test.ts +25 -0
- package/registry/core/test/oauth.test.ts +213 -0
- package/registry/core/test/password-reset.test.ts +77 -0
- package/registry/core/test/rate-limit.test.ts +41 -0
- package/registry/core/test/rbac.test.ts +20 -0
- package/registry/core/test/session-policy.test.ts +209 -0
- package/registry/core/test/test-keys.ts +6 -0
- package/registry/core/test/token-service.test.ts +81 -0
- package/registry/core/test/two-factor.test.ts +72 -0
- package/registry/core/token-service.ts +143 -0
- package/registry/core/tsconfig.json +5 -0
- package/registry/core/two-factor.ts +108 -0
- package/registry/core/types.ts +127 -0
- package/registry/mobile-apps/README.md +65 -0
- package/registry/mobile-apps/bare-rn/shared/.bundle/config +2 -0
- package/registry/mobile-apps/bare-rn/shared/.eslintrc.js +4 -0
- package/registry/mobile-apps/bare-rn/shared/.prettierrc.js +5 -0
- package/registry/mobile-apps/bare-rn/shared/.watchmanconfig +1 -0
- package/registry/mobile-apps/bare-rn/shared/App.tsx +28 -0
- package/registry/mobile-apps/bare-rn/shared/Gemfile +17 -0
- package/registry/mobile-apps/bare-rn/shared/README.md +97 -0
- package/registry/mobile-apps/bare-rn/shared/__mocks__/reactNativeConfigMock.js +3 -0
- package/registry/mobile-apps/bare-rn/shared/__mocks__/styleMock.js +1 -0
- package/registry/mobile-apps/bare-rn/shared/__tests__/App.test.tsx +13 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/build.gradle +121 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/debug.keystore +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/proguard-rules.pro +10 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/AndroidManifest.xml +27 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/java/com/mobilebarern/MainActivity.kt +22 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/java/com/mobilebarern/MainApplication.kt +27 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/values/strings.xml +3 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/values/styles.xml +9 -0
- package/registry/mobile-apps/bare-rn/shared/android/build.gradle +21 -0
- package/registry/mobile-apps/bare-rn/shared/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/registry/mobile-apps/bare-rn/shared/android/gradle.properties +44 -0
- package/registry/mobile-apps/bare-rn/shared/android/gradlew +248 -0
- package/registry/mobile-apps/bare-rn/shared/android/gradlew.bat +98 -0
- package/registry/mobile-apps/bare-rn/shared/android/settings.gradle +6 -0
- package/registry/mobile-apps/bare-rn/shared/app.json +4 -0
- package/registry/mobile-apps/bare-rn/shared/babel.config.js +6 -0
- package/registry/mobile-apps/bare-rn/shared/global.css +2 -0
- package/registry/mobile-apps/bare-rn/shared/index.js +10 -0
- package/registry/mobile-apps/bare-rn/shared/ios/.xcode.env +11 -0
- package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn/AppDelegate.swift +48 -0
- package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
- package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn/Images.xcassets/Contents.json +6 -0
- package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn/Info.plist +59 -0
- package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn/LaunchScreen.storyboard +47 -0
- package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn/PrivacyInfo.xcprivacy +37 -0
- package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn.xcodeproj/project.pbxproj +475 -0
- package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn.xcodeproj/xcshareddata/xcschemes/MobileBareRn.xcscheme +88 -0
- package/registry/mobile-apps/bare-rn/shared/ios/Podfile +34 -0
- package/registry/mobile-apps/bare-rn/shared/jest.config.js +17 -0
- package/registry/mobile-apps/bare-rn/shared/metro.config.js +55 -0
- package/registry/mobile-apps/bare-rn/shared/nativewind-env.d.ts +1 -0
- package/registry/mobile-apps/bare-rn/shared/src/api/tokenStorage.ts +29 -0
- package/registry/mobile-apps/bare-rn/shared/src/components/FormInput.tsx +21 -0
- package/registry/mobile-apps/bare-rn/shared/src/components/PrimaryButton.tsx +47 -0
- package/registry/mobile-apps/bare-rn/shared/src/screens/LoginScreen.tsx +58 -0
- package/registry/mobile-apps/bare-rn/shared/src/screens/SessionsScreen.tsx +77 -0
- package/registry/mobile-apps/bare-rn/shared/src/screens/SignupScreen.tsx +54 -0
- package/registry/mobile-apps/bare-rn/shared/src/screens/TwoFactorScreen.tsx +59 -0
- package/registry/mobile-apps/bare-rn/shared/tailwind.config.js +9 -0
- package/registry/mobile-apps/bare-rn/shared/tsconfig.json +17 -0
- package/registry/mobile-apps/bare-rn/variants/base/package.json +58 -0
- package/registry/mobile-apps/bare-rn/variants/base/src/api/authClient.ts +14 -0
- package/registry/mobile-apps/bare-rn/variants/base/src/navigation/RootNavigator.tsx +58 -0
- package/registry/mobile-apps/bare-rn/variants/base/src/navigation/types.ts +15 -0
- package/registry/mobile-apps/bare-rn/variants/base/src/screens/HomeScreen.tsx +64 -0
- package/registry/mobile-apps/bare-rn/variants/base/src/store/authStore.ts +143 -0
- package/registry/mobile-apps/bare-rn/variants/workspaces/package.json +58 -0
- package/registry/mobile-apps/bare-rn/variants/workspaces/src/api/authClient.ts +22 -0
- package/registry/mobile-apps/bare-rn/variants/workspaces/src/navigation/RootNavigator.tsx +72 -0
- package/registry/mobile-apps/bare-rn/variants/workspaces/src/navigation/types.ts +18 -0
- package/registry/mobile-apps/bare-rn/variants/workspaces/src/screens/HomeScreen.tsx +85 -0
- package/registry/mobile-apps/bare-rn/variants/workspaces/src/screens/WorkspaceSelectScreen.tsx +165 -0
- package/registry/mobile-apps/bare-rn/variants/workspaces/src/store/authStore.ts +177 -0
- package/registry/mobile-apps/bare-rn/variants/workspaces/src/store/workspaceStore.ts +90 -0
- package/registry/mobile-apps/bare-rn/variants/workspaces/src/workspace/activeWorkspace.ts +55 -0
- package/registry/mobile-apps/expo/shared/App.tsx +15 -0
- package/registry/mobile-apps/expo/shared/assets/android-icon-background.png +0 -0
- package/registry/mobile-apps/expo/shared/assets/android-icon-foreground.png +0 -0
- package/registry/mobile-apps/expo/shared/assets/android-icon-monochrome.png +0 -0
- package/registry/mobile-apps/expo/shared/assets/favicon.png +0 -0
- package/registry/mobile-apps/expo/shared/assets/icon.png +0 -0
- package/registry/mobile-apps/expo/shared/assets/splash-icon.png +0 -0
- package/registry/mobile-apps/expo/shared/babel.config.js +6 -0
- package/registry/mobile-apps/expo/shared/global.css +2 -0
- package/registry/mobile-apps/expo/shared/index.ts +8 -0
- package/registry/mobile-apps/expo/shared/metro.config.js +6 -0
- package/registry/mobile-apps/expo/shared/nativewind-env.d.ts +3 -0
- package/registry/mobile-apps/expo/shared/postcss.config.mjs +5 -0
- package/registry/mobile-apps/expo/shared/src/screens/LoginScreen.tsx +81 -0
- package/registry/mobile-apps/expo/shared/src/screens/SessionsScreen.tsx +77 -0
- package/registry/mobile-apps/expo/shared/src/screens/SignupScreen.tsx +77 -0
- package/registry/mobile-apps/expo/shared/src/screens/TwoFactorScreen.tsx +59 -0
- package/registry/mobile-apps/expo/shared/src/storage/asyncStorageTokenStorage.ts +32 -0
- package/registry/mobile-apps/expo/shared/tsconfig.json +6 -0
- package/registry/mobile-apps/expo/variants/base/app.json +25 -0
- package/registry/mobile-apps/expo/variants/base/package.json +35 -0
- package/registry/mobile-apps/expo/variants/base/src/api/authClient.ts +17 -0
- package/registry/mobile-apps/expo/variants/base/src/navigation/RootNavigator.tsx +58 -0
- package/registry/mobile-apps/expo/variants/base/src/navigation/types.ts +10 -0
- package/registry/mobile-apps/expo/variants/base/src/screens/HomeScreen.tsx +75 -0
- package/registry/mobile-apps/expo/variants/base/src/store/authStore.ts +146 -0
- package/registry/mobile-apps/expo/variants/workspaces/app.json +25 -0
- package/registry/mobile-apps/expo/variants/workspaces/package.json +35 -0
- package/registry/mobile-apps/expo/variants/workspaces/src/api/authClient.ts +21 -0
- package/registry/mobile-apps/expo/variants/workspaces/src/navigation/RootNavigator.tsx +82 -0
- package/registry/mobile-apps/expo/variants/workspaces/src/navigation/types.ts +20 -0
- package/registry/mobile-apps/expo/variants/workspaces/src/screens/HomeScreen.tsx +94 -0
- package/registry/mobile-apps/expo/variants/workspaces/src/screens/WorkspaceSelectScreen.tsx +175 -0
- package/registry/mobile-apps/expo/variants/workspaces/src/store/authStore.ts +179 -0
- package/registry/mobile-apps/expo/variants/workspaces/src/store/workspaceStore.ts +90 -0
- package/registry/mobile-apps/expo/variants/workspaces/src/workspace/activeWorkspace.ts +55 -0
- package/registry.json +263 -0
- package/simple-auth-kit.ts +715 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// The caching seam in front of permission resolution.
|
|
2
|
+
//
|
|
3
|
+
// Authorization is resolved from the database on every authorized request — that is what makes a
|
|
4
|
+
// grant or a revocation take effect on the caller's *next request* rather than their next token.
|
|
5
|
+
// Left naked, that puts one multi-join read on the hot path of every call. This module is the
|
|
6
|
+
// seam that keeps the semantics and removes the read, and it is here from the start because
|
|
7
|
+
// retrofitting a cache into a hot path is exactly the change that is expensive later.
|
|
8
|
+
//
|
|
9
|
+
// Three decisions, all of which a consumer can see and none of which they have to accept:
|
|
10
|
+
//
|
|
11
|
+
// 1. **Version-key invalidation, never eviction.** Nothing hunts down and deletes cache entries.
|
|
12
|
+
// A cache key contains two generation counters, and bumping a counter makes every key that
|
|
13
|
+
// embedded the old value unreachable in one write. That matters because one role change can
|
|
14
|
+
// affect an unbounded number of users, and there is no scan that scales.
|
|
15
|
+
//
|
|
16
|
+
// 2. **Two counters, not one.**
|
|
17
|
+
// • a *subject* counter per principal (`userId`, or `userId:workspaceId`), bumped by the
|
|
18
|
+
// frequent, narrow operations: this user's role assignments and direct grants;
|
|
19
|
+
// • a *policy* counter, global, bumped by the rare, wide ones: what a role carries, and
|
|
20
|
+
// whether a permission is active at all.
|
|
21
|
+
// A single global counter would be simpler but would flush every user's entry every time an
|
|
22
|
+
// administrator touched one user's grants. Per-role counters would be more surgical still,
|
|
23
|
+
// but resolving them needs the role→user fan-out this scheme exists to avoid, on the read
|
|
24
|
+
// path, for every request. The cost of what is chosen here is honest and bounded: a role
|
|
25
|
+
// composition change or a permission being deactivated re-resolves everyone once. Those are
|
|
26
|
+
// administrator actions measured in per-day, not per-second.
|
|
27
|
+
//
|
|
28
|
+
// 3. **The store is an injected interface with an in-memory default.** Same style as
|
|
29
|
+
// `TokenStorage` in packages/auth-client: a consumer swaps in Redis by passing a store to
|
|
30
|
+
// `createAuthApp`, without editing a line of this library. Keys are namespaced
|
|
31
|
+
// (`simpleauthkit:authz:*`) so a shared Redis stays separable. No Redis dependency is added here.
|
|
32
|
+
//
|
|
33
|
+
// What is deliberately *not* cached: authentication. Whether a token is valid, denylisted, or
|
|
34
|
+
// belongs to a blocked user is decided on the authentication path (`AuthGuard`), which never
|
|
35
|
+
// reads this cache — so blocking a user or revoking a session cannot be defeated by a warm entry.
|
|
36
|
+
// Negative results are not cached either: "not a member of this workspace" is re-read every time,
|
|
37
|
+
// so being added to a workspace is effective immediately rather than after a TTL.
|
|
38
|
+
import type { AuthConfig } from "./auth.config.js";
|
|
39
|
+
|
|
40
|
+
/** Every key this library writes starts here, so a shared Redis can be separated by prefix. */
|
|
41
|
+
export const CACHE_NAMESPACE = "simpleauthkit:authz";
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The four operations a permission cache needs. Deliberately small and string-valued: every one
|
|
45
|
+
* maps onto a single Redis command (`GET`, `MGET`, `SET ... EX`, `INCR`), so the Redis
|
|
46
|
+
* implementation is a dozen lines and needs no transaction.
|
|
47
|
+
*/
|
|
48
|
+
export interface PermissionCacheStore {
|
|
49
|
+
get(key: string): Promise<string | undefined>;
|
|
50
|
+
getMany(keys: readonly string[]): Promise<Array<string | undefined>>;
|
|
51
|
+
set(key: string, value: string, ttlSeconds: number): Promise<void>;
|
|
52
|
+
/** Atomic increment, returning the new value. `INCR` in Redis; correct here because Node is single-threaded. */
|
|
53
|
+
bump(key: string): Promise<number>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The default store: a `Map` with expiry, scoped to one process.
|
|
58
|
+
*
|
|
59
|
+
* Correct for a single-instance deployment and for development. Across several instances each
|
|
60
|
+
* keeps its own copy, and a version bumped on one is not seen by the others — so a multi-instance
|
|
61
|
+
* deployment should pass a shared store. That is the whole reason the interface exists.
|
|
62
|
+
*
|
|
63
|
+
* `stats` is not decoration: it is how `prove-cycle` asserts that N identical authorized requests
|
|
64
|
+
* cause one database resolution rather than N. A cache nobody can prove is working is a bug
|
|
65
|
+
* surface.
|
|
66
|
+
*/
|
|
67
|
+
export class InMemoryPermissionCacheStore implements PermissionCacheStore {
|
|
68
|
+
readonly stats = { hits: 0, misses: 0, writes: 0 };
|
|
69
|
+
private readonly entries = new Map<string, { value: string; expiresAt: number }>();
|
|
70
|
+
|
|
71
|
+
async get(key: string): Promise<string | undefined> {
|
|
72
|
+
const entry = this.entries.get(key);
|
|
73
|
+
if (!entry) {
|
|
74
|
+
this.stats.misses += 1;
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
if (entry.expiresAt <= Date.now()) {
|
|
78
|
+
this.entries.delete(key);
|
|
79
|
+
this.stats.misses += 1;
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
this.stats.hits += 1;
|
|
83
|
+
return entry.value;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async getMany(keys: readonly string[]): Promise<Array<string | undefined>> {
|
|
87
|
+
// Version reads are bookkeeping, not cache traffic — counting them in hits/misses would make
|
|
88
|
+
// the hit rate meaningless, and `prove-cycle` asserts on that number.
|
|
89
|
+
return keys.map((key) => {
|
|
90
|
+
const entry = this.entries.get(key);
|
|
91
|
+
return entry && entry.expiresAt > Date.now() ? entry.value : undefined;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async set(key: string, value: string, ttlSeconds: number): Promise<void> {
|
|
96
|
+
this.stats.writes += 1;
|
|
97
|
+
this.entries.set(key, { value, expiresAt: Date.now() + ttlSeconds * 1000 });
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async bump(key: string): Promise<number> {
|
|
101
|
+
const entry = this.entries.get(key);
|
|
102
|
+
const next = Number(entry && entry.expiresAt > Date.now() ? entry.value : 0) + 1;
|
|
103
|
+
// Version counters outlive entries on purpose: a counter that expired back to 0 would make
|
|
104
|
+
// every superseded entry reachable again.
|
|
105
|
+
this.entries.set(key, { value: String(next), expiresAt: Number.MAX_SAFE_INTEGER });
|
|
106
|
+
return next;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* The two version counters. Exported because anything writing RBAC rows *outside* this
|
|
112
|
+
* application — a second service, a migration, a `psql` session — is outside the invalidation
|
|
113
|
+
* protocol, and bumping the counter is how such a writer says so. Without that, an out-of-band
|
|
114
|
+
* write is picked up when the entry expires (`permissionCacheTtlSeconds`), not immediately.
|
|
115
|
+
*/
|
|
116
|
+
export const POLICY_VERSION_KEY = `${CACHE_NAMESPACE}:policy-version`;
|
|
117
|
+
export const subjectVersionKey = (subject: string) => `${CACHE_NAMESPACE}:subject-version:${subject}`;
|
|
118
|
+
const entryKey = (subject: string, policy: string, subjectVersion: string) => `${CACHE_NAMESPACE}:entry:${subject}:p${policy}:s${subjectVersion}`;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Reads through to the database, once per (principal, policy version, subject version).
|
|
122
|
+
*
|
|
123
|
+
* Used by each variant's authz middleware. The variants differ only in what a "subject" is — a user
|
|
124
|
+
* id in `base`, a `userId:workspaceId` pair in `workspaces` — which is why this file can be
|
|
125
|
+
* shared while the guards cannot.
|
|
126
|
+
*/
|
|
127
|
+
export class PermissionCache {
|
|
128
|
+
constructor(
|
|
129
|
+
private readonly store: PermissionCacheStore,
|
|
130
|
+
private readonly config: AuthConfig,
|
|
131
|
+
) {}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Concurrent misses on the same key collapse into one `load()`.
|
|
135
|
+
*
|
|
136
|
+
* In-process, which is enough for the default store: a cold key under load costs one database
|
|
137
|
+
* read per instance rather than one per request. A distributed single-flight (a short-lived
|
|
138
|
+
* lock key in the shared store) replaces this without touching callers — the seam is that
|
|
139
|
+
* `load` is a thunk and the map is private.
|
|
140
|
+
*/
|
|
141
|
+
private readonly inFlight = new Map<string, Promise<unknown>>();
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @param subject the identity these permissions belong to — the cache key's principal
|
|
145
|
+
* @param load resolves from the database on a miss. Returning `null` is not cached.
|
|
146
|
+
*/
|
|
147
|
+
async resolve<T>(subject: string, load: () => Promise<T | null>): Promise<T | null> {
|
|
148
|
+
const ttl = this.config.permissionCacheTtlSeconds;
|
|
149
|
+
if (ttl <= 0) return load(); // caching off; the semantics are identical, only the cost differs
|
|
150
|
+
|
|
151
|
+
const [policy = "0", version = "0"] = await this.store.getMany([POLICY_VERSION_KEY, subjectVersionKey(subject)]);
|
|
152
|
+
const key = entryKey(subject, policy, version);
|
|
153
|
+
|
|
154
|
+
const cached = await this.store.get(key);
|
|
155
|
+
if (cached !== undefined) return JSON.parse(cached) as T;
|
|
156
|
+
|
|
157
|
+
const existing = this.inFlight.get(key);
|
|
158
|
+
if (existing) return (await existing) as T | null;
|
|
159
|
+
|
|
160
|
+
const pending = (async () => {
|
|
161
|
+
const resolved = await load();
|
|
162
|
+
// A negative result is not written: see the module comment. Only a real answer is cached.
|
|
163
|
+
if (resolved !== null) await this.store.set(key, JSON.stringify(resolved), ttl);
|
|
164
|
+
return resolved;
|
|
165
|
+
})();
|
|
166
|
+
|
|
167
|
+
this.inFlight.set(key, pending);
|
|
168
|
+
try {
|
|
169
|
+
return await pending;
|
|
170
|
+
} finally {
|
|
171
|
+
this.inFlight.delete(key);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Invalidates one principal: their role assignments or their direct grants changed. One write,
|
|
177
|
+
* no fan-out, and every entry that embedded the old counter is unreachable from that moment.
|
|
178
|
+
*/
|
|
179
|
+
async invalidateSubject(subject: string): Promise<void> {
|
|
180
|
+
await this.store.bump(subjectVersionKey(subject));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Invalidates everyone: what a role carries changed, or a permission was activated or
|
|
185
|
+
* deactivated. One write, whatever the number of users affected — which is the point, because
|
|
186
|
+
* the alternative is enumerating them.
|
|
187
|
+
*/
|
|
188
|
+
async invalidatePolicy(): Promise<void> {
|
|
189
|
+
await this.store.bump(POLICY_VERSION_KEY);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { RateLimitDeps } from "@/lib/auth/core/rate-limit.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* In-memory window counter — fine for a single instance / the reference combo's proof.
|
|
5
|
+
* Swap for Redis (INCR + PEXPIRE) behind this same `increment` signature once you run
|
|
6
|
+
* more than one instance; nothing above this layer needs to change.
|
|
7
|
+
*
|
|
8
|
+
* Plain class, no decorators — identical logic to the NestJS combos' equivalent, just
|
|
9
|
+
* constructed directly instead of registered as an injectable provider.
|
|
10
|
+
*/
|
|
11
|
+
export class InMemoryRateLimitStore implements RateLimitDeps {
|
|
12
|
+
private readonly windows = new Map<string, { count: number; resetAt: number }>();
|
|
13
|
+
|
|
14
|
+
async increment(key: string, windowMs: number): Promise<number> {
|
|
15
|
+
const now = Date.now();
|
|
16
|
+
const existing = this.windows.get(key);
|
|
17
|
+
if (!existing || existing.resetAt <= now) {
|
|
18
|
+
this.windows.set(key, { count: 1, resetAt: now + windowMs });
|
|
19
|
+
return 1;
|
|
20
|
+
}
|
|
21
|
+
existing.count += 1;
|
|
22
|
+
return existing.count;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// The three request-scoped objects every middleware and route handler in this combo reads.
|
|
2
|
+
//
|
|
3
|
+
// req.auth — the verified access-token claims. Identity only: which user, which session.
|
|
4
|
+
// req.authz — the authorization context resolved for *this* request: the roles and
|
|
5
|
+
// permissions that apply to it.
|
|
6
|
+
// req.ability — the same authorization, as the CASL ability the routes are gated on. Derived
|
|
7
|
+
// from `req.authz.permissions` in the same middleware that resolves them, so it
|
|
8
|
+
// costs no extra lookup of any kind. `GET /auth/me` returns the permission slugs
|
|
9
|
+
// rather than a serialised ability, and a client rebuilds the identical object
|
|
10
|
+
// from them with the same `defineAbilitiesFor`.
|
|
11
|
+
//
|
|
12
|
+
// Keeping authentication and authorization separate means a route handler never has to care *how*
|
|
13
|
+
// authorization was decided, only what it decided — see authz.middleware.ts. Importing this
|
|
14
|
+
// module for its side effect installs the Express `Request` augmentation.
|
|
15
|
+
//
|
|
16
|
+
// Express has no request-scoped DI container, so where the reference combo's guards hand a
|
|
17
|
+
// resolved object to the framework, here each middleware attaches it to the request itself.
|
|
18
|
+
// The augmentation is written in Express's own declaration-merging style (`declare global` on
|
|
19
|
+
// the `Express` namespace, see @types/express-serve-static-core) rather than the reference's
|
|
20
|
+
// `declare module "express"`.
|
|
21
|
+
import type { AccessTokenClaims } from "@/lib/auth/core/types.js";
|
|
22
|
+
import type { AppAbility } from "./ability.js";
|
|
23
|
+
import type { AuthzContext } from "./authz.middleware.js";
|
|
24
|
+
|
|
25
|
+
declare global {
|
|
26
|
+
namespace Express {
|
|
27
|
+
interface Request {
|
|
28
|
+
auth?: AccessTokenClaims;
|
|
29
|
+
authz?: AuthzContext;
|
|
30
|
+
ability?: AppAbility;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { NextFunction, Request, RequestHandler, Response } from "express";
|
|
2
|
+
|
|
3
|
+
function deriveMessage(method: string): string {
|
|
4
|
+
switch (method) {
|
|
5
|
+
case "GET":
|
|
6
|
+
return "Fetched successfully";
|
|
7
|
+
case "POST":
|
|
8
|
+
return "Created successfully";
|
|
9
|
+
case "PATCH":
|
|
10
|
+
case "PUT":
|
|
11
|
+
return "Updated successfully";
|
|
12
|
+
case "DELETE":
|
|
13
|
+
return "Deleted successfully";
|
|
14
|
+
default:
|
|
15
|
+
return "Request successful";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Wraps every `res.json(...)` call made after this middleware runs, so no individual route
|
|
21
|
+
* handler needs to know the envelope exists. Distinguishes success from error purely by
|
|
22
|
+
* `res.statusCode` (already set via `res.status(x)` before `.json()` is ever called, by every
|
|
23
|
+
* call site in this codebase) — a body from an error path is spread with `success: false` rather
|
|
24
|
+
* than nested under `data`, since it's already shaped as `{statusCode, message, ...}`.
|
|
25
|
+
*
|
|
26
|
+
* Skips `/docs` and `/docs-json` regardless of where those routes are mounted relative to this
|
|
27
|
+
* middleware — the raw OpenAPI JSON must stay unwrapped.
|
|
28
|
+
*/
|
|
29
|
+
export function responseEnvelope(): RequestHandler {
|
|
30
|
+
return (req: Request, res: Response, next: NextFunction) => {
|
|
31
|
+
if (req.path === "/docs-json" || req.path.startsWith("/docs")) {
|
|
32
|
+
next();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const originalJson = res.json.bind(res);
|
|
37
|
+
res.json = ((body: unknown) => {
|
|
38
|
+
if (res.statusCode >= 400) {
|
|
39
|
+
const shaped = body && typeof body === "object" ? (body as Record<string, unknown>) : { message: body };
|
|
40
|
+
return originalJson({ success: false, statusCode: res.statusCode, ...shaped });
|
|
41
|
+
}
|
|
42
|
+
return originalJson({ success: true, statusCode: res.statusCode, message: deriveMessage(req.method), data: body });
|
|
43
|
+
}) as typeof res.json;
|
|
44
|
+
next();
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// Every route in this combo belongs to exactly one of three tiers, and says so at the point it is
|
|
2
|
+
// registered.
|
|
3
|
+
//
|
|
4
|
+
// 1. public(...) — no authentication at all. Signup, login, refresh, password
|
|
5
|
+
// reset, the OAuth redirect endpoints.
|
|
6
|
+
// 2. authenticated(...) — any logged-in user, no permission. /auth/me, logout, sessions,
|
|
7
|
+
// 2FA enrolment, and (in the workspace variant) creating or
|
|
8
|
+
// listing your own workspaces and seeing who else is in one you
|
|
9
|
+
// belong to.
|
|
10
|
+
// 3. ability(...slugs) — the administrative surface. Every named slug must be in the
|
|
11
|
+
// caller's ability or the request is refused.
|
|
12
|
+
//
|
|
13
|
+
// The reference combo says this with decorators and then walks the route table at startup: a
|
|
14
|
+
// handler carrying none of the three markers fails the boot, by name. Plain Express has no
|
|
15
|
+
// metadata-reflection layer, so "what did this handler declare?" cannot be recovered by
|
|
16
|
+
// inspection after the fact. The property is enforced at the point of registration instead, which
|
|
17
|
+
// gets to the same place by a different road:
|
|
18
|
+
//
|
|
19
|
+
// • `route()` is the only way to put a handler on a tiered router, and its tier argument is
|
|
20
|
+
// required — a route with no tier is not an ungated route, it is a compile error;
|
|
21
|
+
// • the finished router is handed back typed as `RequestHandler`, so `.get`/`.post` are not
|
|
22
|
+
// reachable to sneak a route past `route()`;
|
|
23
|
+
// • the middleware each tier needs is attached *by* `route()`, from the tier itself, so a route
|
|
24
|
+
// cannot claim a tier and then not be behind it — the equivalent of the reference's
|
|
25
|
+
// two-sided "declared tier matches attached guards" check;
|
|
26
|
+
// • and every slug is checked against `PERMISSION_CATALOG` at registration, i.e. at startup, so
|
|
27
|
+
// even an untyped (plain-JS) caller fails loudly at boot rather than serving a route gated on
|
|
28
|
+
// a permission nothing can ever grant.
|
|
29
|
+
import { Router } from "express";
|
|
30
|
+
import type { NextFunction, Request, RequestHandler, Response } from "express";
|
|
31
|
+
import { ABILITY_SUBJECT } from "./ability.js";
|
|
32
|
+
import { PERMISSION_CATALOG, type PermissionSlug } from "./rbac.defaults.js";
|
|
33
|
+
import "./request-context.js";
|
|
34
|
+
|
|
35
|
+
/** The subset of Express's routing verbs this library's routes use. */
|
|
36
|
+
export type TieredMethod = "get" | "post" | "put" | "patch" | "delete";
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* What a route declares about itself. There is deliberately no fourth shape and no default: the
|
|
40
|
+
* union is closed, so adding a route means picking one of these three.
|
|
41
|
+
*/
|
|
42
|
+
export type RouteTier = { kind: "public" } | { kind: "authenticated" } | { kind: "ability"; abilities: PermissionSlug[] };
|
|
43
|
+
|
|
44
|
+
/** Tier 1: reachable with no token at all. Carries no middleware, which is exactly why it has to be said out loud. */
|
|
45
|
+
export const publicRoute = (): RouteTier => ({ kind: "public" });
|
|
46
|
+
|
|
47
|
+
/** Tier 2: any authenticated caller, no permission required. */
|
|
48
|
+
export const authenticated = (): RouteTier => ({ kind: "authenticated" });
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Tier 3: `ability("users:block")`. Several slugs may be named, and **all** of them must be held —
|
|
52
|
+
* the check ANDs them.
|
|
53
|
+
*
|
|
54
|
+
* This is the half of authorization that has to be code. **A route declares what it demands; the
|
|
55
|
+
* database decides who is granted it.** The argument is typed as `PermissionSlug`, the catalog in
|
|
56
|
+
* `rbac.defaults.ts`, so a route cannot be gated on a slug nothing will ever grant. Which *users*
|
|
57
|
+
* hold the slug is entirely a matter of rows.
|
|
58
|
+
*/
|
|
59
|
+
export const ability = (...abilities: PermissionSlug[]): RouteTier => {
|
|
60
|
+
if (!abilities.length) throw new Error("ability() names no permission — use authenticated() if any logged-in caller may call the route");
|
|
61
|
+
for (const slug of abilities) assertInCatalog(slug);
|
|
62
|
+
return { kind: "ability", abilities };
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The authorization boundary for tier-3 routes. This is the *only* thing standing between a caller
|
|
67
|
+
* and an administrative route — there is deliberately no role-based bypass beside it, so a role
|
|
68
|
+
* that carries no permissions confers no authority no matter what it is called.
|
|
69
|
+
*
|
|
70
|
+
* It asks the caller's `Ability` the same question a client asks after `GET /auth/me`, built by
|
|
71
|
+
* the same function from the same slugs, which is what makes server enforcement and client UI
|
|
72
|
+
* incapable of disagreeing.
|
|
73
|
+
*/
|
|
74
|
+
export function requireAbility(abilities: PermissionSlug[]): RequestHandler {
|
|
75
|
+
return function abilityMiddleware(req: Request, res: Response, next: NextFunction): void {
|
|
76
|
+
// No ability at all means the authz middleware resolved nothing — fail closed.
|
|
77
|
+
if (!req.ability) {
|
|
78
|
+
res.status(403).json({ statusCode: 403, code: "FORBIDDEN", message: "no authorization context for this request" });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
for (const slug of abilities) {
|
|
82
|
+
if (!req.ability.can(slug, ABILITY_SUBJECT)) {
|
|
83
|
+
res.status(403).json({ statusCode: 403, code: "FORBIDDEN", message: `missing permission: ${slug}` });
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
next();
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** A router on which naming a tier is not optional. See `createTieredRouter`. */
|
|
92
|
+
export interface TieredRouter {
|
|
93
|
+
/**
|
|
94
|
+
* Registers one route. `tier` is a required positional argument, so there is no call shape that
|
|
95
|
+
* adds a route without one, and the middleware the tier implies is always inserted immediately
|
|
96
|
+
* before `handler` — the two cannot come apart.
|
|
97
|
+
*/
|
|
98
|
+
route(method: TieredMethod, path: string, tier: RouteTier, ...handlers: RequestHandler[]): void;
|
|
99
|
+
/**
|
|
100
|
+
* The composed middleware, for `app.use(...)`. Deliberately typed as `RequestHandler` and not
|
|
101
|
+
* as `Router`, even though that is what it is at runtime: the `.get`/`.post`/… methods are
|
|
102
|
+
* hidden from the type, so `route()` above is the only way to put a handler on this router.
|
|
103
|
+
*/
|
|
104
|
+
readonly handler: RequestHandler;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface TierMiddleware {
|
|
108
|
+
/** Verifies the access token and populates `req.auth`. Attached for tiers 2 and 3. */
|
|
109
|
+
authentication: RequestHandler;
|
|
110
|
+
/** Resolves `req.authz` and `req.ability` from the database. Attached for tiers 2 and 3. */
|
|
111
|
+
authorization: RequestHandler;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Builds a router whose every route names its tier.
|
|
116
|
+
*
|
|
117
|
+
* `middleware.authentication` and `middleware.authorization` are attached per route rather than
|
|
118
|
+
* with a blanket `router.use`, because that is what makes the public tier genuinely public on a
|
|
119
|
+
* router that also carries protected routes — and what makes "declares public but sits behind the
|
|
120
|
+
* auth middleware" unrepresentable rather than merely unlikely.
|
|
121
|
+
*/
|
|
122
|
+
export function createTieredRouter(middleware: TierMiddleware): TieredRouter {
|
|
123
|
+
const router = Router();
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
route(method: TieredMethod, path: string, tier: RouteTier, ...handlers: RequestHandler[]): void {
|
|
127
|
+
router[method](path, ...tierMiddleware(tier, middleware), ...handlers);
|
|
128
|
+
},
|
|
129
|
+
handler: router,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function tierMiddleware(tier: RouteTier, middleware: TierMiddleware): RequestHandler[] {
|
|
134
|
+
switch (tier.kind) {
|
|
135
|
+
case "public":
|
|
136
|
+
return [];
|
|
137
|
+
case "authenticated":
|
|
138
|
+
return [middleware.authentication, middleware.authorization];
|
|
139
|
+
case "ability":
|
|
140
|
+
return [middleware.authentication, middleware.authorization, requireAbility(tier.abilities)];
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function assertInCatalog(slug: string): void {
|
|
145
|
+
if (!Object.prototype.hasOwnProperty.call(PERMISSION_CATALOG, slug)) {
|
|
146
|
+
throw new Error(`"${slug}" is not in PERMISSION_CATALOG — add it to rbac.defaults.ts (the seeder provisions exactly that catalog)`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { and, eq, isNull, ne, sql } from "drizzle-orm";
|
|
3
|
+
import type { SessionStoreDeps } from "@/lib/auth/core/session-policy.js";
|
|
4
|
+
import type { AuditEvent, Revoker, SessionRecord } from "@/lib/auth/core/types.js";
|
|
5
|
+
import { AuditLogRepository } from "./audit-log.repository.js";
|
|
6
|
+
import type { AuthConfig } from "./auth.config.js";
|
|
7
|
+
import type { Database } from "./db.js";
|
|
8
|
+
import { denylistedAccessTokens, sessions } from "./schema.js";
|
|
9
|
+
import { toId, toIdOrNull } from "./id.helper.js";
|
|
10
|
+
|
|
11
|
+
function toSessionRecord(row: typeof sessions.$inferSelect): SessionRecord {
|
|
12
|
+
return {
|
|
13
|
+
id: row.id.toString(),
|
|
14
|
+
userId: row.userId.toString(),
|
|
15
|
+
sessionVersion: row.sessionVersion,
|
|
16
|
+
currentRefreshJti: row.currentRefreshJti,
|
|
17
|
+
provider: row.provider ?? undefined,
|
|
18
|
+
expiresAt: row.expiresAt.toISOString(),
|
|
19
|
+
revokedAt: row.revokedAt?.toISOString() ?? null,
|
|
20
|
+
revokedBy: row.revokedBy?.toString() ?? undefined,
|
|
21
|
+
revokedByIp: row.revokedByIp ?? undefined,
|
|
22
|
+
createdAt: row.createdAt.toISOString(),
|
|
23
|
+
userAgent: row.userAgent ?? undefined,
|
|
24
|
+
ip: row.ip ?? undefined,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The revocation columns, written as one block wherever a session is revoked.
|
|
30
|
+
*
|
|
31
|
+
* `isRevoked` is derived from `revokedAt` here rather than being a fact of its own: the reference
|
|
32
|
+
* schema carries both columns, and the only way two columns for one fact stay honest is if
|
|
33
|
+
* exactly one place computes the second from the first.
|
|
34
|
+
*/
|
|
35
|
+
const revocationFields = (revoker?: Revoker) => ({
|
|
36
|
+
revokedAt: new Date(),
|
|
37
|
+
isRevoked: true,
|
|
38
|
+
isActive: false,
|
|
39
|
+
revokedBy: toIdOrNull(revoker?.userId),
|
|
40
|
+
revokedByIp: revoker?.ip ?? null,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export class SessionRepository implements SessionStoreDeps {
|
|
44
|
+
constructor(
|
|
45
|
+
private readonly db: Database,
|
|
46
|
+
private readonly auditLog: AuditLogRepository,
|
|
47
|
+
private readonly config: AuthConfig,
|
|
48
|
+
) {}
|
|
49
|
+
|
|
50
|
+
/** Delegates to AuditLogRepository — makes the previously-optional, previously-unwired `SessionStoreDeps.appendAuditEvent` real. */
|
|
51
|
+
async appendAuditEvent(event: AuditEvent): Promise<void> {
|
|
52
|
+
await this.auditLog.append(event);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async findSession(id: string): Promise<SessionRecord | null> {
|
|
56
|
+
const [row] = await this.db.select().from(sessions).where(eq(sessions.id, toId(id))).limit(1);
|
|
57
|
+
return row ? toSessionRecord(row) : null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async saveSession(session: SessionRecord): Promise<void> {
|
|
61
|
+
const revoked = session.revokedAt !== null;
|
|
62
|
+
await this.db
|
|
63
|
+
.update(sessions)
|
|
64
|
+
.set({
|
|
65
|
+
sessionVersion: session.sessionVersion,
|
|
66
|
+
currentRefreshJti: session.currentRefreshJti,
|
|
67
|
+
revokedAt: session.revokedAt ? new Date(session.revokedAt) : null,
|
|
68
|
+
isRevoked: revoked,
|
|
69
|
+
isActive: !revoked,
|
|
70
|
+
revokedBy: toIdOrNull(session.revokedBy),
|
|
71
|
+
revokedByIp: session.revokedByIp ?? null,
|
|
72
|
+
})
|
|
73
|
+
.where(eq(sessions.id, toId(session.id)));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* `expiresAt` is this repository's decision, not core's — the store is the half that knows the
|
|
78
|
+
* deployment's configured session lifetime. It is set once here and never rewritten, which is
|
|
79
|
+
* what makes it an absolute cap rather than a sliding window: see `sessionTtlSeconds`.
|
|
80
|
+
*
|
|
81
|
+
* `createdBy` is the user themselves. A session is only ever created by the person logging in,
|
|
82
|
+
* so recording it costs nothing and leaves the column meaningful rather than always null.
|
|
83
|
+
*/
|
|
84
|
+
async createSession(input: { userId: string; userAgent?: string; ip?: string; provider?: string }): Promise<SessionRecord> {
|
|
85
|
+
const userIdBig = toId(input.userId);
|
|
86
|
+
const [row] = await this.db
|
|
87
|
+
.insert(sessions)
|
|
88
|
+
.values({
|
|
89
|
+
userId: userIdBig,
|
|
90
|
+
currentRefreshJti: randomUUID(),
|
|
91
|
+
provider: input.provider,
|
|
92
|
+
userAgent: input.userAgent,
|
|
93
|
+
ip: input.ip,
|
|
94
|
+
expiresAt: new Date(Date.now() + this.config.sessionTtlSeconds * 1000),
|
|
95
|
+
createdBy: userIdBig,
|
|
96
|
+
})
|
|
97
|
+
.returning();
|
|
98
|
+
return toSessionRecord(row);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Bulk revoke for one user. Sessions are user-level, never scoped to anything narrower. */
|
|
102
|
+
async revokeAllByUser(userId: string, revoker?: Revoker): Promise<void> {
|
|
103
|
+
await this.db
|
|
104
|
+
.update(sessions)
|
|
105
|
+
.set({ ...revocationFields(revoker), sessionVersion: sql`${sessions.sessionVersion} + 1` })
|
|
106
|
+
.where(and(eq(sessions.userId, toId(userId)), isNull(sessions.revokedAt)));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async revokeAllByUserExcept(userId: string, keepSessionId: string, revoker?: Revoker): Promise<void> {
|
|
110
|
+
await this.db
|
|
111
|
+
.update(sessions)
|
|
112
|
+
.set({ ...revocationFields(revoker), sessionVersion: sql`${sessions.sessionVersion} + 1` })
|
|
113
|
+
.where(and(eq(sessions.userId, toId(userId)), ne(sessions.id, toId(keepSessionId)), isNull(sessions.revokedAt)));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async denylistJti(jti: string, ttlSeconds: number): Promise<void> {
|
|
117
|
+
const expiresAt = new Date(Date.now() + ttlSeconds * 1000);
|
|
118
|
+
await this.db
|
|
119
|
+
.insert(denylistedAccessTokens)
|
|
120
|
+
.values({ jti, expiresAt })
|
|
121
|
+
.onConflictDoUpdate({ target: denylistedAccessTokens.jti, set: { expiresAt } });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async isDenylisted(jti: string): Promise<boolean> {
|
|
125
|
+
const [row] = await this.db.select().from(denylistedAccessTokens).where(eq(denylistedAccessTokens.jti, jti)).limit(1);
|
|
126
|
+
if (!row) return false;
|
|
127
|
+
if (row.expiresAt.getTime() < Date.now()) return false; // expired; a cleanup job reaps these, see Phase 6
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { and, eq, isNull } from "drizzle-orm";
|
|
2
|
+
import type { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
3
|
+
import { verifyBackupCode } from "@/lib/auth/core/two-factor.js";
|
|
4
|
+
import * as schema from "./schema.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Plain class, no decorators — Drizzle-specific implementation mirroring the reference
|
|
8
|
+
* combo's TwoFactorRepository.
|
|
9
|
+
*
|
|
10
|
+
* Takes bigint directly, not string: every caller already has the user row's id in hand from
|
|
11
|
+
* its own database lookup, so there's nothing to parse here.
|
|
12
|
+
*/
|
|
13
|
+
export class TwoFactorRepository {
|
|
14
|
+
constructor(private readonly db: NodePgDatabase<typeof schema>) {}
|
|
15
|
+
|
|
16
|
+
/** Replaces any prior set — re-confirming enrollment invalidates old backup codes. */
|
|
17
|
+
async saveBackupCodes(userId: bigint, hashes: string[]): Promise<void> {
|
|
18
|
+
await this.db.delete(schema.twoFactorBackupCodes).where(eq(schema.twoFactorBackupCodes.userId, userId));
|
|
19
|
+
if (hashes.length === 0) return;
|
|
20
|
+
await this.db.insert(schema.twoFactorBackupCodes).values(hashes.map((codeHash) => ({ userId, codeHash })));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async consumeBackupCode(userId: bigint, code: string): Promise<boolean> {
|
|
24
|
+
const unused = await this.db
|
|
25
|
+
.select()
|
|
26
|
+
.from(schema.twoFactorBackupCodes)
|
|
27
|
+
.where(and(eq(schema.twoFactorBackupCodes.userId, userId), isNull(schema.twoFactorBackupCodes.usedAt)));
|
|
28
|
+
const match = unused.find((row) => verifyBackupCode(code, row.codeHash));
|
|
29
|
+
if (!match) return false;
|
|
30
|
+
|
|
31
|
+
await this.db.update(schema.twoFactorBackupCodes).set({ usedAt: new Date() }).where(eq(schema.twoFactorBackupCodes.id, match.id));
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async clearBackupCodes(userId: bigint): Promise<void> {
|
|
36
|
+
await this.db.delete(schema.twoFactorBackupCodes).where(eq(schema.twoFactorBackupCodes.userId, userId));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
import type { Server } from "node:http";
|
|
3
|
+
import { createAuthApp } from "../src/create-auth-app.js";
|
|
4
|
+
import { InMemoryPermissionCacheStore } from "../src/permission-cache.js";
|
|
5
|
+
|
|
6
|
+
// No mailer is wired up for the proof, so this stands in for one — prove-cycle.ts reads the
|
|
7
|
+
// raw token back out of here the same way a test inbox would, to exercise the reset flow.
|
|
8
|
+
export const capturedResetTokens = new Map<string, string>();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The very store the app resolves permissions through. Exposed so `prove-cycle.ts` can assert
|
|
12
|
+
* that N identical authorized requests cause one database resolution rather than N — a cache
|
|
13
|
+
* nobody can prove is working is a bug surface, not an optimisation.
|
|
14
|
+
*/
|
|
15
|
+
export const permissionCacheStore = new InMemoryPermissionCacheStore();
|
|
16
|
+
|
|
17
|
+
export async function bootstrap(port: number): Promise<{ close: () => Promise<void> }> {
|
|
18
|
+
const app = createAuthApp({
|
|
19
|
+
// Was 2 seconds, "so the proof can exercise expiry-adjacent paths quickly" — except no
|
|
20
|
+
// assertion in the proof waits for an access token to expire, so it bought nothing and
|
|
21
|
+
// cost a real 1-in-10 flake: the admin's token could die mid-section and surface as a
|
|
22
|
+
// confusing failure two assertions later. Long enough that expiry is never a variable,
|
|
23
|
+
// short enough to stay realistic. See `renewingToken` in test/harness.ts for the other
|
|
24
|
+
// half of the fix.
|
|
25
|
+
accessTokenTtlSeconds: 300,
|
|
26
|
+
permissionCacheStore,
|
|
27
|
+
sendPasswordResetEmail: async (email, token) => {
|
|
28
|
+
capturedResetTokens.set(email, token);
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
const server: Server = await new Promise((resolve) => {
|
|
32
|
+
const s = app.listen(port, () => resolve(s));
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
close: () =>
|
|
36
|
+
new Promise<void>((resolve, reject) => {
|
|
37
|
+
server.close((err) => (err ? reject(err) : resolve()));
|
|
38
|
+
}),
|
|
39
|
+
};
|
|
40
|
+
}
|