@podosoft/podokit 0.2.0 → 0.4.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 +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/fullstack-nest-svelte/apps/api/package.json +29 -11
- package/dist/templates/fullstack-nest-svelte/apps/api/src/app.module.ts +3 -0
- package/dist/templates/fullstack-nest-svelte/apps/api/tsconfig.json +14 -5
- package/dist/templates/fullstack-nest-svelte/apps/web/package.json +8 -6
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/api.ts +5 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/alert/alert-action.svelte +20 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/alert/alert-description.svelte +23 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/alert/alert-title.svelte +23 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/alert/alert.svelte +43 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/alert/index.ts +17 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/avatar/avatar-badge.svelte +26 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/avatar/avatar-fallback.svelte +20 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/avatar/avatar-group-count.svelte +23 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/avatar/avatar-group.svelte +23 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/avatar/avatar-image.svelte +17 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/avatar/avatar.svelte +26 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/avatar/index.ts +22 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/badge/badge.svelte +49 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/badge/index.ts +2 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/dialog-close.svelte +11 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/dialog-content.svelte +48 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/dialog-description.svelte +17 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/dialog-footer.svelte +32 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/dialog-header.svelte +20 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/dialog-overlay.svelte +17 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/dialog-portal.svelte +7 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/dialog-title.svelte +17 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/dialog-trigger.svelte +11 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/dialog.svelte +7 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/index.ts +34 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +44 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte +31 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte +7 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte +27 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte +24 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte +7 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +34 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte +17 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte +20 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +17 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte +7 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte +7 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/index.ts +54 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/index.ts +37 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-content.svelte +45 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-group-heading.svelte +21 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-group.svelte +17 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-item.svelte +40 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-label.svelte +20 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-portal.svelte +7 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-scroll-down-button.svelte +20 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-scroll-up-button.svelte +20 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-separator.svelte +18 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-trigger.svelte +29 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select.svelte +11 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/separator/index.ts +7 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/separator/separator.svelte +23 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/skeleton/index.ts +7 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/skeleton/skeleton.svelte +17 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/sonner/index.ts +1 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/sonner/sonner.svelte +34 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/table/index.ts +28 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/table/table-body.svelte +15 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/table/table-caption.svelte +20 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/table/table-cell.svelte +15 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/table/table-footer.svelte +20 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/table/table-head.svelte +15 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/table/table-header.svelte +20 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/table/table-row.svelte +15 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/table/table.svelte +17 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tabs/index.ts +18 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tabs/tabs-content.svelte +17 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tabs/tabs-list.svelte +40 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tabs/tabs-trigger.svelte +23 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tabs/tabs.svelte +19 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tooltip/index.ts +19 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tooltip/tooltip-content.svelte +52 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tooltip/tooltip-portal.svelte +7 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tooltip/tooltip-provider.svelte +7 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tooltip/tooltip-trigger.svelte +7 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tooltip/tooltip.svelte +7 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/i18n/ko.ts +5 -2
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/server/api.ts +19 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/server/backend-proxy.ts +43 -4
- package/dist/templates/fullstack-nest-svelte/apps/web/src/routes/+page.svelte +8 -3
- package/dist/templates/fullstack-nest-svelte/apps/web/src/routes/api/[...path]/+server.ts +12 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/routes/api/auth/[...all]/+server.ts +12 -0
- package/dist/templates/fullstack-nest-svelte/dot-gitignore +5 -0
- package/dist/templates/fullstack-nest-svelte/package.json +11 -3
- package/dist/templates/fullstack-nest-svelte/tests/README.md +15 -0
- package/dist/templates/fullstack-nest-svelte/tests/api/health.api.spec.ts +14 -0
- package/dist/templates/fullstack-nest-svelte/tests/package.json +14 -0
- package/dist/templates/fullstack-nest-svelte/tests/playwright.config.ts +25 -0
- package/dist/templates/fullstack-nest-svelte/tests/ui/smoke.ui.spec.ts +8 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/app.d.ts +23 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/hooks.server.ts +20 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/app-sidebar.svelte +59 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/language-switch.svelte +33 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/sidebar-user-menu.svelte +44 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/table-pagination.svelte +40 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/theme-toggle.svelte +27 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/collapsible/collapsible-content.svelte +7 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/collapsible/collapsible-trigger.svelte +7 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/collapsible/collapsible.svelte +11 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/collapsible/index.ts +13 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/index.ts +31 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-content.svelte +20 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-ellipsis.svelte +22 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-item.svelte +14 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-link.svelte +42 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-next-button.svelte +31 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-next.svelte +26 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-prev-button.svelte +31 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-previous.svelte +26 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination.svelte +28 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/index.ts +34 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-close.svelte +7 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-content.svelte +55 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-description.svelte +17 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-footer.svelte +20 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-header.svelte +20 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-overlay.svelte +17 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-portal.svelte +7 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-title.svelte +17 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-trigger.svelte +7 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet.svelte +7 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/constants.ts +6 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/context.svelte.ts +81 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/index.ts +75 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-content.svelte +24 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-footer.svelte +21 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-group-action.svelte +33 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-group-content.svelte +21 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-group-label.svelte +33 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-group.svelte +21 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-header.svelte +21 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-input.svelte +21 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-inset.svelte +20 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-action.svelte +37 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte +24 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-button.svelte +102 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-item.svelte +21 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte +36 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte +39 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte +21 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte +21 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu.svelte +21 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-provider.svelte +53 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-rail.svelte +36 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-separator.svelte +19 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-trigger.svelte +36 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar.svelte +108 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/user-avatar.svelte +12 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/hooks/is-mobile.svelte.ts +9 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/index.ts +27 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/messages.ts +359 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/+layout.server.ts +14 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/+layout.svelte +50 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/+page.svelte +25 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/account/+page.server.ts +5 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/account/+page.svelte +374 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/audit/+page.server.ts +8 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/audit/+page.svelte +78 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/sessions/+page.server.ts +8 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/sessions/+page.svelte +122 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/users/+page.server.ts +9 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/users/+page.svelte +406 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/+layout.svelte +13 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/forgot-password/+page.svelte +54 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/login/+page.svelte +59 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/reset-password/+page.svelte +51 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/signup/+page.svelte +60 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/+layout.server.ts +22 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/+layout.svelte +20 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/+page.svelte +22 -0
- package/dist/templates/modules/admin-dashboard/files/tests/api/audit.api.spec.ts +48 -0
- package/dist/templates/modules/admin-dashboard/files/tests/api/auth.api.spec.ts +43 -0
- package/dist/templates/modules/admin-dashboard/files/tests/api/users-admin.api.spec.ts +134 -0
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/accounts.ts +6 -0
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/hydration.ts +9 -0
- package/dist/templates/modules/admin-dashboard/files/tests/playwright.config.ts +26 -0
- package/dist/templates/modules/admin-dashboard/files/tests/seed.setup.ts +29 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/account.ui.spec.ts +55 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/audit.ui.spec.ts +16 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/auth.ui.spec.ts +45 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/dashboard.ui.spec.ts +25 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/guards.ui.spec.ts +21 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/i18n.ui.spec.ts +13 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/sessions.ui.spec.ts +15 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/sidebar.ui.spec.ts +16 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/users-crud.ui.spec.ts +129 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/users.ui.spec.ts +19 -0
- package/dist/templates/modules/admin-dashboard/module.manifest.json +41 -0
- package/dist/templates/modules/api-key-auth/files/apps/api/src/api-key/api-key.decorator.ts +9 -0
- package/dist/templates/modules/api-key-auth/files/apps/api/src/api-key/api-key.guard.ts +29 -0
- package/dist/templates/modules/api-key-auth/files/apps/api/src/api-key/api-key.module.ts +9 -0
- package/dist/templates/modules/api-key-auth/files/apps/api/src/machine/machine.controller.ts +14 -0
- package/dist/templates/modules/api-key-auth/module.manifest.json +19 -0
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/README.md +59 -0
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit-events.ts +34 -0
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit-hook.ts +70 -0
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit-log.entity.ts +46 -0
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.controller.ts +21 -0
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.decorator.ts +23 -0
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.interceptor.ts +54 -0
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.module.ts +14 -0
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.service.ts +36 -0
- package/dist/templates/modules/audit-log/files/apps/api/src/migrations/1720300000000-InitAuditLogs.ts +31 -0
- package/dist/templates/modules/audit-log/module.manifest.json +39 -0
- package/dist/templates/modules/auth/files/apps/api/src/account/account.controller.ts +30 -0
- package/dist/templates/modules/auth/files/apps/api/src/account/account.module.ts +5 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth.ts +59 -0
- package/dist/templates/modules/auth/files/apps/api/src/health/health.controller.ts +26 -0
- package/dist/templates/modules/auth/module.manifest.json +70 -0
- package/dist/templates/modules/bullmq/module.manifest.json +11 -3
- package/dist/templates/modules/logging/files/apps/api/src/logging/logging.module.ts +29 -0
- package/dist/templates/modules/logging/module.manifest.json +27 -0
- package/dist/templates/modules/rate-limit/files/apps/api/src/rate-limit/rate-limit.module.ts +26 -0
- package/dist/templates/modules/rate-limit/module.manifest.json +25 -0
- package/dist/templates/todo/apps/api/package.json +29 -11
- package/dist/templates/todo/apps/api/src/app.module.ts +3 -0
- package/dist/templates/todo/apps/api/tsconfig.json +14 -5
- package/dist/templates/todo/apps/web/package.json +8 -6
- package/dist/templates/todo/apps/web/src/lib/api.ts +5 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/alert/alert-action.svelte +20 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/alert/alert-description.svelte +23 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/alert/alert-title.svelte +23 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/alert/alert.svelte +43 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/alert/index.ts +17 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/avatar/avatar-badge.svelte +26 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/avatar/avatar-fallback.svelte +20 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/avatar/avatar-group-count.svelte +23 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/avatar/avatar-group.svelte +23 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/avatar/avatar-image.svelte +17 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/avatar/avatar.svelte +26 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/avatar/index.ts +22 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/badge/badge.svelte +49 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/badge/index.ts +2 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/dialog-close.svelte +11 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/dialog-content.svelte +48 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/dialog-description.svelte +17 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/dialog-footer.svelte +32 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/dialog-header.svelte +20 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/dialog-overlay.svelte +17 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/dialog-portal.svelte +7 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/dialog-title.svelte +17 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/dialog-trigger.svelte +11 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/dialog.svelte +7 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/index.ts +34 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +44 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte +31 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte +7 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte +27 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte +24 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte +7 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +34 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte +17 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte +20 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +17 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte +7 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte +7 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/index.ts +54 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/select/index.ts +37 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/select/select-content.svelte +45 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/select/select-group-heading.svelte +21 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/select/select-group.svelte +17 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/select/select-item.svelte +40 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/select/select-label.svelte +20 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/select/select-portal.svelte +7 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/select/select-scroll-down-button.svelte +20 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/select/select-scroll-up-button.svelte +20 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/select/select-separator.svelte +18 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/select/select-trigger.svelte +29 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/select/select.svelte +11 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/separator/index.ts +7 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/separator/separator.svelte +23 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/skeleton/index.ts +7 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/skeleton/skeleton.svelte +17 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/sonner/index.ts +1 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/sonner/sonner.svelte +34 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/table/index.ts +28 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/table/table-body.svelte +15 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/table/table-caption.svelte +20 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/table/table-cell.svelte +15 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/table/table-footer.svelte +20 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/table/table-head.svelte +15 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/table/table-header.svelte +20 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/table/table-row.svelte +15 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/table/table.svelte +17 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/tabs/index.ts +18 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/tabs/tabs-content.svelte +17 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/tabs/tabs-list.svelte +40 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/tabs/tabs-trigger.svelte +23 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/tabs/tabs.svelte +19 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/tooltip/index.ts +19 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/tooltip/tooltip-content.svelte +52 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/tooltip/tooltip-portal.svelte +7 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/tooltip/tooltip-provider.svelte +7 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/tooltip/tooltip-trigger.svelte +7 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/tooltip/tooltip.svelte +7 -0
- package/dist/templates/todo/apps/web/src/lib/i18n/ko.ts +5 -2
- package/dist/templates/todo/apps/web/src/lib/server/api.ts +19 -0
- package/dist/templates/todo/apps/web/src/lib/server/backend-proxy.ts +43 -4
- package/dist/templates/todo/apps/web/src/routes/+page.svelte +19 -18
- package/dist/templates/todo/apps/web/src/routes/api/[...path]/+server.ts +12 -0
- package/dist/templates/todo/apps/web/src/routes/api/auth/[...all]/+server.ts +12 -0
- package/dist/templates/todo/dot-gitignore +5 -0
- package/dist/templates/todo/package.json +11 -3
- package/dist/templates/todo/tests/README.md +15 -0
- package/dist/templates/todo/tests/api/health.api.spec.ts +14 -0
- package/dist/templates/todo/tests/package.json +14 -0
- package/dist/templates/todo/tests/playwright.config.ts +25 -0
- package/dist/templates/todo/tests/ui/smoke.ui.spec.ts +8 -0
- package/package.json +2 -2
- package/dist/templates/fullstack-nest-svelte/apps/api/test/health.e2e-spec.ts +0 -23
- package/dist/templates/fullstack-nest-svelte/apps/web/src/routes/api/health/+server.ts +0 -12
- package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/auth.controller.ts +0 -31
- package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/auth.module.ts +0 -22
- package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/auth.service.ts +0 -44
- package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/dto/login.dto.ts +0 -12
- package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/dto/register.dto.ts +0 -13
- package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/jwt-auth.guard.ts +0 -5
- package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/jwt.strategy.ts +0 -23
- package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/user.entity.ts +0 -16
- package/dist/templates/modules/auth-jwt/files/apps/api/src/migrations/1720200000000-InitUsers.ts +0 -23
- package/dist/templates/modules/auth-jwt/module.manifest.json +0 -31
- package/dist/templates/todo/apps/api/test/health.e2e-spec.ts +0 -23
- package/dist/templates/todo/apps/web/src/routes/api/health/+server.ts +0 -12
- package/dist/templates/todo/apps/web/src/routes/api/todos/+server.ts +0 -24
- package/dist/templates/todo/apps/web/src/routes/api/todos/[id]/+server.ts +0 -24
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class InitAuditLogs1720300000000 implements MigrationInterface {
|
|
4
|
+
name = "InitAuditLogs1720300000000";
|
|
5
|
+
|
|
6
|
+
async up(queryRunner: QueryRunner): Promise<void> {
|
|
7
|
+
await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS "pgcrypto"`);
|
|
8
|
+
await queryRunner.query(`
|
|
9
|
+
CREATE TABLE "audit_logs" (
|
|
10
|
+
"id" uuid NOT NULL DEFAULT gen_random_uuid(),
|
|
11
|
+
"action" character varying(128) NOT NULL,
|
|
12
|
+
"actorId" character varying,
|
|
13
|
+
"actorName" character varying,
|
|
14
|
+
"actorEmail" character varying,
|
|
15
|
+
"targetType" character varying,
|
|
16
|
+
"targetId" character varying,
|
|
17
|
+
"targetLabel" character varying,
|
|
18
|
+
"ip" character varying,
|
|
19
|
+
"metadata" jsonb,
|
|
20
|
+
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
21
|
+
CONSTRAINT "PK_audit_logs_id" PRIMARY KEY ("id")
|
|
22
|
+
)
|
|
23
|
+
`);
|
|
24
|
+
await queryRunner.query(`CREATE INDEX "IDX_audit_logs_action" ON "audit_logs" ("action")`);
|
|
25
|
+
await queryRunner.query(`CREATE INDEX "IDX_audit_logs_createdAt" ON "audit_logs" ("createdAt")`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async down(queryRunner: QueryRunner): Promise<void> {
|
|
29
|
+
await queryRunner.query(`DROP TABLE "audit_logs"`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "audit-log",
|
|
3
|
+
"description": "Audit logging: records who did each mutating request (who/method/path/status) to an audit_logs table — a NestJS interceptor for your own API routes plus a better-auth hook for auth/admin actions (login, ban, role changes, ...). Requires auth.",
|
|
4
|
+
"requires": [
|
|
5
|
+
"auth"
|
|
6
|
+
],
|
|
7
|
+
"targetApp": "api",
|
|
8
|
+
"env": [
|
|
9
|
+
"# audit-log: surfaces the admin Audit log page in the dashboard",
|
|
10
|
+
"AUDIT_LOG_ENABLED=true"
|
|
11
|
+
],
|
|
12
|
+
"inject": [
|
|
13
|
+
{
|
|
14
|
+
"file": "apps/api/src/app.module.ts",
|
|
15
|
+
"marker": "// podokit:imports",
|
|
16
|
+
"text": "import { AuditModule } from \"./audit/audit.module\";"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"file": "apps/api/src/app.module.ts",
|
|
20
|
+
"marker": "// podokit:module-imports",
|
|
21
|
+
"text": "AuditModule,"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"file": "apps/api/src/auth/auth.ts",
|
|
25
|
+
"marker": "// podokit:auth-imports",
|
|
26
|
+
"text": "import { auditAfterHook } from \"../audit/audit-hook\";"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"file": "apps/api/src/auth/auth.ts",
|
|
30
|
+
"marker": "// podokit:auth-options",
|
|
31
|
+
"text": "hooks: { after: auditAfterHook },"
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"instructions": [
|
|
35
|
+
"Run migrations to create the audit_logs table: npm run migration:run -w <app>-api",
|
|
36
|
+
"Auth/admin actions (login, ban, role changes, session revocation, user CRUD) are recorded via a better-auth hook; your own mutating NestJS routes are recorded by the interceptor.",
|
|
37
|
+
"List recent entries (admin session required): curl -b cookies.txt localhost:3000/audit-logs"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Controller, Get } from "@nestjs/common";
|
|
2
|
+
import { Session, type UserSession } from "@thallesp/nestjs-better-auth";
|
|
3
|
+
import { ApiTags } from "@nestjs/swagger";
|
|
4
|
+
|
|
5
|
+
type Capabilities = { twoFactor: boolean; providers: string[]; deleteAccount: boolean; auditLog: boolean };
|
|
6
|
+
|
|
7
|
+
// Protected by the global AuthGuard. Use @Session() to read the current user.
|
|
8
|
+
@ApiTags("account")
|
|
9
|
+
@Controller("account")
|
|
10
|
+
export class AccountController {
|
|
11
|
+
@Get("me")
|
|
12
|
+
me(@Session() session: UserSession) {
|
|
13
|
+
return session.user;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Which optional auth features are enabled, so the UI can show/hide sections.
|
|
17
|
+
@Get("capabilities")
|
|
18
|
+
capabilities(): Capabilities {
|
|
19
|
+
const providers = [
|
|
20
|
+
process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET ? "google" : null,
|
|
21
|
+
process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET ? "github" : null,
|
|
22
|
+
].filter((p): p is string => p !== null);
|
|
23
|
+
return {
|
|
24
|
+
twoFactor: process.env.AUTH_TWO_FACTOR === "true",
|
|
25
|
+
providers,
|
|
26
|
+
deleteAccount: process.env.AUTH_ALLOW_DELETE === "true",
|
|
27
|
+
auditLog: process.env.AUDIT_LOG_ENABLED === "true",
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { betterAuth, type BetterAuthOptions, type BetterAuthPlugin } from "better-auth";
|
|
2
|
+
import { twoFactor } from "better-auth/plugins";
|
|
3
|
+
import { Pool } from "pg";
|
|
4
|
+
// podokit:auth-imports
|
|
5
|
+
|
|
6
|
+
// OAuth providers are enabled only when their credentials are present.
|
|
7
|
+
function socialProviders(): NonNullable<BetterAuthOptions["socialProviders"]> {
|
|
8
|
+
const providers: NonNullable<BetterAuthOptions["socialProviders"]> = {};
|
|
9
|
+
if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) {
|
|
10
|
+
providers.google = {
|
|
11
|
+
clientId: process.env.GOOGLE_CLIENT_ID,
|
|
12
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
if (process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET) {
|
|
16
|
+
providers.github = {
|
|
17
|
+
clientId: process.env.GITHUB_CLIENT_ID,
|
|
18
|
+
clientSecret: process.env.GITHUB_CLIENT_SECRET,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return providers;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Plugins are collected here so other PodoKit modules can add their own.
|
|
25
|
+
const plugins: BetterAuthPlugin[] = [];
|
|
26
|
+
if (process.env.AUTH_TWO_FACTOR === "true") {
|
|
27
|
+
plugins.push(twoFactor());
|
|
28
|
+
}
|
|
29
|
+
// podokit:auth-plugins
|
|
30
|
+
|
|
31
|
+
export const auth = betterAuth({
|
|
32
|
+
database: new Pool({
|
|
33
|
+
host: process.env.POSTGRES_HOST ?? "localhost",
|
|
34
|
+
port: Number(process.env.POSTGRES_PORT ?? 5432),
|
|
35
|
+
user: process.env.POSTGRES_USER ?? "podokit",
|
|
36
|
+
password: process.env.POSTGRES_PASSWORD ?? "podokit",
|
|
37
|
+
database: process.env.POSTGRES_DB ?? "podokit",
|
|
38
|
+
}),
|
|
39
|
+
emailAndPassword: {
|
|
40
|
+
enabled: true,
|
|
41
|
+
// podokit:auth-email-password
|
|
42
|
+
},
|
|
43
|
+
socialProviders: socialProviders(),
|
|
44
|
+
plugins,
|
|
45
|
+
secret: process.env.BETTER_AUTH_SECRET ?? "change-me-in-production-min-32-characters",
|
|
46
|
+
baseURL: process.env.BETTER_AUTH_URL ?? "http://localhost:3000",
|
|
47
|
+
advanced: {
|
|
48
|
+
ipAddress: {
|
|
49
|
+
// The API sits behind the SvelteKit server proxy, which forwards the
|
|
50
|
+
// resolved client IP as x-forwarded-for; trust it so sessions record an IP.
|
|
51
|
+
ipAddressHeaders: ["x-forwarded-for"],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
user: {
|
|
55
|
+
// Self-service account deletion — opt in with AUTH_ALLOW_DELETE=true.
|
|
56
|
+
deleteUser: { enabled: process.env.AUTH_ALLOW_DELETE === "true" },
|
|
57
|
+
},
|
|
58
|
+
// podokit:auth-options
|
|
59
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Controller, Get } from "@nestjs/common";
|
|
2
|
+
import { InjectDataSource } from "@nestjs/typeorm";
|
|
3
|
+
import { DataSource } from "typeorm";
|
|
4
|
+
import { Public } from "@thallesp/nestjs-better-auth";
|
|
5
|
+
|
|
6
|
+
// Health checks stay public under the global auth guard.
|
|
7
|
+
@Public()
|
|
8
|
+
@Controller("health")
|
|
9
|
+
export class HealthController {
|
|
10
|
+
constructor(@InjectDataSource() private readonly dataSource: DataSource) {}
|
|
11
|
+
|
|
12
|
+
@Get()
|
|
13
|
+
liveness(): { status: string; uptime: number; timestamp: string } {
|
|
14
|
+
return { status: "ok", uptime: process.uptime(), timestamp: new Date().toISOString() };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@Get("ready")
|
|
18
|
+
async readiness(): Promise<{ status: string; db: string }> {
|
|
19
|
+
try {
|
|
20
|
+
await this.dataSource.query("SELECT 1");
|
|
21
|
+
return { status: "ready", db: "up" };
|
|
22
|
+
} catch {
|
|
23
|
+
return { status: "degraded", db: "down" };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "auth",
|
|
3
|
+
"description": "Full authentication (better-auth): email/password, sessions, and \u2014 via config \u2014 OAuth and 2FA. Installs a global guard so the API is secure by default; health and /api/auth stay public.",
|
|
4
|
+
"requires": [],
|
|
5
|
+
"targetApp": "api",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"better-auth": "^1.6.23",
|
|
8
|
+
"@thallesp/nestjs-better-auth": "^2.7.0",
|
|
9
|
+
"pg": "^8.12.0"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@types/pg": "^8.11.10"
|
|
13
|
+
},
|
|
14
|
+
"env": [
|
|
15
|
+
"# auth (better-auth) \u2014 set a strong secret (>= 32 chars) in production",
|
|
16
|
+
"BETTER_AUTH_SECRET=change-me-in-production-min-32-characters",
|
|
17
|
+
"BETTER_AUTH_URL=http://localhost:3000",
|
|
18
|
+
"# 2FA (TOTP): set true to enable the plugin",
|
|
19
|
+
"AUTH_TWO_FACTOR=false",
|
|
20
|
+
"# OAuth (optional) \u2014 set a provider's id+secret to enable it",
|
|
21
|
+
"# GOOGLE_CLIENT_ID=",
|
|
22
|
+
"# GOOGLE_CLIENT_SECRET=",
|
|
23
|
+
"# GITHUB_CLIENT_ID=",
|
|
24
|
+
"# GITHUB_CLIENT_SECRET="
|
|
25
|
+
],
|
|
26
|
+
"inject": [
|
|
27
|
+
{
|
|
28
|
+
"file": "apps/api/src/app.module.ts",
|
|
29
|
+
"marker": "// podokit:imports",
|
|
30
|
+
"text": "import { APP_GUARD } from \"@nestjs/core\";"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"file": "apps/api/src/app.module.ts",
|
|
34
|
+
"marker": "// podokit:imports",
|
|
35
|
+
"text": "import { AuthModule, AuthGuard } from \"@thallesp/nestjs-better-auth\";"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"file": "apps/api/src/app.module.ts",
|
|
39
|
+
"marker": "// podokit:imports",
|
|
40
|
+
"text": "import { auth } from \"./auth/auth\";"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"file": "apps/api/src/app.module.ts",
|
|
44
|
+
"marker": "// podokit:imports",
|
|
45
|
+
"text": "import { AccountModule } from \"./account/account.module\";"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"file": "apps/api/src/app.module.ts",
|
|
49
|
+
"marker": "// podokit:module-imports",
|
|
50
|
+
"text": "AuthModule.forRoot(auth),"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"file": "apps/api/src/app.module.ts",
|
|
54
|
+
"marker": "// podokit:module-imports",
|
|
55
|
+
"text": "AccountModule,"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"file": "apps/api/src/app.module.ts",
|
|
59
|
+
"marker": "// podokit:providers",
|
|
60
|
+
"text": "{ provide: APP_GUARD, useClass: AuthGuard },"
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
"instructions": [
|
|
64
|
+
"Set BETTER_AUTH_SECRET in .env (>= 32 chars).",
|
|
65
|
+
"Create the auth tables: npx @better-auth/cli migrate -y --config apps/api/src/auth/auth.ts",
|
|
66
|
+
"Sign up: curl -c cookies.txt -XPOST localhost:3000/api/auth/sign-up/email -H 'content-type: application/json' -d '{\"email\":\"a@example.com\",\"password\":\"password123\",\"name\":\"A\"}'",
|
|
67
|
+
"Call a protected route with the session: curl -b cookies.txt localhost:3000/account/me",
|
|
68
|
+
"Everything is now protected by default; mark public routes with @Public(). Enable OAuth by setting a provider's env vars; enable 2FA with AUTH_TWO_FACTOR=true."
|
|
69
|
+
]
|
|
70
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"requires": [],
|
|
5
5
|
"targetApp": "api",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@nestjs/bullmq": "^
|
|
7
|
+
"@nestjs/bullmq": "^11.0.4",
|
|
8
8
|
"bullmq": "^5.34.0"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
@@ -15,8 +15,16 @@
|
|
|
15
15
|
"# bullmq uses REDIS_HOST / REDIS_PORT (already defined above)"
|
|
16
16
|
],
|
|
17
17
|
"inject": [
|
|
18
|
-
{
|
|
19
|
-
|
|
18
|
+
{
|
|
19
|
+
"file": "apps/api/src/app.module.ts",
|
|
20
|
+
"marker": "// podokit:imports",
|
|
21
|
+
"text": "import { JobsModule } from \"./jobs/jobs.module\";"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"file": "apps/api/src/app.module.ts",
|
|
25
|
+
"marker": "// podokit:module-imports",
|
|
26
|
+
"text": "JobsModule,"
|
|
27
|
+
}
|
|
20
28
|
],
|
|
21
29
|
"instructions": [
|
|
22
30
|
"Ensure Redis is running: docker compose -f infra/docker/docker-compose.yml up -d",
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Module } from "@nestjs/common";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
4
|
+
import { LoggerModule } from "nestjs-pino";
|
|
5
|
+
|
|
6
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
7
|
+
|
|
8
|
+
// Structured request logging with a correlation id. Importing this module
|
|
9
|
+
// installs the pino-http middleware for all routes.
|
|
10
|
+
@Module({
|
|
11
|
+
imports: [
|
|
12
|
+
LoggerModule.forRoot({
|
|
13
|
+
pinoHttp: {
|
|
14
|
+
level: process.env.LOG_LEVEL ?? "info",
|
|
15
|
+
// Pretty, single-line logs in dev; JSON in production.
|
|
16
|
+
transport: isProduction ? undefined : { target: "pino-pretty", options: { singleLine: true } },
|
|
17
|
+
autoLogging: true,
|
|
18
|
+
// One id per request; honor an inbound x-request-id and echo it back.
|
|
19
|
+
genReqId: (req: IncomingMessage, res: ServerResponse): string => {
|
|
20
|
+
const header = req.headers["x-request-id"];
|
|
21
|
+
const id = (Array.isArray(header) ? header[0] : header) ?? randomUUID();
|
|
22
|
+
res.setHeader("x-request-id", id);
|
|
23
|
+
return id;
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
}),
|
|
27
|
+
],
|
|
28
|
+
})
|
|
29
|
+
export class LoggingModule {}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "logging",
|
|
3
|
+
"description": "Structured request logging (nestjs-pino) with a per-request correlation id (x-request-id). Pretty in dev, JSON in production.",
|
|
4
|
+
"requires": [],
|
|
5
|
+
"targetApp": "api",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"nestjs-pino": "^4.3.0",
|
|
8
|
+
"pino-http": "^10.3.0"
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"pino-pretty": "^13.0.0"
|
|
12
|
+
},
|
|
13
|
+
"env": [
|
|
14
|
+
"# logging",
|
|
15
|
+
"LOG_LEVEL=info"
|
|
16
|
+
],
|
|
17
|
+
"inject": [
|
|
18
|
+
{ "file": "apps/api/src/app.module.ts", "marker": "// podokit:imports", "text": "import { LoggingModule } from \"./logging/logging.module\";" },
|
|
19
|
+
{ "file": "apps/api/src/app.module.ts", "marker": "// podokit:module-imports", "text": "LoggingModule," }
|
|
20
|
+
],
|
|
21
|
+
"instructions": [
|
|
22
|
+
"Every HTTP request is now logged as structured JSON with a correlation id.",
|
|
23
|
+
"Make a request and watch the API logs: curl localhost:3000/health",
|
|
24
|
+
"Set LOG_LEVEL (debug|info|warn|error) in .env. Logs are pretty in dev and JSON when NODE_ENV=production.",
|
|
25
|
+
"To route Nest's own logs through pino too, add to main.ts: NestFactory.create(AppModule, { bufferLogs: true }) then app.useLogger(app.get(Logger))."
|
|
26
|
+
]
|
|
27
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Module } from "@nestjs/common";
|
|
2
|
+
import { APP_GUARD } from "@nestjs/core";
|
|
3
|
+
import { ThrottlerGuard, ThrottlerModule } from "@nestjs/throttler";
|
|
4
|
+
import { ThrottlerStorageRedisService } from "@nest-lab/throttler-storage-redis";
|
|
5
|
+
import Redis from "ioredis";
|
|
6
|
+
|
|
7
|
+
const ttlSeconds = Number(process.env.RATE_LIMIT_TTL ?? 60);
|
|
8
|
+
const limit = Number(process.env.RATE_LIMIT_MAX ?? 100);
|
|
9
|
+
|
|
10
|
+
// Distributed rate limiting: the counter lives in Redis, so the limit holds
|
|
11
|
+
// across API replicas. Registers a global guard (429 when exceeded).
|
|
12
|
+
@Module({
|
|
13
|
+
imports: [
|
|
14
|
+
ThrottlerModule.forRoot({
|
|
15
|
+
throttlers: [{ ttl: ttlSeconds * 1000, limit }],
|
|
16
|
+
storage: new ThrottlerStorageRedisService(
|
|
17
|
+
new Redis({
|
|
18
|
+
host: process.env.REDIS_HOST ?? "localhost",
|
|
19
|
+
port: Number(process.env.REDIS_PORT ?? 6379),
|
|
20
|
+
}),
|
|
21
|
+
),
|
|
22
|
+
}),
|
|
23
|
+
],
|
|
24
|
+
providers: [{ provide: APP_GUARD, useClass: ThrottlerGuard }],
|
|
25
|
+
})
|
|
26
|
+
export class RateLimitModule {}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rate-limit",
|
|
3
|
+
"description": "Rate limiting with @nestjs/throttler backed by Redis (correct across API replicas). Installs a global throttler guard; configurable via env.",
|
|
4
|
+
"requires": ["redis"],
|
|
5
|
+
"targetApp": "api",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"@nestjs/throttler": "^6.5.0",
|
|
8
|
+
"@nest-lab/throttler-storage-redis": "^1.2.0"
|
|
9
|
+
},
|
|
10
|
+
"env": [
|
|
11
|
+
"# rate limit (window in seconds, max requests per window per client)",
|
|
12
|
+
"RATE_LIMIT_TTL=60",
|
|
13
|
+
"RATE_LIMIT_MAX=100"
|
|
14
|
+
],
|
|
15
|
+
"inject": [
|
|
16
|
+
{ "file": "apps/api/src/app.module.ts", "marker": "// podokit:imports", "text": "import { RateLimitModule } from \"./rate-limit/rate-limit.module\";" },
|
|
17
|
+
{ "file": "apps/api/src/app.module.ts", "marker": "// podokit:module-imports", "text": "RateLimitModule," }
|
|
18
|
+
],
|
|
19
|
+
"instructions": [
|
|
20
|
+
"Ensure Redis is running: docker compose -f infra/docker/docker-compose.yml up -d",
|
|
21
|
+
"Tune RATE_LIMIT_TTL / RATE_LIMIT_MAX in .env.",
|
|
22
|
+
"Exceeding the limit returns HTTP 429. Skip a route with @SkipThrottle() or tune it with @Throttle().",
|
|
23
|
+
"The limit is shared across API replicas because it is stored in Redis."
|
|
24
|
+
]
|
|
25
|
+
}
|
|
@@ -7,19 +7,19 @@
|
|
|
7
7
|
"build": "nest build",
|
|
8
8
|
"start": "node dist/main",
|
|
9
9
|
"lint": "tsc -p tsconfig.json --noEmit",
|
|
10
|
-
"test": "jest",
|
|
10
|
+
"test": "jest --passWithNoTests",
|
|
11
11
|
"typeorm": "typeorm-ts-node-commonjs -d src/database/data-source.ts",
|
|
12
12
|
"migration:run": "npm run typeorm -- migration:run",
|
|
13
13
|
"migration:revert": "npm run typeorm -- migration:revert",
|
|
14
14
|
"migration:generate": "npm run typeorm -- migration:generate"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@nestjs/common": "^
|
|
18
|
-
"@nestjs/config": "^
|
|
19
|
-
"@nestjs/core": "^
|
|
20
|
-
"@nestjs/platform-express": "^
|
|
21
|
-
"@nestjs/swagger": "^
|
|
22
|
-
"@nestjs/typeorm": "^
|
|
17
|
+
"@nestjs/common": "^11.1.27",
|
|
18
|
+
"@nestjs/config": "^4.0.4",
|
|
19
|
+
"@nestjs/core": "^11.1.27",
|
|
20
|
+
"@nestjs/platform-express": "^11.1.27",
|
|
21
|
+
"@nestjs/swagger": "^11.4.5",
|
|
22
|
+
"@nestjs/typeorm": "^11.0.3",
|
|
23
23
|
"class-transformer": "^0.5.1",
|
|
24
24
|
"class-validator": "^0.14.1",
|
|
25
25
|
"dotenv": "^16.4.5",
|
|
@@ -30,15 +30,33 @@
|
|
|
30
30
|
"zod": "^3.23.8"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@nestjs/cli": "^
|
|
34
|
-
"@nestjs/testing": "^
|
|
35
|
-
"@types/express": "^
|
|
33
|
+
"@nestjs/cli": "^11.0.23",
|
|
34
|
+
"@nestjs/testing": "^11.1.27",
|
|
35
|
+
"@types/express": "^5.0.1",
|
|
36
36
|
"@types/node": "^20.17.0",
|
|
37
37
|
"@types/supertest": "^6.0.2",
|
|
38
38
|
"jest": "^29.7.0",
|
|
39
39
|
"supertest": "^7.0.0",
|
|
40
40
|
"ts-jest": "^29.2.5",
|
|
41
41
|
"ts-node": "^10.9.2",
|
|
42
|
-
"typescript": "^
|
|
42
|
+
"typescript": "^6.0.3"
|
|
43
|
+
},
|
|
44
|
+
"jest": {
|
|
45
|
+
"moduleFileExtensions": [
|
|
46
|
+
"js",
|
|
47
|
+
"json",
|
|
48
|
+
"ts"
|
|
49
|
+
],
|
|
50
|
+
"rootDir": "src",
|
|
51
|
+
"testRegex": ".*\\.spec\\.ts$",
|
|
52
|
+
"transform": {
|
|
53
|
+
"^.+\\.ts$": [
|
|
54
|
+
"ts-jest",
|
|
55
|
+
{
|
|
56
|
+
"tsconfig": "tsconfig.json"
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
"testEnvironment": "node"
|
|
43
61
|
}
|
|
44
62
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"module": "
|
|
3
|
+
"module": "nodenext",
|
|
4
4
|
"target": "ES2022",
|
|
5
|
-
"moduleResolution": "
|
|
5
|
+
"moduleResolution": "nodenext",
|
|
6
6
|
"outDir": "dist",
|
|
7
7
|
"rootDir": "src",
|
|
8
8
|
"declaration": false,
|
|
@@ -14,8 +14,17 @@
|
|
|
14
14
|
"noImplicitReturns": true,
|
|
15
15
|
"esModuleInterop": true,
|
|
16
16
|
"skipLibCheck": true,
|
|
17
|
-
"forceConsistentCasingInFileNames": true
|
|
17
|
+
"forceConsistentCasingInFileNames": true,
|
|
18
|
+
"types": [
|
|
19
|
+
"node"
|
|
20
|
+
]
|
|
18
21
|
},
|
|
19
|
-
"include": [
|
|
20
|
-
|
|
22
|
+
"include": [
|
|
23
|
+
"src/**/*.ts"
|
|
24
|
+
],
|
|
25
|
+
"exclude": [
|
|
26
|
+
"node_modules",
|
|
27
|
+
"dist",
|
|
28
|
+
"test"
|
|
29
|
+
]
|
|
21
30
|
}
|
|
@@ -8,16 +8,18 @@
|
|
|
8
8
|
"build": "vite build",
|
|
9
9
|
"preview": "vite preview",
|
|
10
10
|
"lint": "svelte-check --tsconfig ./tsconfig.json",
|
|
11
|
-
"test": "echo \"add
|
|
11
|
+
"test": "echo \"unit: add vitest; e2e: npm run test:e2e (see tests/)\" && exit 0"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"
|
|
14
|
+
"@internationalized/date": "^3.12.2",
|
|
15
|
+
"@lucide/svelte": "^1.23.0",
|
|
16
|
+
"@podosoft/podokit-api-client": "^0.4.0",
|
|
15
17
|
"bits-ui": "^2.18.1",
|
|
16
18
|
"clsx": "^2.1.1",
|
|
19
|
+
"mode-watcher": "^1.1.0",
|
|
20
|
+
"svelte-sonner": "^1.1.1",
|
|
17
21
|
"tailwind-merge": "^3.6.0",
|
|
18
|
-
"tailwind-variants": "^3.2.2"
|
|
19
|
-
"@lucide/svelte": "^1.23.0",
|
|
20
|
-
"@internationalized/date": "^3.12.2"
|
|
22
|
+
"tailwind-variants": "^3.2.2"
|
|
21
23
|
},
|
|
22
24
|
"devDependencies": {
|
|
23
25
|
"@sveltejs/adapter-node": "^5.2.0",
|
|
@@ -28,7 +30,7 @@
|
|
|
28
30
|
"svelte-check": "^4.0.0",
|
|
29
31
|
"tailwindcss": "^4.0.0",
|
|
30
32
|
"typesafe-i18n": "^5.26.2",
|
|
31
|
-
"typescript": "^
|
|
33
|
+
"typescript": "^6.0.3",
|
|
32
34
|
"vite": "^5.4.0",
|
|
33
35
|
"tw-animate-css": "^1.4.0"
|
|
34
36
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="alert-action"
|
|
16
|
+
class={cn("absolute top-2 right-2", className)}
|
|
17
|
+
{...restProps}
|
|
18
|
+
>
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="alert-description"
|
|
16
|
+
class={cn(
|
|
17
|
+
"text-muted-foreground text-sm text-balance md:text-pretty [&_p:not(:last-child)]:mb-4 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...restProps}
|
|
21
|
+
>
|
|
22
|
+
{@render children?.()}
|
|
23
|
+
</div>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="alert-title"
|
|
16
|
+
class={cn(
|
|
17
|
+
"font-medium group-has-[>svg]/alert:col-start-2 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...restProps}
|
|
21
|
+
>
|
|
22
|
+
{@render children?.()}
|
|
23
|
+
</div>
|