@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,140 @@
|
|
|
1
|
+
// Authorization is resolved from the database on every authorized request — a grant or
|
|
2
|
+
// revocation takes effect on the caller's next request, not their next token — which puts one
|
|
3
|
+
// multi-join read on the hot path. This module caches that read without changing the semantics.
|
|
4
|
+
//
|
|
5
|
+
// 1. Version-key invalidation, never eviction: a cache key embeds two generation counters, and
|
|
6
|
+
// bumping one makes every key built from the old value unreachable in a single write.
|
|
7
|
+
// 2. Two counters: a *subject* counter per principal, bumped by that user's own role/grant
|
|
8
|
+
// changes; a global *policy* counter, bumped by role-composition or permission-activation
|
|
9
|
+
// changes. A single global counter would flush everyone on any one user's grant change.
|
|
10
|
+
// 3. The store is an injected interface with an in-memory default — swap in Redis via
|
|
11
|
+
// `AuthModule.forRoot` without touching this file. Keys are namespaced `simpleauthkit:authz:*`.
|
|
12
|
+
//
|
|
13
|
+
// Authentication is deliberately not cached here: token validity/denylist/blocked-user checks
|
|
14
|
+
// happen on `AuthGuard`, which never reads this cache. Negative results aren't cached either —
|
|
15
|
+
// "not a member of this workspace" is re-read every time.
|
|
16
|
+
import { Inject, Injectable } from "@nestjs/common";
|
|
17
|
+
import { AUTH_CONFIG, AuthConfig } from "./auth.config.js";
|
|
18
|
+
|
|
19
|
+
/** DI token for the store. Provide your own to `AuthModule.forRoot({ permissionCacheStore })`. */
|
|
20
|
+
export const PERMISSION_CACHE_STORE = Symbol("PERMISSION_CACHE_STORE");
|
|
21
|
+
|
|
22
|
+
export const CACHE_NAMESPACE = "simpleauthkit:authz";
|
|
23
|
+
|
|
24
|
+
// Small and string-valued: each op maps onto one Redis command (GET, MGET, SET EX, INCR).
|
|
25
|
+
export interface PermissionCacheStore {
|
|
26
|
+
get(key: string): Promise<string | undefined>;
|
|
27
|
+
getMany(keys: readonly string[]): Promise<Array<string | undefined>>;
|
|
28
|
+
set(key: string, value: string, ttlSeconds: number): Promise<void>;
|
|
29
|
+
bump(key: string): Promise<number>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Default store: a Map with expiry, scoped to one process. Correct for single-instance
|
|
33
|
+
// deployments; a multi-instance deployment should pass a shared store instead, since a version
|
|
34
|
+
// bumped on one instance isn't seen by the others.
|
|
35
|
+
//
|
|
36
|
+
// `stats` isn't decoration — `prove-cycle` asserts N identical authorized requests cause one
|
|
37
|
+
// database resolution, not N.
|
|
38
|
+
export class InMemoryPermissionCacheStore implements PermissionCacheStore {
|
|
39
|
+
readonly stats = { hits: 0, misses: 0, writes: 0 };
|
|
40
|
+
private readonly entries = new Map<string, { value: string; expiresAt: number }>();
|
|
41
|
+
|
|
42
|
+
async get(key: string): Promise<string | undefined> {
|
|
43
|
+
const entry = this.entries.get(key);
|
|
44
|
+
if (!entry) {
|
|
45
|
+
this.stats.misses += 1;
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
if (entry.expiresAt <= Date.now()) {
|
|
49
|
+
this.entries.delete(key);
|
|
50
|
+
this.stats.misses += 1;
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
this.stats.hits += 1;
|
|
54
|
+
return entry.value;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async getMany(keys: readonly string[]): Promise<Array<string | undefined>> {
|
|
58
|
+
// Version reads are bookkeeping, not cache traffic — counting them in hits/misses would make
|
|
59
|
+
// the hit rate meaningless, and `prove-cycle` asserts on that number.
|
|
60
|
+
return keys.map((key) => {
|
|
61
|
+
const entry = this.entries.get(key);
|
|
62
|
+
return entry && entry.expiresAt > Date.now() ? entry.value : undefined;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async set(key: string, value: string, ttlSeconds: number): Promise<void> {
|
|
67
|
+
this.stats.writes += 1;
|
|
68
|
+
this.entries.set(key, { value, expiresAt: Date.now() + ttlSeconds * 1000 });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async bump(key: string): Promise<number> {
|
|
72
|
+
const entry = this.entries.get(key);
|
|
73
|
+
const next = Number(entry && entry.expiresAt > Date.now() ? entry.value : 0) + 1;
|
|
74
|
+
// Version counters outlive entries on purpose: a counter that expired back to 0 would make
|
|
75
|
+
// every superseded entry reachable again.
|
|
76
|
+
this.entries.set(key, { value: String(next), expiresAt: Number.MAX_SAFE_INTEGER });
|
|
77
|
+
return next;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Exported so anything writing RBAC rows outside this application (a second service, a
|
|
82
|
+
// migration, a psql session) can bump the counter itself — otherwise an out-of-band write is
|
|
83
|
+
// only picked up once the cache entry expires.
|
|
84
|
+
export const POLICY_VERSION_KEY = `${CACHE_NAMESPACE}:policy-version`;
|
|
85
|
+
export const subjectVersionKey = (subject: string) => `${CACHE_NAMESPACE}:subject-version:${subject}`;
|
|
86
|
+
const entryKey = (subject: string, policy: string, subjectVersion: string) => `${CACHE_NAMESPACE}:entry:${subject}:p${policy}:s${subjectVersion}`;
|
|
87
|
+
|
|
88
|
+
// Reads through to the database once per (principal, policy version, subject version). Used by
|
|
89
|
+
// each variant's `AuthzGuard` — the variants differ only in what a "subject" is (a user id in
|
|
90
|
+
// `base`, a `userId:workspaceId` pair in `workspaces`), so this file is shared while the guards
|
|
91
|
+
// aren't.
|
|
92
|
+
@Injectable()
|
|
93
|
+
export class PermissionCache {
|
|
94
|
+
constructor(
|
|
95
|
+
@Inject(PERMISSION_CACHE_STORE) private readonly store: PermissionCacheStore,
|
|
96
|
+
@Inject(AUTH_CONFIG) private readonly config: AuthConfig,
|
|
97
|
+
) {}
|
|
98
|
+
|
|
99
|
+
// Concurrent misses on the same key collapse into one `load()` — in-process, so a cold key
|
|
100
|
+
// under load costs one database read per instance rather than one per request.
|
|
101
|
+
private readonly inFlight = new Map<string, Promise<unknown>>();
|
|
102
|
+
|
|
103
|
+
async resolve<T>(subject: string, load: () => Promise<T | null>): Promise<T | null> {
|
|
104
|
+
const ttl = this.config.permissionCacheTtlSeconds;
|
|
105
|
+
if (ttl <= 0) return load(); // caching off; the semantics are identical, only the cost differs
|
|
106
|
+
|
|
107
|
+
const [policy = "0", version = "0"] = await this.store.getMany([POLICY_VERSION_KEY, subjectVersionKey(subject)]);
|
|
108
|
+
const key = entryKey(subject, policy, version);
|
|
109
|
+
|
|
110
|
+
const cached = await this.store.get(key);
|
|
111
|
+
if (cached !== undefined) return JSON.parse(cached) as T;
|
|
112
|
+
|
|
113
|
+
const existing = this.inFlight.get(key);
|
|
114
|
+
if (existing) return (await existing) as T | null;
|
|
115
|
+
|
|
116
|
+
const pending = (async () => {
|
|
117
|
+
const resolved = await load();
|
|
118
|
+
// A negative result is not written: see the module comment. Only a real answer is cached.
|
|
119
|
+
if (resolved !== null) await this.store.set(key, JSON.stringify(resolved), ttl);
|
|
120
|
+
return resolved;
|
|
121
|
+
})();
|
|
122
|
+
|
|
123
|
+
this.inFlight.set(key, pending);
|
|
124
|
+
try {
|
|
125
|
+
return await pending;
|
|
126
|
+
} finally {
|
|
127
|
+
this.inFlight.delete(key);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// One principal's role assignments or direct grants changed.
|
|
132
|
+
async invalidateSubject(subject: string): Promise<void> {
|
|
133
|
+
await this.store.bump(subjectVersionKey(subject));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// What a role carries changed, or a permission was activated/deactivated — affects everyone.
|
|
137
|
+
async invalidatePolicy(): Promise<void> {
|
|
138
|
+
await this.store.bump(POLICY_VERSION_KEY);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Injectable } from "@nestjs/common";
|
|
2
|
+
import type { RateLimitDeps } from "@/lib/auth/core/rate-limit.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* In-memory window counter — fine for a single instance / the reference combo's proof.
|
|
6
|
+
* Swap for Redis (INCR + PEXPIRE) behind this same `increment` signature once you run
|
|
7
|
+
* more than one instance; nothing above this layer needs to change.
|
|
8
|
+
*/
|
|
9
|
+
@Injectable()
|
|
10
|
+
export class InMemoryRateLimitStore implements RateLimitDeps {
|
|
11
|
+
private readonly windows = new Map<string, { count: number; resetAt: number }>();
|
|
12
|
+
|
|
13
|
+
async increment(key: string, windowMs: number): Promise<number> {
|
|
14
|
+
const now = Date.now();
|
|
15
|
+
const existing = this.windows.get(key);
|
|
16
|
+
if (!existing || existing.resetAt <= now) {
|
|
17
|
+
this.windows.set(key, { count: 1, resetAt: now + windowMs });
|
|
18
|
+
return 1;
|
|
19
|
+
}
|
|
20
|
+
existing.count += 1;
|
|
21
|
+
return existing.count;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** DI token for the store. Provide your own to `AuthModule.forRoot({ rateLimitStore })` — mirrors `PERMISSION_CACHE_STORE`. */
|
|
26
|
+
export const RATE_LIMIT_STORE = Symbol("RATE_LIMIT_STORE");
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// The three request-scoped objects every guard and controller in this combo reads.
|
|
2
|
+
//
|
|
3
|
+
// req.auth — verified access-token claims: which user, which session.
|
|
4
|
+
// req.authz — the roles/permissions resolved for this request.
|
|
5
|
+
// req.ability — the same authorization as a CASL ability, derived from req.authz.permissions
|
|
6
|
+
// in the same guard that resolves them.
|
|
7
|
+
//
|
|
8
|
+
// Importing this module for its side effect installs the Express `Request` augmentation.
|
|
9
|
+
import type { AccessTokenClaims } from "@/lib/auth/core/types.js";
|
|
10
|
+
import type { AppAbility } from "./ability.js";
|
|
11
|
+
import type { AuthzContext } from "./authz.guard.js";
|
|
12
|
+
|
|
13
|
+
declare module "express" {
|
|
14
|
+
interface Request {
|
|
15
|
+
auth?: AccessTokenClaims;
|
|
16
|
+
authz?: AuthzContext;
|
|
17
|
+
ability?: AppAbility;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from "@nestjs/common";
|
|
2
|
+
import { Observable, map } from "rxjs";
|
|
3
|
+
import type { Request, Response } from "express";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Every successful response is wrapped the same way: the real HTTP status alongside the payload,
|
|
7
|
+
* so a client never has to special-case "was this a 200 or a 201" by inspecting the body — the
|
|
8
|
+
* envelope's `statusCode` and the transport's own status line always agree.
|
|
9
|
+
*/
|
|
10
|
+
export interface Envelope<T> {
|
|
11
|
+
success: true;
|
|
12
|
+
statusCode: number;
|
|
13
|
+
message: string;
|
|
14
|
+
data: T;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function deriveMessage(method: string): string {
|
|
18
|
+
switch (method) {
|
|
19
|
+
case "GET":
|
|
20
|
+
return "Fetched successfully";
|
|
21
|
+
case "POST":
|
|
22
|
+
return "Created successfully";
|
|
23
|
+
case "PATCH":
|
|
24
|
+
case "PUT":
|
|
25
|
+
return "Updated successfully";
|
|
26
|
+
case "DELETE":
|
|
27
|
+
return "Deleted successfully";
|
|
28
|
+
default:
|
|
29
|
+
return "Request successful";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@Injectable()
|
|
34
|
+
export class ResponseInterceptor<T> implements NestInterceptor<T, Envelope<T>> {
|
|
35
|
+
intercept(context: ExecutionContext, next: CallHandler<T>): Observable<Envelope<T>> {
|
|
36
|
+
const http = context.switchToHttp();
|
|
37
|
+
const req = http.getRequest<Request>();
|
|
38
|
+
const res = http.getResponse<Response>();
|
|
39
|
+
return next.handle().pipe(
|
|
40
|
+
map((data) => ({
|
|
41
|
+
success: true,
|
|
42
|
+
statusCode: res.statusCode,
|
|
43
|
+
message: deriveMessage(req.method),
|
|
44
|
+
data,
|
|
45
|
+
})),
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// Every route in this combo belongs to exactly one of three tiers, and says so on itself:
|
|
2
|
+
// @Public() (no auth), @Authenticated() (any logged-in user), @CheckAbility(...slugs) (every
|
|
3
|
+
// named slug required). `assertEveryRouteDeclaresATier` below fails startup, by name, for any
|
|
4
|
+
// route declaring none of the three — so an ungated admin route can't ship silently. The check
|
|
5
|
+
// also verifies the guards actually attached match the declared tier, so a route marked
|
|
6
|
+
// @Authenticated() with no AuthGuard can't masquerade as gated.
|
|
7
|
+
import { RequestMethod, SetMetadata } from "@nestjs/common";
|
|
8
|
+
import { GUARDS_METADATA, METHOD_METADATA, PATH_METADATA } from "@nestjs/common/constants.js";
|
|
9
|
+
import type { PermissionSlug } from "./rbac.defaults.js";
|
|
10
|
+
|
|
11
|
+
export const ROUTE_TIER_KEY = "routeTier";
|
|
12
|
+
export const CHECK_ABILITY_KEY = "checkAbility";
|
|
13
|
+
|
|
14
|
+
export type RouteTier = "public" | "authenticated" | "ability";
|
|
15
|
+
|
|
16
|
+
export const Public = () => SetMetadata(ROUTE_TIER_KEY, "public" satisfies RouteTier);
|
|
17
|
+
|
|
18
|
+
// Pair with @UseGuards(AuthGuard) — the startup check refuses a route that claims this tier
|
|
19
|
+
// without an authentication guard behind it.
|
|
20
|
+
export const Authenticated = () => SetMetadata(ROUTE_TIER_KEY, "authenticated" satisfies RouteTier);
|
|
21
|
+
|
|
22
|
+
// Requires AuthGuard, then the variant's authorization guard, then AbilityGuard. All named
|
|
23
|
+
// slugs must be held — the guard ANDs them. The argument is typed as `PermissionSlug` (the
|
|
24
|
+
// catalog in rbac.defaults.ts) so a route can't be gated on a slug nothing will ever grant.
|
|
25
|
+
export const CheckAbility = (...abilities: PermissionSlug[]) =>
|
|
26
|
+
applyBoth(SetMetadata(ROUTE_TIER_KEY, "ability" satisfies RouteTier), SetMetadata(CHECK_ABILITY_KEY, abilities as string[]));
|
|
27
|
+
|
|
28
|
+
/** `applyDecorators` from @nestjs/common, minus its class-decorator branch — these two are method decorators. */
|
|
29
|
+
function applyBoth(...decorators: MethodDecorator[]): MethodDecorator {
|
|
30
|
+
return (target, key, descriptor) => {
|
|
31
|
+
for (const decorate of decorators) decorate(target, key, descriptor);
|
|
32
|
+
return descriptor;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** A guard entry in `@UseGuards` may be a class or an already-constructed instance; both reduce to a class. */
|
|
37
|
+
type GuardEntry = Function | { constructor: Function };
|
|
38
|
+
|
|
39
|
+
const guardClassesOn = (target: object): Function[] =>
|
|
40
|
+
((Reflect.getMetadata(GUARDS_METADATA, target) as GuardEntry[] | undefined) ?? []).map((g) => (typeof g === "function" ? g : g.constructor));
|
|
41
|
+
|
|
42
|
+
const joinPath = (...parts: unknown[]) =>
|
|
43
|
+
"/" +
|
|
44
|
+
parts
|
|
45
|
+
.filter((p): p is string => typeof p === "string")
|
|
46
|
+
.flatMap((p) => p.split("/"))
|
|
47
|
+
.filter((segment) => segment.length > 0)
|
|
48
|
+
.join("/");
|
|
49
|
+
|
|
50
|
+
/** Own methods carrying Nest's route metadata. Controllers here are flat classes; inherited routes are not a shape this combo uses. */
|
|
51
|
+
function routeHandlersOf(controller: Function): Array<{ name: string; handler: Function }> {
|
|
52
|
+
const prototype = controller.prototype as Record<string, unknown>;
|
|
53
|
+
return Object.getOwnPropertyNames(prototype)
|
|
54
|
+
.filter((name) => name !== "constructor")
|
|
55
|
+
.map((name) => ({ name, handler: prototype[name] }))
|
|
56
|
+
.filter((entry): entry is { name: string; handler: Function } => typeof entry.handler === "function")
|
|
57
|
+
.filter((entry) => Reflect.getMetadata(PATH_METADATA, entry.handler) !== undefined);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Fail-closed at startup over the whole route table this library owns. Called from
|
|
61
|
+
// `AuthModule.forRoot` with the same array that populates `controllers:`, so nothing can be
|
|
62
|
+
// added to the module and left out of the check. Covers only this combo's own controllers, not
|
|
63
|
+
// the consuming application's.
|
|
64
|
+
export function assertEveryRouteDeclaresATier(controllers: readonly Function[], guards: { authentication: Function; ability: Function }): void {
|
|
65
|
+
const offenders: string[] = [];
|
|
66
|
+
|
|
67
|
+
for (const controller of controllers) {
|
|
68
|
+
const controllerPath = Reflect.getMetadata(PATH_METADATA, controller);
|
|
69
|
+
const controllerGuards = guardClassesOn(controller);
|
|
70
|
+
|
|
71
|
+
for (const { name, handler } of routeHandlersOf(controller)) {
|
|
72
|
+
const method = RequestMethod[Reflect.getMetadata(METHOD_METADATA, handler) as RequestMethod] ?? "?";
|
|
73
|
+
const route = `${method} ${joinPath(controllerPath, Reflect.getMetadata(PATH_METADATA, handler))} (${controller.name}.${name})`;
|
|
74
|
+
|
|
75
|
+
const tier: RouteTier | undefined = Reflect.getMetadata(ROUTE_TIER_KEY, handler) ?? Reflect.getMetadata(ROUTE_TIER_KEY, controller);
|
|
76
|
+
if (!tier) {
|
|
77
|
+
offenders.push(`${route} — declares no tier: add @Public(), @Authenticated(), or @CheckAbility(slug)`);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// A tier is a claim about what protects the route. Check the claim against the guards that
|
|
82
|
+
// are actually attached, so "marked but not wired" cannot masquerade as "gated".
|
|
83
|
+
const attached = [...controllerGuards, ...guardClassesOn(handler)];
|
|
84
|
+
const authenticated = attached.includes(guards.authentication);
|
|
85
|
+
const ability = attached.includes(guards.ability);
|
|
86
|
+
|
|
87
|
+
if (tier === "public" && (authenticated || ability)) offenders.push(`${route} — declares @Public() but is behind ${attached.map((g) => g.name).join(", ")}`);
|
|
88
|
+
if (tier === "authenticated" && !authenticated) offenders.push(`${route} — declares @Authenticated() but has no ${guards.authentication.name}`);
|
|
89
|
+
if (tier === "authenticated" && ability) offenders.push(`${route} — declares @Authenticated() but is behind ${guards.ability.name}; use @CheckAbility instead`);
|
|
90
|
+
if (tier === "ability" && !(authenticated && ability))
|
|
91
|
+
offenders.push(`${route} — declares @CheckAbility but is not behind ${guards.authentication.name} + ${guards.ability.name}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (offenders.length) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`${offenders.length} route(s) are not correctly tiered — refusing to start:\n ` +
|
|
98
|
+
offenders.join("\n ") +
|
|
99
|
+
`\n\nEvery route must be exactly one of: @Public() (no auth), @Authenticated() (any logged-in user), @CheckAbility(slug) (a permission).`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { Inject, Injectable } from "@nestjs/common";
|
|
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 { PrismaClient } from "../generated/prisma/client.js";
|
|
6
|
+
import { AUTH_CONFIG, AuthConfig } from "./auth.config.js";
|
|
7
|
+
import { AuditLogRepository } from "./audit-log.repository.js";
|
|
8
|
+
import { toId, toIdOrNull } from "./id.helper.js";
|
|
9
|
+
|
|
10
|
+
function toSessionRecord(row: {
|
|
11
|
+
id: bigint;
|
|
12
|
+
userId: bigint;
|
|
13
|
+
sessionVersion: number;
|
|
14
|
+
currentRefreshJti: string;
|
|
15
|
+
provider: string | null;
|
|
16
|
+
expiresAt: Date;
|
|
17
|
+
revokedAt: Date | null;
|
|
18
|
+
revokedBy: bigint | null;
|
|
19
|
+
revokedByIp: string | null;
|
|
20
|
+
createdAt: Date;
|
|
21
|
+
userAgent: string | null;
|
|
22
|
+
ip: string | null;
|
|
23
|
+
}): SessionRecord {
|
|
24
|
+
return {
|
|
25
|
+
id: row.id.toString(),
|
|
26
|
+
userId: row.userId.toString(),
|
|
27
|
+
sessionVersion: row.sessionVersion,
|
|
28
|
+
currentRefreshJti: row.currentRefreshJti,
|
|
29
|
+
provider: row.provider ?? undefined,
|
|
30
|
+
expiresAt: row.expiresAt.toISOString(),
|
|
31
|
+
revokedAt: row.revokedAt?.toISOString() ?? null,
|
|
32
|
+
revokedBy: row.revokedBy?.toString() ?? undefined,
|
|
33
|
+
revokedByIp: row.revokedByIp ?? undefined,
|
|
34
|
+
createdAt: row.createdAt.toISOString(),
|
|
35
|
+
userAgent: row.userAgent ?? undefined,
|
|
36
|
+
ip: row.ip ?? undefined,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// `isRevoked` is derived from `revokedAt` here, not stored as its own fact — one place computing
|
|
41
|
+
// it keeps the two columns honest.
|
|
42
|
+
const revocationFields = (revoker?: Revoker) => ({
|
|
43
|
+
revokedAt: new Date(),
|
|
44
|
+
isRevoked: true,
|
|
45
|
+
isActive: false,
|
|
46
|
+
revokedBy: toIdOrNull(revoker?.userId),
|
|
47
|
+
revokedByIp: revoker?.ip ?? null,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
@Injectable()
|
|
51
|
+
export class SessionRepository implements SessionStoreDeps {
|
|
52
|
+
constructor(
|
|
53
|
+
@Inject(PrismaClient) private readonly prisma: PrismaClient,
|
|
54
|
+
@Inject(AuditLogRepository) private readonly auditLog: AuditLogRepository,
|
|
55
|
+
@Inject(AUTH_CONFIG) private readonly config: AuthConfig,
|
|
56
|
+
) {}
|
|
57
|
+
|
|
58
|
+
async appendAuditEvent(event: AuditEvent): Promise<void> {
|
|
59
|
+
await this.auditLog.append(event);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async findSession(id: string): Promise<SessionRecord | null> {
|
|
63
|
+
const row = await this.prisma.session.findUnique({ where: { id: toId(id) } });
|
|
64
|
+
return row ? toSessionRecord(row) : null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async saveSession(session: SessionRecord): Promise<void> {
|
|
68
|
+
const revoked = session.revokedAt !== null;
|
|
69
|
+
await this.prisma.session.update({
|
|
70
|
+
where: { id: toId(session.id) },
|
|
71
|
+
data: {
|
|
72
|
+
sessionVersion: session.sessionVersion,
|
|
73
|
+
currentRefreshJti: session.currentRefreshJti,
|
|
74
|
+
revokedAt: session.revokedAt ? new Date(session.revokedAt) : null,
|
|
75
|
+
isRevoked: revoked,
|
|
76
|
+
isActive: !revoked,
|
|
77
|
+
revokedBy: toIdOrNull(session.revokedBy),
|
|
78
|
+
revokedByIp: session.revokedByIp ?? null,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// expiresAt is set once here and never rewritten — an absolute cap, not a sliding window.
|
|
84
|
+
// `id` is no longer client-supplied — the bigint PK auto-increments.
|
|
85
|
+
async createSession(input: { userId: string; userAgent?: string; ip?: string; provider?: string }): Promise<SessionRecord> {
|
|
86
|
+
const userIdBig = toId(input.userId);
|
|
87
|
+
const row = await this.prisma.session.create({
|
|
88
|
+
data: {
|
|
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
|
+
});
|
|
98
|
+
return toSessionRecord(row);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async revokeAllByUser(userId: string, revoker?: Revoker): Promise<void> {
|
|
102
|
+
await this.prisma.session.updateMany({
|
|
103
|
+
where: { userId: toId(userId), revokedAt: null },
|
|
104
|
+
data: { ...revocationFields(revoker), sessionVersion: { increment: 1 } },
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async revokeAllByUserExcept(userId: string, keepSessionId: string, revoker?: Revoker): Promise<void> {
|
|
109
|
+
await this.prisma.session.updateMany({
|
|
110
|
+
where: { userId: toId(userId), id: { not: toId(keepSessionId) }, revokedAt: null },
|
|
111
|
+
data: { ...revocationFields(revoker), sessionVersion: { increment: 1 } },
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async denylistJti(jti: string, ttlSeconds: number): Promise<void> {
|
|
116
|
+
await this.prisma.denylistedAccessToken.upsert({
|
|
117
|
+
where: { jti },
|
|
118
|
+
create: { jti, expiresAt: new Date(Date.now() + ttlSeconds * 1000) },
|
|
119
|
+
update: { expiresAt: new Date(Date.now() + ttlSeconds * 1000) },
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async isDenylisted(jti: string): Promise<boolean> {
|
|
124
|
+
const row = await this.prisma.denylistedAccessToken.findUnique({ where: { jti } });
|
|
125
|
+
if (!row) return false;
|
|
126
|
+
if (row.expiresAt.getTime() < Date.now()) return false; // expired; a cleanup job reaps these, see Phase 6
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Inject, Injectable } from "@nestjs/common";
|
|
2
|
+
import { verifyBackupCode } from "@/lib/auth/core/two-factor.js";
|
|
3
|
+
import { PrismaClient } from "../generated/prisma/client.js";
|
|
4
|
+
|
|
5
|
+
// Takes bigint directly, not string: every caller already has the user row's id in hand from
|
|
6
|
+
// its own Prisma lookup, so there's nothing to parse here.
|
|
7
|
+
@Injectable()
|
|
8
|
+
export class TwoFactorRepository {
|
|
9
|
+
constructor(@Inject(PrismaClient) private readonly prisma: PrismaClient) {}
|
|
10
|
+
|
|
11
|
+
/** Replaces any prior set — re-confirming enrollment invalidates old backup codes. */
|
|
12
|
+
async saveBackupCodes(userId: bigint, hashes: string[]): Promise<void> {
|
|
13
|
+
await this.prisma.twoFactorBackupCode.deleteMany({ where: { userId } });
|
|
14
|
+
await this.prisma.twoFactorBackupCode.createMany({ data: hashes.map((codeHash) => ({ userId, codeHash })) });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async consumeBackupCode(userId: bigint, code: string): Promise<boolean> {
|
|
18
|
+
const unused = await this.prisma.twoFactorBackupCode.findMany({ where: { userId, usedAt: null } });
|
|
19
|
+
const match = unused.find((row) => verifyBackupCode(code, row.codeHash));
|
|
20
|
+
if (!match) return false;
|
|
21
|
+
|
|
22
|
+
await this.prisma.twoFactorBackupCode.update({ where: { id: match.id }, data: { usedAt: new Date() } });
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async clearBackupCodes(userId: bigint): Promise<void> {
|
|
27
|
+
await this.prisma.twoFactorBackupCode.deleteMany({ where: { userId } });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import { Module } from "@nestjs/common";
|
|
3
|
+
import { NestFactory } from "@nestjs/core";
|
|
4
|
+
import { AuthModule } from "../src/auth.module.js";
|
|
5
|
+
import { InMemoryPermissionCacheStore } from "../src/permission-cache.js";
|
|
6
|
+
|
|
7
|
+
// No mailer is wired up for the proof, so this stands in for one — prove-cycle.ts reads the
|
|
8
|
+
// raw token back out of here the same way a test inbox would, to exercise the reset flow.
|
|
9
|
+
export const capturedResetTokens = new Map<string, string>();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The very store the app resolves permissions through. Exposed so `prove-cycle.ts` can assert
|
|
13
|
+
* that N identical authorized requests cause one database resolution rather than N — a cache
|
|
14
|
+
* nobody can prove is working is a bug surface, not an optimisation.
|
|
15
|
+
*/
|
|
16
|
+
export const permissionCacheStore = new InMemoryPermissionCacheStore();
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The app module is built *inside* `bootstrap()`, not at import time.
|
|
20
|
+
*
|
|
21
|
+
* `AuthModule.forRoot` is where the startup route-tier check runs, and the proof needs to call
|
|
22
|
+
* bootstrap twice: once with a deliberately untiered route present, expecting the boot to fail,
|
|
23
|
+
* and once without. A module built at import time would have run the check before the proof could
|
|
24
|
+
* arrange either case.
|
|
25
|
+
*/
|
|
26
|
+
export async function bootstrap(port: number) {
|
|
27
|
+
@Module({
|
|
28
|
+
imports: [
|
|
29
|
+
AuthModule.forRoot({
|
|
30
|
+
// Was 2 seconds, "so the proof can exercise expiry-adjacent paths quickly" — except no
|
|
31
|
+
// assertion in the proof waits for an access token to expire, so it bought nothing and
|
|
32
|
+
// cost a real 1-in-10 flake: the admin's token could die mid-section and surface as a
|
|
33
|
+
// confusing failure two assertions later. Long enough that expiry is never a variable,
|
|
34
|
+
// short enough to stay realistic. See `renewingToken` in test/harness.ts for the other
|
|
35
|
+
// half of the fix.
|
|
36
|
+
accessTokenTtlSeconds: 300,
|
|
37
|
+
// The proof fires hundreds of requests in well under a minute — the production buckets
|
|
38
|
+
// (100/1s, 200/10s, 400/60s) would 429 it partway through. One generous bucket keeps the
|
|
39
|
+
// throttler guard on every request without ever tripping, so its wiring is exercised
|
|
40
|
+
// rather than switched off.
|
|
41
|
+
throttle: [{ name: "proof", ttl: 1_000, limit: 100_000 }],
|
|
42
|
+
permissionCacheStore,
|
|
43
|
+
sendPasswordResetEmail: async (email, token) => {
|
|
44
|
+
capturedResetTokens.set(email, token);
|
|
45
|
+
},
|
|
46
|
+
}),
|
|
47
|
+
],
|
|
48
|
+
})
|
|
49
|
+
class AppModule {}
|
|
50
|
+
|
|
51
|
+
const app = await NestFactory.create(AppModule, { logger: false });
|
|
52
|
+
await app.listen(port);
|
|
53
|
+
return app;
|
|
54
|
+
}
|