@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
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { invalidateAll } from "$app/navigation";
|
|
3
|
+
import { Badge } from "$lib/components/ui/badge";
|
|
4
|
+
import { Switch } from "$lib/components/ui/switch";
|
|
5
|
+
import { Input } from "$lib/components/ui/input";
|
|
6
|
+
import { Label } from "$lib/components/ui/label";
|
|
7
|
+
import { Button } from "$lib/components/ui/button";
|
|
8
|
+
import * as Card from "$lib/components/ui/card";
|
|
9
|
+
import * as Dialog from "$lib/components/ui/dialog";
|
|
10
|
+
import { toast } from "svelte-sonner";
|
|
11
|
+
import { api } from "$lib/api";
|
|
12
|
+
import { getI18n } from "$lib/i18n";
|
|
13
|
+
import type { Capabilities } from "@podosoft/podokit-api-client";
|
|
14
|
+
import type { AuthConfigView, SocialProviderView } from "./+page.server";
|
|
15
|
+
|
|
16
|
+
let { data }: { data: { capabilities: Capabilities; authConfig: AuthConfigView | null } } = $props();
|
|
17
|
+
const i18n = getI18n();
|
|
18
|
+
const caps = $derived(data.capabilities);
|
|
19
|
+
const ac = $derived(data.authConfig);
|
|
20
|
+
|
|
21
|
+
type EditableFlag = "twoFactor" | "magicLink" | "emailOtp" | "username" | "multiSession" | "phoneNumber" | "apiKey" | "passkey" | "organization" | "oidcProvider";
|
|
22
|
+
type ServerKey = "requireEmailVerification" | "hibp" | "allowDelete" | "auditLog";
|
|
23
|
+
|
|
24
|
+
// Every card renders from this one shape, so they share size and structure: a
|
|
25
|
+
// title (+ optional enable switch), a description, and a footer with a status
|
|
26
|
+
// badge and optional configure action.
|
|
27
|
+
type Item = {
|
|
28
|
+
key: string;
|
|
29
|
+
name: string;
|
|
30
|
+
desc: string;
|
|
31
|
+
status: { on: boolean; label: string };
|
|
32
|
+
toggle?: { checked: boolean; disabled: boolean; onChange: (v: boolean) => void };
|
|
33
|
+
configure?: { label: string; onClick: () => void };
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
let saving = $state(false);
|
|
37
|
+
// Writes DB-backed auth config (OAuth providers, SMTP, server toggles) — applied
|
|
38
|
+
// live (the auth instance rebuilds on the next request), no restart.
|
|
39
|
+
async function put(body: unknown): Promise<boolean> {
|
|
40
|
+
saving = true;
|
|
41
|
+
let ok = false;
|
|
42
|
+
try {
|
|
43
|
+
await api.put("/account/auth-config", body);
|
|
44
|
+
await invalidateAll();
|
|
45
|
+
toast.success(i18n.t.settings.saved);
|
|
46
|
+
ok = true;
|
|
47
|
+
} catch {
|
|
48
|
+
toast.error(i18n.t.settings.saveFailed);
|
|
49
|
+
}
|
|
50
|
+
saving = false;
|
|
51
|
+
return ok;
|
|
52
|
+
}
|
|
53
|
+
// Feature flags live in a separate store (app_setting) with their own endpoint.
|
|
54
|
+
async function toggleFlag(flag: EditableFlag, next: boolean): Promise<void> {
|
|
55
|
+
saving = true;
|
|
56
|
+
try {
|
|
57
|
+
await api.put("/account/settings", { [flag]: next });
|
|
58
|
+
await invalidateAll();
|
|
59
|
+
toast.success(i18n.t.settings.saved);
|
|
60
|
+
} catch {
|
|
61
|
+
toast.error(i18n.t.settings.saveFailed);
|
|
62
|
+
}
|
|
63
|
+
saving = false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const callbackBase = $derived(typeof window === "undefined" ? "" : window.location.origin);
|
|
67
|
+
const labelOf = (id: string): string => ac?.catalog.find((c) => c.id === id)?.label ?? id;
|
|
68
|
+
const configured = $derived(Object.values(ac?.social ?? {}));
|
|
69
|
+
const enabledProviders = $derived(configured.filter((p) => p.enabled && p.clientId && p.hasSecret));
|
|
70
|
+
const configuredIds = $derived(new Set(Object.keys(ac?.social ?? {})));
|
|
71
|
+
const addable = $derived((ac?.catalog ?? []).filter((p) => !configuredIds.has(p.id)));
|
|
72
|
+
|
|
73
|
+
function socialStatus(v: SocialProviderView): { on: boolean; label: string } {
|
|
74
|
+
if (v.enabled && v.clientId && v.hasSecret) return { on: true, label: i18n.t.settings.enabled };
|
|
75
|
+
if (v.clientId || v.hasSecret || v.enabled) return { on: false, label: i18n.t.settings.configuredOff };
|
|
76
|
+
return { on: false, label: i18n.t.settings.notConfigured };
|
|
77
|
+
}
|
|
78
|
+
const socialCardStatus = $derived<{ on: boolean; label: string }>(
|
|
79
|
+
enabledProviders.length > 0
|
|
80
|
+
? { on: true, label: enabledProviders.map((p) => labelOf(p.id)).join(", ") }
|
|
81
|
+
: { on: false, label: configured.length > 0 ? i18n.t.settings.configuredOff : i18n.t.settings.notConfigured },
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
// ── Dialogs. Secret fields are write-only; leaving them blank keeps the stored
|
|
85
|
+
// secret. The Social login dialog manages every provider (list ⇄ add/edit
|
|
86
|
+
// form) in one popup. ──
|
|
87
|
+
let openDialog = $state<"social" | "smtp" | null>(null);
|
|
88
|
+
let socialView = $state<"list" | "form">("list");
|
|
89
|
+
let socialForm = $state({ id: "", clientId: "", clientSecret: "", redirectURI: "", enabled: true, adding: false });
|
|
90
|
+
let smtp = $state({ enabled: false, host: "", port: 587, secure: false, user: "", from: "", pass: "" });
|
|
91
|
+
$effect(() => {
|
|
92
|
+
if (!ac) return;
|
|
93
|
+
smtp = { enabled: ac.smtp.enabled, host: ac.smtp.host, port: ac.smtp.port, secure: ac.smtp.secure, user: ac.smtp.user, from: ac.smtp.from, pass: "" };
|
|
94
|
+
});
|
|
95
|
+
const editingView = $derived(socialForm.adding ? undefined : ac?.social[socialForm.id]);
|
|
96
|
+
|
|
97
|
+
function openSocialManager(): void {
|
|
98
|
+
socialView = "list";
|
|
99
|
+
openDialog = "social";
|
|
100
|
+
}
|
|
101
|
+
function startEditSocial(id: string): void {
|
|
102
|
+
const v = ac?.social[id];
|
|
103
|
+
socialForm = { id, clientId: v?.clientId ?? "", clientSecret: "", redirectURI: v?.redirectURI ?? "", enabled: v?.enabled ?? true, adding: false };
|
|
104
|
+
socialView = "form";
|
|
105
|
+
}
|
|
106
|
+
function startAddSocial(): void {
|
|
107
|
+
socialForm = { id: addable[0]?.id ?? "", clientId: "", clientSecret: "", redirectURI: "", enabled: true, adding: true };
|
|
108
|
+
socialView = "form";
|
|
109
|
+
}
|
|
110
|
+
async function saveSocial(): Promise<void> {
|
|
111
|
+
if (!socialForm.id) return;
|
|
112
|
+
const done = await put({
|
|
113
|
+
social: {
|
|
114
|
+
[socialForm.id]: {
|
|
115
|
+
enabled: socialForm.enabled,
|
|
116
|
+
clientId: socialForm.clientId,
|
|
117
|
+
redirectURI: socialForm.redirectURI,
|
|
118
|
+
...(socialForm.clientSecret ? { clientSecret: socialForm.clientSecret } : {}),
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
if (done) socialView = "list"; // back to the list; keep the dialog open to add more
|
|
123
|
+
}
|
|
124
|
+
async function removeSocial(id: string): Promise<void> {
|
|
125
|
+
await put({ social: { [id]: { delete: true } } });
|
|
126
|
+
}
|
|
127
|
+
async function saveSmtp(): Promise<void> {
|
|
128
|
+
const done = await put({ smtp: { enabled: smtp.enabled, host: smtp.host, port: Number(smtp.port), secure: smtp.secure, user: smtp.user, from: smtp.from, ...(smtp.pass ? { pass: smtp.pass } : {}) } });
|
|
129
|
+
if (done) openDialog = null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ── Card builders (uniform Item shape) ──
|
|
133
|
+
const flagCard = (flag: EditableFlag, name: string, desc: string): Item => ({
|
|
134
|
+
key: `flag-${flag}`,
|
|
135
|
+
name,
|
|
136
|
+
desc,
|
|
137
|
+
status: { on: caps[flag], label: caps[flag] ? i18n.t.settings.enabled : i18n.t.settings.disabled },
|
|
138
|
+
toggle: { checked: caps[flag], disabled: saving, onChange: (v) => toggleFlag(flag, v) },
|
|
139
|
+
});
|
|
140
|
+
const serverCard = (key: ServerKey, name: string, desc: string, on: boolean): Item => ({
|
|
141
|
+
key: `srv-${key}`,
|
|
142
|
+
name,
|
|
143
|
+
desc,
|
|
144
|
+
status: { on, label: on ? i18n.t.settings.enabled : i18n.t.settings.disabled },
|
|
145
|
+
toggle: { checked: on, disabled: saving || !ac, onChange: (v) => put({ server: { [key]: v } }) },
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const signInMethods = $derived<Item[]>([
|
|
149
|
+
{ key: "emailPassword", name: i18n.t.settings.emailPassword, desc: i18n.t.settings.emailPasswordDesc, status: { on: true, label: i18n.t.settings.enabled } },
|
|
150
|
+
// A single Social login card; providers (Google, GitHub, Apple, …) are added,
|
|
151
|
+
// edited and removed dynamically inside its dialog.
|
|
152
|
+
{ key: "social", name: i18n.t.settings.socialTitle, desc: i18n.t.settings.socialCardDesc, status: socialCardStatus, configure: { label: i18n.t.settings.configure, onClick: openSocialManager } },
|
|
153
|
+
flagCard("magicLink", i18n.t.settings.magicLink, i18n.t.settings.magicLinkDesc),
|
|
154
|
+
flagCard("emailOtp", i18n.t.settings.emailOtp, i18n.t.settings.emailOtpDesc),
|
|
155
|
+
flagCard("username", i18n.t.settings.username, i18n.t.settings.usernameDesc),
|
|
156
|
+
flagCard("passkey", i18n.t.settings.passkey, i18n.t.settings.passkeyDesc),
|
|
157
|
+
flagCard("phoneNumber", i18n.t.settings.phoneNumber, i18n.t.settings.phoneNumberDesc),
|
|
158
|
+
flagCard("multiSession", i18n.t.settings.multiSession, i18n.t.settings.multiSessionDesc),
|
|
159
|
+
]);
|
|
160
|
+
// SMTP delivery is configured inside the email-verification card (its "SMTP
|
|
161
|
+
// settings" popup): verification needs outbound mail, so they live together.
|
|
162
|
+
const emailSettings = $derived<Item[]>([
|
|
163
|
+
{
|
|
164
|
+
key: "email",
|
|
165
|
+
name: i18n.t.settings.emailVerification,
|
|
166
|
+
desc: i18n.t.settings.emailVerificationDesc,
|
|
167
|
+
status: { on: caps.emailVerification, label: caps.emailVerification ? i18n.t.settings.enabled : i18n.t.settings.disabled },
|
|
168
|
+
toggle: { checked: caps.emailVerification, disabled: saving || !ac, onChange: (v) => put({ server: { requireEmailVerification: v } }) },
|
|
169
|
+
configure: { label: i18n.t.settings.smtpConfigure, onClick: () => (openDialog = "smtp") },
|
|
170
|
+
},
|
|
171
|
+
]);
|
|
172
|
+
const security = $derived<Item[]>([
|
|
173
|
+
flagCard("twoFactor", i18n.t.settings.twoFactor, i18n.t.settings.twoFactorDesc),
|
|
174
|
+
serverCard("hibp", i18n.t.settings.breachCheck, i18n.t.settings.breachCheckDesc, caps.passwordBreachCheck),
|
|
175
|
+
serverCard("allowDelete", i18n.t.settings.accountDeletion, i18n.t.settings.accountDeletionDesc, caps.deleteAccount),
|
|
176
|
+
serverCard("auditLog", i18n.t.settings.auditLog, i18n.t.settings.auditLogDesc, caps.auditLog),
|
|
177
|
+
]);
|
|
178
|
+
const advanced = $derived<Item[]>([
|
|
179
|
+
flagCard("apiKey", i18n.t.settings.apiKey, i18n.t.settings.apiKeyDesc),
|
|
180
|
+
flagCard("organization", i18n.t.settings.organization, i18n.t.settings.organizationDesc),
|
|
181
|
+
flagCard("oidcProvider", i18n.t.settings.oidcProvider, i18n.t.settings.oidcProviderDesc),
|
|
182
|
+
]);
|
|
183
|
+
const sections = $derived([
|
|
184
|
+
{ title: i18n.t.settings.signInTitle, subtitle: i18n.t.settings.signInSubtitle, items: signInMethods },
|
|
185
|
+
{ title: i18n.t.settings.emailTitle, subtitle: i18n.t.settings.emailSubtitle, items: emailSettings },
|
|
186
|
+
{ title: i18n.t.settings.securityTitle, subtitle: i18n.t.settings.securitySubtitle, items: security },
|
|
187
|
+
{ title: i18n.t.settings.advancedTitle, subtitle: i18n.t.settings.advancedSubtitle, items: advanced },
|
|
188
|
+
]);
|
|
189
|
+
</script>
|
|
190
|
+
|
|
191
|
+
{#snippet statusBadge(on: boolean, label: string)}
|
|
192
|
+
<Badge variant={on ? "secondary" : "outline"} class="max-w-full gap-1.5">
|
|
193
|
+
<span class={`size-1.5 shrink-0 rounded-full ${on ? "bg-green-500" : "bg-muted-foreground/40"}`}></span><span class="truncate">{label}</span>
|
|
194
|
+
</Badge>
|
|
195
|
+
{/snippet}
|
|
196
|
+
|
|
197
|
+
{#snippet card(item: Item)}
|
|
198
|
+
<Card.Root class="flex min-h-[8.5rem] flex-col gap-2 p-4">
|
|
199
|
+
<div class="flex items-start justify-between gap-3">
|
|
200
|
+
<span class="font-medium">{item.name}</span>
|
|
201
|
+
{#if item.toggle}
|
|
202
|
+
{@const t = item.toggle}
|
|
203
|
+
<Switch aria-label={item.name} checked={t.checked} disabled={t.disabled} onCheckedChange={(v) => t.onChange(v === true)} class="shrink-0" />
|
|
204
|
+
{/if}
|
|
205
|
+
</div>
|
|
206
|
+
<p class="text-muted-foreground flex-1 text-sm">{item.desc}</p>
|
|
207
|
+
<div class="mt-auto flex items-center justify-between gap-2 pt-1">
|
|
208
|
+
{@render statusBadge(item.status.on, item.status.label)}
|
|
209
|
+
{#if item.configure}
|
|
210
|
+
{@const c = item.configure}
|
|
211
|
+
<Button variant="outline" size="sm" class="shrink-0" disabled={!ac} onclick={c.onClick}>{c.label}</Button>
|
|
212
|
+
{/if}
|
|
213
|
+
</div>
|
|
214
|
+
</Card.Root>
|
|
215
|
+
{/snippet}
|
|
216
|
+
|
|
217
|
+
<div class="flex flex-col gap-6">
|
|
218
|
+
<div>
|
|
219
|
+
<h1 class="text-2xl font-semibold">{i18n.t.settings.title}</h1>
|
|
220
|
+
<p class="text-muted-foreground text-sm">{i18n.t.settings.subtitle}</p>
|
|
221
|
+
</div>
|
|
222
|
+
|
|
223
|
+
{#each sections as section (section.title)}
|
|
224
|
+
<section class="flex flex-col gap-3">
|
|
225
|
+
<div>
|
|
226
|
+
<h2 class="text-lg font-semibold">{section.title}</h2>
|
|
227
|
+
<p class="text-muted-foreground text-sm">{section.subtitle}</p>
|
|
228
|
+
</div>
|
|
229
|
+
<div class="grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
|
|
230
|
+
{#each section.items as item (item.key)}
|
|
231
|
+
{@render card(item)}
|
|
232
|
+
{/each}
|
|
233
|
+
</div>
|
|
234
|
+
</section>
|
|
235
|
+
{/each}
|
|
236
|
+
</div>
|
|
237
|
+
|
|
238
|
+
<!-- Social login: manage every provider (list ⇄ add/edit form) in one dialog -->
|
|
239
|
+
<Dialog.Root open={openDialog === "social"} onOpenChange={(v) => { if (!v) openDialog = null; }}>
|
|
240
|
+
<Dialog.Content>
|
|
241
|
+
<Dialog.Header>
|
|
242
|
+
<Dialog.Title>{i18n.t.settings.socialTitle}</Dialog.Title>
|
|
243
|
+
<Dialog.Description>{socialView === "list" ? i18n.t.settings.socialSubtitle : i18n.t.settings.socialDialogDesc}</Dialog.Description>
|
|
244
|
+
</Dialog.Header>
|
|
245
|
+
|
|
246
|
+
{#if socialView === "list"}
|
|
247
|
+
<div class="flex flex-col gap-2">
|
|
248
|
+
{#if configured.length === 0}
|
|
249
|
+
<p class="text-muted-foreground py-2 text-sm">{i18n.t.settings.noProviders}</p>
|
|
250
|
+
{/if}
|
|
251
|
+
{#each configured as p (p.id)}
|
|
252
|
+
{@const st = socialStatus(p)}
|
|
253
|
+
<div class="flex items-center gap-3 rounded-md border p-3">
|
|
254
|
+
<div class="flex min-w-0 flex-1 flex-col gap-1">
|
|
255
|
+
<span class="font-medium">{labelOf(p.id)}</span>
|
|
256
|
+
{@render statusBadge(st.on, st.label)}
|
|
257
|
+
</div>
|
|
258
|
+
<Switch aria-label={labelOf(p.id)} checked={p.enabled} disabled={saving} onCheckedChange={(v) => put({ social: { [p.id]: { enabled: v === true } } })} class="shrink-0" />
|
|
259
|
+
<Button variant="outline" size="sm" class="shrink-0" onclick={() => startEditSocial(p.id)}>{i18n.t.settings.edit}</Button>
|
|
260
|
+
<Button variant="ghost" size="sm" class="text-muted-foreground shrink-0" disabled={saving} onclick={() => removeSocial(p.id)}>{i18n.t.settings.remove}</Button>
|
|
261
|
+
</div>
|
|
262
|
+
{/each}
|
|
263
|
+
</div>
|
|
264
|
+
<Dialog.Footer class="sm:justify-between">
|
|
265
|
+
<Button onclick={startAddSocial} disabled={addable.length === 0}>{i18n.t.settings.addProvider}</Button>
|
|
266
|
+
<Button variant="outline" onclick={() => (openDialog = null)}>{i18n.t.settings.close}</Button>
|
|
267
|
+
</Dialog.Footer>
|
|
268
|
+
{:else}
|
|
269
|
+
<div class="flex flex-col gap-3">
|
|
270
|
+
{#if socialForm.adding}
|
|
271
|
+
<div class="flex flex-col gap-1">
|
|
272
|
+
<Label for="social-provider">{i18n.t.settings.provider}</Label>
|
|
273
|
+
<select
|
|
274
|
+
id="social-provider"
|
|
275
|
+
bind:value={socialForm.id}
|
|
276
|
+
class="border-input bg-background ring-offset-background focus-visible:ring-ring h-9 rounded-md border px-3 text-sm focus-visible:ring-2 focus-visible:outline-none"
|
|
277
|
+
>
|
|
278
|
+
{#each addable as p (p.id)}
|
|
279
|
+
<option value={p.id}>{p.label}</option>
|
|
280
|
+
{/each}
|
|
281
|
+
</select>
|
|
282
|
+
</div>
|
|
283
|
+
{/if}
|
|
284
|
+
<label class="flex items-center justify-between gap-3 text-sm font-medium">{i18n.t.settings.smtpEnabled}<Switch aria-label={i18n.t.settings.smtpEnabled} bind:checked={socialForm.enabled} /></label>
|
|
285
|
+
<div class="flex flex-col gap-1"><Label for="social-id">{i18n.t.settings.clientId}</Label><Input id="social-id" bind:value={socialForm.clientId} autocomplete="off" /></div>
|
|
286
|
+
<div class="flex flex-col gap-1"><Label for="social-secret">{i18n.t.settings.clientSecret}</Label><Input id="social-secret" type="password" bind:value={socialForm.clientSecret} autocomplete="off" placeholder={editingView?.hasSecret ? i18n.t.settings.secretSet : ""} /></div>
|
|
287
|
+
{#if socialForm.id}
|
|
288
|
+
<p class="text-muted-foreground text-xs">{i18n.t.settings.redirectUri}: <code class="break-all">{callbackBase}/api/auth/callback/{socialForm.id}</code></p>
|
|
289
|
+
{/if}
|
|
290
|
+
</div>
|
|
291
|
+
<Dialog.Footer>
|
|
292
|
+
<Button variant="outline" onclick={() => (socialView = "list")}>{i18n.t.settings.back}</Button>
|
|
293
|
+
<Button disabled={saving || !socialForm.id} onclick={saveSocial}>{i18n.t.settings.save}</Button>
|
|
294
|
+
</Dialog.Footer>
|
|
295
|
+
{/if}
|
|
296
|
+
</Dialog.Content>
|
|
297
|
+
</Dialog.Root>
|
|
298
|
+
|
|
299
|
+
<!-- SMTP (email delivery) dialog — reached from the email-verification card -->
|
|
300
|
+
<Dialog.Root open={openDialog === "smtp"} onOpenChange={(v) => { if (!v) openDialog = null; }}>
|
|
301
|
+
<Dialog.Content>
|
|
302
|
+
<Dialog.Header>
|
|
303
|
+
<Dialog.Title>{i18n.t.settings.smtpTitle}</Dialog.Title>
|
|
304
|
+
<Dialog.Description>{i18n.t.settings.smtpSubtitle}</Dialog.Description>
|
|
305
|
+
</Dialog.Header>
|
|
306
|
+
<div class="flex flex-col gap-3">
|
|
307
|
+
<label class="flex items-center justify-between gap-3 text-sm font-medium">{i18n.t.settings.smtpEnabled}<Switch aria-label={i18n.t.settings.smtpEnabled} bind:checked={smtp.enabled} /></label>
|
|
308
|
+
<div class="grid gap-3 sm:grid-cols-2">
|
|
309
|
+
<div class="flex flex-col gap-1"><Label for="smtp-host">{i18n.t.settings.smtpHost}</Label><Input id="smtp-host" bind:value={smtp.host} autocomplete="off" /></div>
|
|
310
|
+
<div class="flex flex-col gap-1"><Label for="smtp-port">{i18n.t.settings.smtpPort}</Label><Input id="smtp-port" type="number" bind:value={smtp.port} /></div>
|
|
311
|
+
<div class="flex flex-col gap-1"><Label for="smtp-user">{i18n.t.settings.smtpUser}</Label><Input id="smtp-user" bind:value={smtp.user} autocomplete="off" /></div>
|
|
312
|
+
<div class="flex flex-col gap-1"><Label for="smtp-pass">{i18n.t.settings.smtpPass}</Label><Input id="smtp-pass" type="password" bind:value={smtp.pass} autocomplete="off" placeholder={ac?.smtp.hasSecret ? i18n.t.settings.secretSet : ""} /></div>
|
|
313
|
+
<div class="flex flex-col gap-1"><Label for="smtp-from">{i18n.t.settings.smtpFrom}</Label><Input id="smtp-from" bind:value={smtp.from} autocomplete="off" /></div>
|
|
314
|
+
<label class="flex items-center gap-2 self-end pb-2 text-sm"><Switch aria-label={i18n.t.settings.smtpSecure} bind:checked={smtp.secure} />{i18n.t.settings.smtpSecure}</label>
|
|
315
|
+
</div>
|
|
316
|
+
</div>
|
|
317
|
+
<Dialog.Footer>
|
|
318
|
+
<Button variant="outline" onclick={() => (openDialog = null)}>{i18n.t.settings.cancel}</Button>
|
|
319
|
+
<Button disabled={saving} onclick={saveSmtp}>{i18n.t.settings.save}</Button>
|
|
320
|
+
</Dialog.Footer>
|
|
321
|
+
</Dialog.Content>
|
|
322
|
+
</Dialog.Root>
|