@podosoft/podokit 0.3.0 → 0.5.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 +2 -2
- package/dist/add.d.ts +2 -0
- package/dist/add.js +5 -1
- package/dist/assemble.d.ts +14 -0
- package/dist/assemble.js +60 -0
- package/dist/create.d.ts +2 -0
- package/dist/create.js +7 -0
- package/dist/eject.d.ts +11 -0
- package/dist/eject.js +27 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +122 -1
- package/dist/inspect.d.ts +43 -0
- package/dist/inspect.js +82 -0
- package/dist/lockfile.d.ts +80 -0
- package/dist/lockfile.js +193 -0
- package/dist/templates/base/dot-env.example +11 -6
- package/dist/templates/fullstack-nest-svelte/README.md +2 -2
- package/dist/templates/fullstack-nest-svelte/apps/api/package.json +21 -2
- package/dist/templates/fullstack-nest-svelte/apps/api/src/app.module.ts +6 -3
- package/dist/templates/fullstack-nest-svelte/apps/api/src/common/app-exception.ts +4 -12
- package/dist/templates/fullstack-nest-svelte/apps/api/src/config/env.validation.ts +1 -1
- package/dist/templates/fullstack-nest-svelte/apps/api/src/main.ts +1 -1
- package/dist/templates/fullstack-nest-svelte/apps/api/tsconfig.json +14 -5
- package/dist/templates/fullstack-nest-svelte/apps/web/package.json +4 -3
- 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/select/select-content.svelte +1 -1
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-item.svelte +1 -1
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/switch/index.ts +7 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/switch/switch.svelte +29 -0
- 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 +44 -5
- 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-env.example +10 -5
- package/dist/templates/fullstack-nest-svelte/dot-gitignore +5 -0
- package/dist/templates/fullstack-nest-svelte/infra/docker/docker-compose.yml +37 -0
- package/dist/templates/fullstack-nest-svelte/infra/docker/sms-sink.mjs +40 -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 +15 -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 +26 -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 +65 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/data-table.svelte +149 -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 +39 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/table-toolbar.svelte +93 -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 +575 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/guards.ts +22 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/+layout.server.ts +30 -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)/accept-invitation/[id]/+page.svelte +36 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/+page.svelte +45 -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 +685 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/audit/+page.server.ts +6 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/audit/+page.svelte +123 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/organizations/+page.server.ts +6 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/organizations/+page.svelte +264 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/sessions/+page.server.ts +7 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/sessions/+page.svelte +156 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/settings/+page.server.ts +26 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/settings/+page.svelte +322 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/users/+page.server.ts +7 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/users/+page.svelte +552 -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 +61 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/login/+page.server.ts +14 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/login/+page.svelte +181 -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 +76 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/verify-email/+page.svelte +63 -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/apps/web/src/routes/oauth2/consent/+page.svelte +54 -0
- package/dist/templates/modules/admin-dashboard/files/tests/api/audit.api.spec.ts +97 -0
- package/dist/templates/modules/admin-dashboard/files/tests/api/auth-config.api.spec.ts +80 -0
- package/dist/templates/modules/admin-dashboard/files/tests/api/auth.api.spec.ts +306 -0
- package/dist/templates/modules/admin-dashboard/files/tests/api/email.api.spec.ts +120 -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/helpers/mailpit.ts +49 -0
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/sms.ts +35 -0
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/totp.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 +40 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/account.ui.spec.ts +126 -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/data-table.ui.spec.ts +32 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/email-admin.ui.spec.ts +60 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/email.ui.spec.ts +42 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/guards.ui.spec.ts +22 -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/oidc.ui.spec.ts +57 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/organizations.ui.spec.ts +52 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/passkey.ui.spec.ts +36 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/sessions.ui.spec.ts +22 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/settings.ui.spec.ts +34 -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/table-toolbar.ui.spec.ts +32 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/users-crud.ui.spec.ts +145 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/users.ui.spec.ts +51 -0
- package/dist/templates/modules/admin-dashboard/module.manifest.json +47 -0
- package/dist/templates/modules/api-key-auth/module.manifest.json +3 -3
- 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-enabled.ts +16 -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 +74 -0
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit-log.entity.ts +29 -8
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.controller.ts +7 -2
- 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 +38 -21
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.module.ts +2 -1
- 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 +10 -4
- package/dist/templates/modules/audit-log/module.manifest.json +37 -8
- package/dist/templates/modules/auth/files/apps/api/src/account/account.controller.ts +85 -3
- package/dist/templates/modules/auth/files/apps/api/src/account/account.module.ts +2 -1
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth-provider.ts +73 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth.ts +199 -31
- package/dist/templates/modules/auth/files/apps/api/src/auth/db.ts +12 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/feature-gate.ts +56 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/org-permissions.ts +22 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/permissions.ts +23 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth-config/auth-config.controller.ts +33 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth-config/auth-config.entity.ts +26 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth-config/auth-config.module.ts +12 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth-config/auth-config.service.ts +118 -0
- package/dist/templates/modules/auth/files/apps/api/src/mail/mailer.ts +65 -0
- package/dist/templates/modules/auth/files/apps/api/src/migrations/1720400000000-InitAppSettings.ts +38 -0
- package/dist/templates/modules/auth/files/apps/api/src/migrations/1720500000000-InitAuthConfig.ts +27 -0
- package/dist/templates/modules/auth/files/apps/api/src/settings/app-setting.entity.ts +16 -0
- package/dist/templates/modules/auth/files/apps/api/src/settings/flag-defaults.ts +25 -0
- package/dist/templates/modules/auth/files/apps/api/src/settings/settings.module.ts +11 -0
- package/dist/templates/modules/auth/files/apps/api/src/settings/settings.service.ts +57 -0
- package/dist/templates/modules/auth/files/apps/api/src/sms/sms.ts +23 -0
- package/dist/templates/modules/auth/module.manifest.json +54 -20
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/worker.module.ts +6 -3
- package/dist/templates/modules/bullmq/module.manifest.json +4 -4
- package/dist/templates/modules/file-upload/module.manifest.json +3 -3
- package/dist/templates/modules/job-progress/module.manifest.json +9 -9
- package/dist/templates/modules/logging/module.manifest.json +3 -3
- package/dist/templates/modules/object-storage-s3/files/infra/docker/minio.compose.yml +5 -3
- package/dist/templates/modules/object-storage-s3/module.manifest.json +5 -5
- package/dist/templates/modules/rate-limit/module.manifest.json +2 -2
- package/dist/templates/modules/redis/module.manifest.json +4 -4
- package/dist/templates/modules/sse/module.manifest.json +4 -4
- package/dist/templates/todo/README.md +2 -2
- package/dist/templates/todo/apps/api/package.json +21 -2
- package/dist/templates/todo/apps/api/src/app.module.ts +6 -3
- package/dist/templates/todo/apps/api/src/common/app-exception.ts +4 -12
- package/dist/templates/todo/apps/api/src/config/env.validation.ts +1 -1
- package/dist/templates/todo/apps/api/src/main.ts +1 -1
- package/dist/templates/todo/apps/api/tsconfig.json +14 -5
- package/dist/templates/todo/apps/web/package.json +4 -3
- package/dist/templates/todo/apps/web/src/lib/api.ts +5 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/switch/index.ts +7 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/switch/switch.svelte +29 -0
- 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 +44 -5
- 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-env.example +10 -5
- package/dist/templates/todo/dot-gitignore +5 -0
- package/dist/templates/todo/infra/docker/docker-compose.yml +4 -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 +15 -0
- package/dist/templates/todo/tests/playwright.config.ts +25 -0
- package/dist/templates/todo/tests/ui/smoke.ui.spec.ts +8 -0
- package/dist/update.d.ts +49 -0
- package/dist/update.js +158 -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/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
package/dist/lockfile.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_OWNED_GLOBS = exports.LOCK_SCHEMA_VERSION = void 0;
|
|
4
|
+
exports.manifestPath = manifestPath;
|
|
5
|
+
exports.filesLockPath = filesLockPath;
|
|
6
|
+
exports.readManifest = readManifest;
|
|
7
|
+
exports.writeManifest = writeManifest;
|
|
8
|
+
exports.writeFilesLock = writeFilesLock;
|
|
9
|
+
exports.podokitVersion = podokitVersion;
|
|
10
|
+
exports.matchGlob = matchGlob;
|
|
11
|
+
exports.classifyTier = classifyTier;
|
|
12
|
+
exports.walkFiles = walkFiles;
|
|
13
|
+
exports.computeFilesLock = computeFilesLock;
|
|
14
|
+
exports.readFilesLock = readFilesLock;
|
|
15
|
+
exports.computeDrift = computeDrift;
|
|
16
|
+
exports.initLockfile = initLockfile;
|
|
17
|
+
exports.recordModules = recordModules;
|
|
18
|
+
const node_fs_1 = require("node:fs");
|
|
19
|
+
const node_path_1 = require("node:path");
|
|
20
|
+
const podokit_template_engine_1 = require("@podosoft/podokit-template-engine");
|
|
21
|
+
/**
|
|
22
|
+
* The generation lockfile (`.podokit/`) records how a project was assembled so
|
|
23
|
+
* `podo update` can rebuild the same inputs and detect user edits. See
|
|
24
|
+
* ADR-0009 (update mechanism) and ADR-0010 (ownership model).
|
|
25
|
+
*/
|
|
26
|
+
exports.LOCK_SCHEMA_VERSION = 1;
|
|
27
|
+
const PODOKIT_DIR = ".podokit";
|
|
28
|
+
const MANIFEST_FILE = "manifest.json";
|
|
29
|
+
const FILES_LOCK_FILE = "files.lock";
|
|
30
|
+
/** Marker that flags a file as assembled from base + module injections. */
|
|
31
|
+
const INJECTION_MARKER = "// podokit:";
|
|
32
|
+
/** Directories that never belong in the lockfile (generated / vendored). */
|
|
33
|
+
const WALK_IGNORE = new Set([
|
|
34
|
+
"node_modules",
|
|
35
|
+
".git",
|
|
36
|
+
PODOKIT_DIR,
|
|
37
|
+
"dist",
|
|
38
|
+
"build",
|
|
39
|
+
".svelte-kit",
|
|
40
|
+
".turbo",
|
|
41
|
+
"coverage",
|
|
42
|
+
]);
|
|
43
|
+
/** Default user-owned globs seeded into a new project. See ADR-0010. */
|
|
44
|
+
exports.DEFAULT_OWNED_GLOBS = [
|
|
45
|
+
"apps/web/src/routes/**",
|
|
46
|
+
"apps/web/src/lib/components/ui/**",
|
|
47
|
+
];
|
|
48
|
+
const TEXT_EXT = /\.(json|md|ts|tsx|js|mjs|cjs|css|html|yml|yaml|txt|env|svelte|gitignore|example)$/i;
|
|
49
|
+
function podokitDir(projectRoot) {
|
|
50
|
+
return (0, node_path_1.join)(projectRoot, PODOKIT_DIR);
|
|
51
|
+
}
|
|
52
|
+
function manifestPath(projectRoot) {
|
|
53
|
+
return (0, node_path_1.join)(podokitDir(projectRoot), MANIFEST_FILE);
|
|
54
|
+
}
|
|
55
|
+
function filesLockPath(projectRoot) {
|
|
56
|
+
return (0, node_path_1.join)(podokitDir(projectRoot), FILES_LOCK_FILE);
|
|
57
|
+
}
|
|
58
|
+
function readManifest(projectRoot) {
|
|
59
|
+
const file = manifestPath(projectRoot);
|
|
60
|
+
return (0, node_fs_1.existsSync)(file) ? JSON.parse((0, node_fs_1.readFileSync)(file, "utf8")) : null;
|
|
61
|
+
}
|
|
62
|
+
function writeManifest(projectRoot, manifest) {
|
|
63
|
+
(0, node_fs_1.mkdirSync)(podokitDir(projectRoot), { recursive: true });
|
|
64
|
+
(0, node_fs_1.writeFileSync)(manifestPath(projectRoot), `${JSON.stringify(manifest, null, 2)}\n`);
|
|
65
|
+
}
|
|
66
|
+
function writeFilesLock(projectRoot, lock) {
|
|
67
|
+
(0, node_fs_1.mkdirSync)(podokitDir(projectRoot), { recursive: true });
|
|
68
|
+
(0, node_fs_1.writeFileSync)(filesLockPath(projectRoot), `${JSON.stringify(lock, null, 2)}\n`);
|
|
69
|
+
}
|
|
70
|
+
/** Read the PodoKit CLI's own version to stamp into the manifest. */
|
|
71
|
+
function podokitVersion() {
|
|
72
|
+
try {
|
|
73
|
+
const pkg = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, "..", "package.json"), "utf8"));
|
|
74
|
+
return pkg.version ?? "0.0.0";
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return "0.0.0";
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** Match a POSIX relative path against a simple glob supporting `*` and `**`. */
|
|
81
|
+
function matchGlob(path, glob) {
|
|
82
|
+
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
83
|
+
// Single pass so replacement output is never re-scanned.
|
|
84
|
+
const pattern = escaped.replace(/\/\*\*$|\*\*\/|\*\*|\*/g, (token) => {
|
|
85
|
+
if (token === "/**")
|
|
86
|
+
return "(?:/.*)?"; // trailing `/**`: the dir itself or anything under it
|
|
87
|
+
if (token === "**/")
|
|
88
|
+
return "(?:.*/)?"; // `**/`: zero or more leading segments
|
|
89
|
+
if (token === "**")
|
|
90
|
+
return ".*"; // `**`: any characters
|
|
91
|
+
return "[^/]*"; // `*`: within a single segment
|
|
92
|
+
});
|
|
93
|
+
return new RegExp(`^${pattern}$`).test(path);
|
|
94
|
+
}
|
|
95
|
+
function isTextPath(relPath) {
|
|
96
|
+
return relPath.startsWith("dot-") || TEXT_EXT.test(relPath);
|
|
97
|
+
}
|
|
98
|
+
/** Classify a file's ownership tier. See ADR-0010 priority order. */
|
|
99
|
+
function classifyTier(relPath, content, ownedGlobs) {
|
|
100
|
+
if (ownedGlobs.some((glob) => matchGlob(relPath, glob)))
|
|
101
|
+
return "owned";
|
|
102
|
+
if (isTextPath(relPath) && content.toString("utf8").includes(INJECTION_MARKER))
|
|
103
|
+
return "assembled";
|
|
104
|
+
return "managed";
|
|
105
|
+
}
|
|
106
|
+
/** List project files as POSIX-relative paths, skipping generated/vendored dirs. */
|
|
107
|
+
function walkFiles(projectRoot, prefix = "") {
|
|
108
|
+
const out = [];
|
|
109
|
+
for (const entry of (0, node_fs_1.readdirSync)((0, node_path_1.join)(projectRoot, prefix))) {
|
|
110
|
+
if (WALK_IGNORE.has(entry))
|
|
111
|
+
continue;
|
|
112
|
+
const rel = prefix ? `${prefix}/${entry}` : entry;
|
|
113
|
+
if ((0, node_fs_1.statSync)((0, node_path_1.join)(projectRoot, rel)).isDirectory()) {
|
|
114
|
+
out.push(...walkFiles(projectRoot, rel));
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
out.push(rel);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
/** Recompute the files.lock by hashing and classifying every project file. */
|
|
123
|
+
function computeFilesLock(projectRoot, ownedGlobs) {
|
|
124
|
+
const files = {};
|
|
125
|
+
for (const rel of walkFiles(projectRoot).sort()) {
|
|
126
|
+
const content = (0, node_fs_1.readFileSync)((0, node_path_1.join)(projectRoot, rel));
|
|
127
|
+
files[rel] = { tier: classifyTier(rel, content, ownedGlobs), outHash: (0, podokit_template_engine_1.hashContent)(content) };
|
|
128
|
+
}
|
|
129
|
+
return { schemaVersion: exports.LOCK_SCHEMA_VERSION, files };
|
|
130
|
+
}
|
|
131
|
+
function readFilesLock(projectRoot) {
|
|
132
|
+
const file = filesLockPath(projectRoot);
|
|
133
|
+
return (0, node_fs_1.existsSync)(file) ? JSON.parse((0, node_fs_1.readFileSync)(file, "utf8")) : null;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Compare the current disk against the lock: report managed/assembled files the
|
|
137
|
+
* user has edited (hash mismatch) or deleted. Owned files are never reported —
|
|
138
|
+
* they belong to the user. Returns empty drift when there is no lock.
|
|
139
|
+
*/
|
|
140
|
+
function computeDrift(projectRoot) {
|
|
141
|
+
const lock = readFilesLock(projectRoot);
|
|
142
|
+
const drifted = [];
|
|
143
|
+
const missing = [];
|
|
144
|
+
if (!lock)
|
|
145
|
+
return { drifted, missing };
|
|
146
|
+
for (const [rel, entry] of Object.entries(lock.files)) {
|
|
147
|
+
if (entry.tier === "owned")
|
|
148
|
+
continue;
|
|
149
|
+
const abs = (0, node_path_1.join)(projectRoot, rel);
|
|
150
|
+
if (!(0, node_fs_1.existsSync)(abs)) {
|
|
151
|
+
missing.push(rel);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if ((0, podokit_template_engine_1.hashContent)((0, node_fs_1.readFileSync)(abs)) !== entry.outHash)
|
|
155
|
+
drifted.push(rel);
|
|
156
|
+
}
|
|
157
|
+
return { drifted: drifted.sort(), missing: missing.sort() };
|
|
158
|
+
}
|
|
159
|
+
/** Write the initial `.podokit/` lockfile for a freshly created project. */
|
|
160
|
+
function initLockfile(projectRoot, options) {
|
|
161
|
+
const ownedGlobs = options.ownedGlobs ?? exports.DEFAULT_OWNED_GLOBS;
|
|
162
|
+
const manifest = {
|
|
163
|
+
schemaVersion: exports.LOCK_SCHEMA_VERSION,
|
|
164
|
+
podokitVersion: options.version ?? podokitVersion(),
|
|
165
|
+
template: options.template,
|
|
166
|
+
packageManager: options.packageManager,
|
|
167
|
+
answers: options.answers,
|
|
168
|
+
modules: [],
|
|
169
|
+
ownedGlobs,
|
|
170
|
+
};
|
|
171
|
+
writeManifest(projectRoot, manifest);
|
|
172
|
+
writeFilesLock(projectRoot, computeFilesLock(projectRoot, ownedGlobs));
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Record applied modules in the manifest and recompute the files.lock. Modules
|
|
176
|
+
* already present are left in place (idempotent); the lock always reflects disk.
|
|
177
|
+
* No-op on projects without a manifest (pre-lockfile projects; see `podo adopt`).
|
|
178
|
+
*/
|
|
179
|
+
function recordModules(projectRoot, moduleNames, version) {
|
|
180
|
+
const manifest = readManifest(projectRoot);
|
|
181
|
+
if (!manifest)
|
|
182
|
+
return;
|
|
183
|
+
const stampedWith = version ?? podokitVersion();
|
|
184
|
+
const known = new Set(manifest.modules.map((m) => m.name));
|
|
185
|
+
for (const name of moduleNames) {
|
|
186
|
+
if (known.has(name))
|
|
187
|
+
continue;
|
|
188
|
+
manifest.modules.push({ name, order: manifest.modules.length, addedWith: stampedWith });
|
|
189
|
+
known.add(name);
|
|
190
|
+
}
|
|
191
|
+
writeManifest(projectRoot, manifest);
|
|
192
|
+
writeFilesLock(projectRoot, computeFilesLock(projectRoot, manifest.ownedGlobs));
|
|
193
|
+
}
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
# Copy to .env and adjust. Never commit real secrets.
|
|
2
|
+
|
|
3
|
+
# ── Required (runtime) ───────────────────────────────────────────────
|
|
2
4
|
POSTGRES_HOST=localhost
|
|
3
5
|
POSTGRES_PORT=5432
|
|
4
6
|
POSTGRES_USER=podokit
|
|
5
7
|
POSTGRES_PASSWORD=podokit
|
|
6
8
|
POSTGRES_DB=podokit
|
|
7
9
|
|
|
10
|
+
APP_BASE_URL=http://localhost:5001
|
|
11
|
+
API_BASE_URL=http://localhost:5002
|
|
12
|
+
BACKEND_INTERNAL_URL=http://localhost:5002
|
|
13
|
+
CORS_ORIGIN=http://localhost:5001
|
|
14
|
+
PORT=5002
|
|
15
|
+
|
|
16
|
+
# ── Optional / development ───────────────────────────────────────────
|
|
17
|
+
# Redis — only needed by cache/queue modules. Start it with:
|
|
18
|
+
# docker compose --profile cache up
|
|
8
19
|
REDIS_HOST=localhost
|
|
9
20
|
REDIS_PORT=6379
|
|
10
|
-
|
|
11
|
-
APP_BASE_URL=http://localhost:5173
|
|
12
|
-
API_BASE_URL=http://localhost:3000
|
|
13
|
-
BACKEND_INTERNAL_URL=http://localhost:3000
|
|
14
|
-
CORS_ORIGIN=http://localhost:5173
|
|
15
|
-
PORT=3000
|
|
@@ -18,8 +18,8 @@ docker compose -f infra/docker/docker-compose.yml up -d
|
|
|
18
18
|
{{packageManager}} run dev
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
- API: http://localhost:
|
|
22
|
-
- Web: http://localhost:
|
|
21
|
+
- API: http://localhost:5002 — health at `/health`, docs at `/api-docs`
|
|
22
|
+
- Web: http://localhost:5001
|
|
23
23
|
|
|
24
24
|
## Database & migrations
|
|
25
25
|
|
|
@@ -7,13 +7,14 @@
|
|
|
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
|
+
"@podosoft/podokit-contracts": "^0.1.0",
|
|
17
18
|
"@nestjs/common": "^11.1.27",
|
|
18
19
|
"@nestjs/config": "^4.0.4",
|
|
19
20
|
"@nestjs/core": "^11.1.27",
|
|
@@ -39,6 +40,24 @@
|
|
|
39
40
|
"supertest": "^7.0.0",
|
|
40
41
|
"ts-jest": "^29.2.5",
|
|
41
42
|
"ts-node": "^10.9.2",
|
|
42
|
-
"typescript": "^
|
|
43
|
+
"typescript": "^6.0.3"
|
|
44
|
+
},
|
|
45
|
+
"jest": {
|
|
46
|
+
"moduleFileExtensions": [
|
|
47
|
+
"js",
|
|
48
|
+
"json",
|
|
49
|
+
"ts"
|
|
50
|
+
],
|
|
51
|
+
"rootDir": "src",
|
|
52
|
+
"testRegex": ".*\\.spec\\.ts$",
|
|
53
|
+
"transform": {
|
|
54
|
+
"^.+\\.ts$": [
|
|
55
|
+
"ts-jest",
|
|
56
|
+
{
|
|
57
|
+
"tsconfig": "tsconfig.json"
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
"testEnvironment": "node"
|
|
43
62
|
}
|
|
44
63
|
}
|
|
@@ -4,17 +4,20 @@ import { TypeOrmModule } from "@nestjs/typeorm";
|
|
|
4
4
|
import { validateEnv } from "./config/env.validation";
|
|
5
5
|
import { dataSourceOptions } from "./database/data-source";
|
|
6
6
|
import { HealthModule } from "./health/health.module";
|
|
7
|
-
// podokit:imports
|
|
7
|
+
// podokit:begin:imports
|
|
8
|
+
// podokit:end:imports
|
|
8
9
|
|
|
9
10
|
@Module({
|
|
10
11
|
imports: [
|
|
11
12
|
ConfigModule.forRoot({ isGlobal: true, validate: validateEnv }),
|
|
12
13
|
TypeOrmModule.forRoot(dataSourceOptions),
|
|
13
14
|
HealthModule,
|
|
14
|
-
// podokit:module-imports
|
|
15
|
+
// podokit:begin:module-imports
|
|
16
|
+
// podokit:end:module-imports
|
|
15
17
|
],
|
|
16
18
|
providers: [
|
|
17
|
-
// podokit:providers
|
|
19
|
+
// podokit:begin:providers
|
|
20
|
+
// podokit:end:providers
|
|
18
21
|
],
|
|
19
22
|
})
|
|
20
23
|
export class AppModule {}
|
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
readonly code: string,
|
|
6
|
-
message: string,
|
|
7
|
-
readonly statusCode = 400,
|
|
8
|
-
) {
|
|
9
|
-
super(message);
|
|
10
|
-
this.name = "AppException";
|
|
11
|
-
}
|
|
12
|
-
}
|
|
1
|
+
// AppException is a shared contract (stable, language-independent error `code`
|
|
2
|
+
// the frontend branches on). Re-exported from @podosoft/podokit-contracts so the
|
|
3
|
+
// backend and any other layer reference one definition.
|
|
4
|
+
export { AppException } from "@podosoft/podokit-contracts";
|
|
@@ -3,7 +3,7 @@ import { z } from "zod";
|
|
|
3
3
|
// Schema-validated environment. Fails fast at boot if something is wrong.
|
|
4
4
|
const schema = z.object({
|
|
5
5
|
NODE_ENV: z.string().default("development"),
|
|
6
|
-
PORT: z.coerce.number().default(
|
|
6
|
+
PORT: z.coerce.number().default(5002),
|
|
7
7
|
CORS_ORIGIN: z.string().optional(),
|
|
8
8
|
POSTGRES_HOST: z.string().default("localhost"),
|
|
9
9
|
POSTGRES_PORT: z.coerce.number().default(5432),
|
|
@@ -22,7 +22,7 @@ async function bootstrap(): Promise<void> {
|
|
|
22
22
|
.build();
|
|
23
23
|
SwaggerModule.setup("api-docs", app, SwaggerModule.createDocument(app, config));
|
|
24
24
|
|
|
25
|
-
const port = Number(process.env.PORT ??
|
|
25
|
+
const port = Number(process.env.PORT ?? 5002);
|
|
26
26
|
await app.listen(port);
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -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
|
}
|
|
@@ -4,15 +4,16 @@
|
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"dev": "vite dev --port
|
|
7
|
+
"dev": "vite dev --port 5001",
|
|
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
15
|
"@lucide/svelte": "^1.23.0",
|
|
16
|
+
"@podosoft/podokit-api-client": "^0.5.0",
|
|
16
17
|
"bits-ui": "^2.18.1",
|
|
17
18
|
"clsx": "^2.1.1",
|
|
18
19
|
"mode-watcher": "^1.1.0",
|
|
@@ -29,7 +30,7 @@
|
|
|
29
30
|
"svelte-check": "^4.0.0",
|
|
30
31
|
"tailwindcss": "^4.0.0",
|
|
31
32
|
"typesafe-i18n": "^5.26.2",
|
|
32
|
-
"typescript": "^
|
|
33
|
+
"typescript": "^6.0.3",
|
|
33
34
|
"vite": "^5.4.0",
|
|
34
35
|
"tw-animate-css": "^1.4.0"
|
|
35
36
|
}
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
{preventScroll}
|
|
28
28
|
data-slot="select-content"
|
|
29
29
|
class={cn(
|
|
30
|
-
"bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 min-w-36 rounded-lg shadow-md ring-1 duration-100 data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 relative isolate z-50 overflow-x-hidden overflow-y-auto",
|
|
30
|
+
"bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 min-w-36 rounded-lg p-1 shadow-md ring-1 duration-100 data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 relative isolate z-50 max-h-96 overflow-x-hidden overflow-y-auto",
|
|
31
31
|
className
|
|
32
32
|
)}
|
|
33
33
|
{...restProps}
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
{#snippet children({ selected, highlighted })}
|
|
27
27
|
<span class="absolute end-2 flex size-3.5 items-center justify-center">
|
|
28
28
|
{#if selected}
|
|
29
|
-
<CheckIcon class="
|
|
29
|
+
<CheckIcon class="size-3.5" />
|
|
30
30
|
{/if}
|
|
31
31
|
</span>
|
|
32
32
|
<span class="flex flex-1 gap-2 shrink-0 whitespace-nowrap">
|
package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/switch/switch.svelte
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Switch as SwitchPrimitive } from "bits-ui";
|
|
3
|
+
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
checked = $bindable(false),
|
|
8
|
+
class: className,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithoutChildrenOrChild<SwitchPrimitive.RootProps> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<SwitchPrimitive.Root
|
|
14
|
+
bind:ref
|
|
15
|
+
data-slot="switch"
|
|
16
|
+
class={cn(
|
|
17
|
+
"peer focus-visible:border-ring focus-visible:ring-ring/50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
bind:checked
|
|
21
|
+
{...restProps}
|
|
22
|
+
>
|
|
23
|
+
<SwitchPrimitive.Thumb
|
|
24
|
+
data-slot="switch-thumb"
|
|
25
|
+
class={cn(
|
|
26
|
+
"bg-background pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0"
|
|
27
|
+
)}
|
|
28
|
+
/>
|
|
29
|
+
</SwitchPrimitive.Root>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { RequestEvent } from "@sveltejs/kit";
|
|
2
|
+
import { createApiClient, type ApiClient } from "@podosoft/podokit-api-client";
|
|
3
|
+
import { backendBaseUrl } from "./backend-proxy";
|
|
4
|
+
|
|
5
|
+
// Server-side client for load functions and hooks: points at the internal
|
|
6
|
+
// backend URL and forwards the incoming request's cookies.
|
|
7
|
+
export function serverApiClient(event: RequestEvent): ApiClient {
|
|
8
|
+
const cookie = event.request.headers.get("cookie") ?? "";
|
|
9
|
+
const fetchWithCookies: typeof fetch = (input, init) => {
|
|
10
|
+
const headers = new Headers(init?.headers);
|
|
11
|
+
if (cookie) headers.set("cookie", cookie);
|
|
12
|
+
return event.fetch(input, { ...init, headers });
|
|
13
|
+
};
|
|
14
|
+
return createApiClient({
|
|
15
|
+
baseUrl: backendBaseUrl(),
|
|
16
|
+
apiBasePath: "",
|
|
17
|
+
fetch: fetchWithCookies,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -1,16 +1,55 @@
|
|
|
1
1
|
// Server-side proxy boundary. The browser never talks to the API directly:
|
|
2
|
-
//
|
|
3
|
-
|
|
2
|
+
// requests go through SvelteKit server routes, which forward an allowlist of
|
|
3
|
+
// headers to BACKEND_INTERNAL_URL and relay the response (including Set-Cookie).
|
|
4
|
+
const FORWARDED_HEADERS = ["authorization", "cookie", "content-type", "accept", "origin", "referer"];
|
|
5
|
+
const RELAYED_RESPONSE_HEADERS = ["content-type", "location", "cache-control"];
|
|
4
6
|
|
|
5
7
|
export function backendBaseUrl(): string {
|
|
6
|
-
return process.env.BACKEND_INTERNAL_URL ?? "http://localhost:
|
|
8
|
+
return process.env.BACKEND_INTERNAL_URL ?? "http://localhost:5002";
|
|
7
9
|
}
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
// Normalize the resolved client IP for the forwarded header. better-auth stores
|
|
12
|
+
// IPv4 as-is but anonymizes IPv6 (masks the host bits), so an IPv6 loopback would
|
|
13
|
+
// be recorded as "::" — map loopback to 127.0.0.1 for a clean local value, and
|
|
14
|
+
// unwrap IPv4-mapped addresses.
|
|
15
|
+
function normalizeClientIp(address: string | undefined): string | undefined {
|
|
16
|
+
if (!address) return undefined;
|
|
17
|
+
if (address === "::1" || address === "::") return "127.0.0.1";
|
|
18
|
+
if (address.startsWith("::ffff:")) return address.slice("::ffff:".length);
|
|
19
|
+
return address;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function proxyRequest(
|
|
23
|
+
request: Request,
|
|
24
|
+
targetUrl: string,
|
|
25
|
+
clientAddress?: string,
|
|
26
|
+
): Promise<Response> {
|
|
10
27
|
const headers = new Headers();
|
|
11
28
|
for (const name of FORWARDED_HEADERS) {
|
|
12
29
|
const value = request.headers.get(name);
|
|
13
30
|
if (value) headers.set(name, value);
|
|
14
31
|
}
|
|
15
|
-
|
|
32
|
+
// Forward the resolved client IP so the API (better-auth) can record it.
|
|
33
|
+
const forwardedIp = normalizeClientIp(clientAddress);
|
|
34
|
+
if (forwardedIp) headers.set("x-forwarded-for", forwardedIp);
|
|
35
|
+
|
|
36
|
+
const hasBody = request.method !== "GET" && request.method !== "HEAD";
|
|
37
|
+
const upstream = await fetch(targetUrl, {
|
|
38
|
+
method: request.method,
|
|
39
|
+
headers,
|
|
40
|
+
body: hasBody ? await request.text() : undefined,
|
|
41
|
+
redirect: "manual",
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const responseHeaders = new Headers();
|
|
45
|
+
for (const name of RELAYED_RESPONSE_HEADERS) {
|
|
46
|
+
const value = upstream.headers.get(name);
|
|
47
|
+
if (value) responseHeaders.set(name, value);
|
|
48
|
+
}
|
|
49
|
+
// Relay Set-Cookie so auth session cookies reach the browser.
|
|
50
|
+
for (const cookie of upstream.headers.getSetCookie()) {
|
|
51
|
+
responseHeaders.append("set-cookie", cookie);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return new Response(upstream.body, { status: upstream.status, headers: responseHeaders });
|
|
16
55
|
}
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { Button } from "$lib/components/ui/button";
|
|
3
3
|
import * as Card from "$lib/components/ui/card";
|
|
4
|
+
import { api } from "$lib/api";
|
|
5
|
+
import { ApiError } from "@podosoft/podokit-api-client";
|
|
4
6
|
|
|
5
7
|
type Health = { status: string } | { error: string };
|
|
6
8
|
|
|
7
9
|
let health = $state<Health | null>(null);
|
|
8
10
|
|
|
9
11
|
async function check(): Promise<void> {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
try {
|
|
13
|
+
health = await api.get<{ status: string }>("/health");
|
|
14
|
+
} catch (err) {
|
|
15
|
+
health = { error: err instanceof ApiError ? `${err.code} (${err.statusCode})` : "Request failed" };
|
|
16
|
+
}
|
|
12
17
|
}
|
|
13
18
|
</script>
|
|
14
19
|
|
|
@@ -21,7 +26,7 @@
|
|
|
21
26
|
<Card.Root>
|
|
22
27
|
<Card.Header>
|
|
23
28
|
<Card.Title>API health</Card.Title>
|
|
24
|
-
<Card.Description>Check the NestJS API through the
|
|
29
|
+
<Card.Description>Check the NestJS API through the typed ApiClient.</Card.Description>
|
|
25
30
|
</Card.Header>
|
|
26
31
|
<Card.Content class="flex flex-col gap-4">
|
|
27
32
|
<Button class="w-fit" onclick={check}>Check API health</Button>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RequestHandler } from "@sveltejs/kit";
|
|
2
|
+
import { backendBaseUrl, proxyRequest } from "$lib/server/backend-proxy";
|
|
3
|
+
|
|
4
|
+
// Proxy the app's REST API, stripping the /api prefix (backend routes are at root).
|
|
5
|
+
const handler: RequestHandler = ({ request, params, url, getClientAddress }) =>
|
|
6
|
+
proxyRequest(request, `${backendBaseUrl()}/${params.path}${url.search}`, getClientAddress());
|
|
7
|
+
|
|
8
|
+
export const GET = handler;
|
|
9
|
+
export const POST = handler;
|
|
10
|
+
export const PUT = handler;
|
|
11
|
+
export const PATCH = handler;
|
|
12
|
+
export const DELETE = handler;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RequestHandler } from "@sveltejs/kit";
|
|
2
|
+
import { backendBaseUrl, proxyRequest } from "$lib/server/backend-proxy";
|
|
3
|
+
|
|
4
|
+
// Proxy the better-auth handler, keeping the /api/auth prefix.
|
|
5
|
+
const handler: RequestHandler = ({ request, params, url, getClientAddress }) =>
|
|
6
|
+
proxyRequest(request, `${backendBaseUrl()}/api/auth/${params.all}${url.search}`, getClientAddress());
|
|
7
|
+
|
|
8
|
+
export const GET = handler;
|
|
9
|
+
export const POST = handler;
|
|
10
|
+
export const PUT = handler;
|
|
11
|
+
export const PATCH = handler;
|
|
12
|
+
export const DELETE = handler;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Copy to .env and adjust. Never commit real secrets.
|
|
2
|
+
|
|
3
|
+
# ── Required (runtime) ───────────────────────────────────────────────
|
|
2
4
|
NODE_ENV=development
|
|
3
|
-
PORT=
|
|
5
|
+
PORT=5002
|
|
4
6
|
|
|
5
7
|
POSTGRES_HOST=localhost
|
|
6
8
|
POSTGRES_PORT=5432
|
|
@@ -8,9 +10,12 @@ POSTGRES_USER=podokit
|
|
|
8
10
|
POSTGRES_PASSWORD=podokit
|
|
9
11
|
POSTGRES_DB=podokit
|
|
10
12
|
|
|
13
|
+
# Web -> API boundary (server-side only; not exposed to the browser)
|
|
14
|
+
BACKEND_INTERNAL_URL=http://localhost:5002
|
|
15
|
+
CORS_ORIGIN=http://localhost:5001
|
|
16
|
+
|
|
17
|
+
# ── Optional / development ───────────────────────────────────────────
|
|
18
|
+
# Redis — only needed by cache/queue modules (redis, bullmq, rate-limit,
|
|
19
|
+
# job-progress, sse). Start it with: docker compose --profile cache up
|
|
11
20
|
REDIS_HOST=localhost
|
|
12
21
|
REDIS_PORT=6379
|
|
13
|
-
|
|
14
|
-
# Web -> API boundary (server-side only; not exposed to the browser)
|
|
15
|
-
BACKEND_INTERNAL_URL=http://localhost:3000
|
|
16
|
-
CORS_ORIGIN=http://localhost:5173
|