@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.
Files changed (245) hide show
  1. package/README.md +2 -2
  2. package/dist/add.d.ts +2 -0
  3. package/dist/add.js +5 -1
  4. package/dist/assemble.d.ts +14 -0
  5. package/dist/assemble.js +60 -0
  6. package/dist/create.d.ts +2 -0
  7. package/dist/create.js +7 -0
  8. package/dist/eject.d.ts +11 -0
  9. package/dist/eject.js +27 -0
  10. package/dist/index.d.ts +2 -0
  11. package/dist/index.js +122 -1
  12. package/dist/inspect.d.ts +43 -0
  13. package/dist/inspect.js +82 -0
  14. package/dist/lockfile.d.ts +80 -0
  15. package/dist/lockfile.js +193 -0
  16. package/dist/templates/base/dot-env.example +11 -6
  17. package/dist/templates/fullstack-nest-svelte/README.md +2 -2
  18. package/dist/templates/fullstack-nest-svelte/apps/api/package.json +21 -2
  19. package/dist/templates/fullstack-nest-svelte/apps/api/src/app.module.ts +6 -3
  20. package/dist/templates/fullstack-nest-svelte/apps/api/src/common/app-exception.ts +4 -12
  21. package/dist/templates/fullstack-nest-svelte/apps/api/src/config/env.validation.ts +1 -1
  22. package/dist/templates/fullstack-nest-svelte/apps/api/src/main.ts +1 -1
  23. package/dist/templates/fullstack-nest-svelte/apps/api/tsconfig.json +14 -5
  24. package/dist/templates/fullstack-nest-svelte/apps/web/package.json +4 -3
  25. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/api.ts +5 -0
  26. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-content.svelte +1 -1
  27. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-item.svelte +1 -1
  28. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/switch/index.ts +7 -0
  29. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/switch/switch.svelte +29 -0
  30. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/server/api.ts +19 -0
  31. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/server/backend-proxy.ts +44 -5
  32. package/dist/templates/fullstack-nest-svelte/apps/web/src/routes/+page.svelte +8 -3
  33. package/dist/templates/fullstack-nest-svelte/apps/web/src/routes/api/[...path]/+server.ts +12 -0
  34. package/dist/templates/fullstack-nest-svelte/apps/web/src/routes/api/auth/[...all]/+server.ts +12 -0
  35. package/dist/templates/fullstack-nest-svelte/dot-env.example +10 -5
  36. package/dist/templates/fullstack-nest-svelte/dot-gitignore +5 -0
  37. package/dist/templates/fullstack-nest-svelte/infra/docker/docker-compose.yml +37 -0
  38. package/dist/templates/fullstack-nest-svelte/infra/docker/sms-sink.mjs +40 -0
  39. package/dist/templates/fullstack-nest-svelte/package.json +11 -3
  40. package/dist/templates/fullstack-nest-svelte/tests/README.md +15 -0
  41. package/dist/templates/fullstack-nest-svelte/tests/api/health.api.spec.ts +14 -0
  42. package/dist/templates/fullstack-nest-svelte/tests/package.json +15 -0
  43. package/dist/templates/fullstack-nest-svelte/tests/playwright.config.ts +25 -0
  44. package/dist/templates/fullstack-nest-svelte/tests/ui/smoke.ui.spec.ts +8 -0
  45. package/dist/templates/modules/admin-dashboard/files/apps/web/src/app.d.ts +26 -0
  46. package/dist/templates/modules/admin-dashboard/files/apps/web/src/hooks.server.ts +20 -0
  47. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/app-sidebar.svelte +65 -0
  48. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/data-table.svelte +149 -0
  49. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/language-switch.svelte +33 -0
  50. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/sidebar-user-menu.svelte +44 -0
  51. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/table-pagination.svelte +39 -0
  52. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/table-toolbar.svelte +93 -0
  53. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/theme-toggle.svelte +27 -0
  54. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/collapsible/collapsible-content.svelte +7 -0
  55. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/collapsible/collapsible-trigger.svelte +7 -0
  56. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/collapsible/collapsible.svelte +11 -0
  57. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/collapsible/index.ts +13 -0
  58. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/index.ts +31 -0
  59. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-content.svelte +20 -0
  60. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-ellipsis.svelte +22 -0
  61. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-item.svelte +14 -0
  62. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-link.svelte +42 -0
  63. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-next-button.svelte +31 -0
  64. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-next.svelte +26 -0
  65. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-prev-button.svelte +31 -0
  66. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-previous.svelte +26 -0
  67. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination.svelte +28 -0
  68. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/index.ts +34 -0
  69. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-close.svelte +7 -0
  70. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-content.svelte +55 -0
  71. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-description.svelte +17 -0
  72. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-footer.svelte +20 -0
  73. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-header.svelte +20 -0
  74. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-overlay.svelte +17 -0
  75. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-portal.svelte +7 -0
  76. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-title.svelte +17 -0
  77. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-trigger.svelte +7 -0
  78. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet.svelte +7 -0
  79. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/constants.ts +6 -0
  80. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/context.svelte.ts +81 -0
  81. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/index.ts +75 -0
  82. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-content.svelte +24 -0
  83. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-footer.svelte +21 -0
  84. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-group-action.svelte +33 -0
  85. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-group-content.svelte +21 -0
  86. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-group-label.svelte +33 -0
  87. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-group.svelte +21 -0
  88. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-header.svelte +21 -0
  89. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-input.svelte +21 -0
  90. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-inset.svelte +20 -0
  91. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-action.svelte +37 -0
  92. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte +24 -0
  93. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-button.svelte +102 -0
  94. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-item.svelte +21 -0
  95. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte +36 -0
  96. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte +39 -0
  97. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte +21 -0
  98. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte +21 -0
  99. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu.svelte +21 -0
  100. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-provider.svelte +53 -0
  101. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-rail.svelte +36 -0
  102. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-separator.svelte +19 -0
  103. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-trigger.svelte +36 -0
  104. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar.svelte +108 -0
  105. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/user-avatar.svelte +12 -0
  106. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/hooks/is-mobile.svelte.ts +9 -0
  107. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/index.ts +27 -0
  108. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/messages.ts +575 -0
  109. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/guards.ts +22 -0
  110. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/+layout.server.ts +30 -0
  111. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/+layout.svelte +50 -0
  112. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/accept-invitation/[id]/+page.svelte +36 -0
  113. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/+page.svelte +45 -0
  114. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/account/+page.server.ts +5 -0
  115. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/account/+page.svelte +685 -0
  116. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/audit/+page.server.ts +6 -0
  117. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/audit/+page.svelte +123 -0
  118. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/organizations/+page.server.ts +6 -0
  119. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/organizations/+page.svelte +264 -0
  120. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/sessions/+page.server.ts +7 -0
  121. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/sessions/+page.svelte +156 -0
  122. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/settings/+page.server.ts +26 -0
  123. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/settings/+page.svelte +322 -0
  124. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/users/+page.server.ts +7 -0
  125. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/users/+page.svelte +552 -0
  126. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/+layout.svelte +13 -0
  127. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/forgot-password/+page.svelte +61 -0
  128. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/login/+page.server.ts +14 -0
  129. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/login/+page.svelte +181 -0
  130. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/reset-password/+page.svelte +51 -0
  131. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/signup/+page.svelte +76 -0
  132. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/verify-email/+page.svelte +63 -0
  133. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/+layout.server.ts +22 -0
  134. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/+layout.svelte +20 -0
  135. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/+page.svelte +22 -0
  136. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/oauth2/consent/+page.svelte +54 -0
  137. package/dist/templates/modules/admin-dashboard/files/tests/api/audit.api.spec.ts +97 -0
  138. package/dist/templates/modules/admin-dashboard/files/tests/api/auth-config.api.spec.ts +80 -0
  139. package/dist/templates/modules/admin-dashboard/files/tests/api/auth.api.spec.ts +306 -0
  140. package/dist/templates/modules/admin-dashboard/files/tests/api/email.api.spec.ts +120 -0
  141. package/dist/templates/modules/admin-dashboard/files/tests/api/users-admin.api.spec.ts +134 -0
  142. package/dist/templates/modules/admin-dashboard/files/tests/helpers/accounts.ts +6 -0
  143. package/dist/templates/modules/admin-dashboard/files/tests/helpers/hydration.ts +9 -0
  144. package/dist/templates/modules/admin-dashboard/files/tests/helpers/mailpit.ts +49 -0
  145. package/dist/templates/modules/admin-dashboard/files/tests/helpers/sms.ts +35 -0
  146. package/dist/templates/modules/admin-dashboard/files/tests/helpers/totp.ts +9 -0
  147. package/dist/templates/modules/admin-dashboard/files/tests/playwright.config.ts +26 -0
  148. package/dist/templates/modules/admin-dashboard/files/tests/seed.setup.ts +40 -0
  149. package/dist/templates/modules/admin-dashboard/files/tests/ui/account.ui.spec.ts +126 -0
  150. package/dist/templates/modules/admin-dashboard/files/tests/ui/audit.ui.spec.ts +16 -0
  151. package/dist/templates/modules/admin-dashboard/files/tests/ui/auth.ui.spec.ts +45 -0
  152. package/dist/templates/modules/admin-dashboard/files/tests/ui/dashboard.ui.spec.ts +25 -0
  153. package/dist/templates/modules/admin-dashboard/files/tests/ui/data-table.ui.spec.ts +32 -0
  154. package/dist/templates/modules/admin-dashboard/files/tests/ui/email-admin.ui.spec.ts +60 -0
  155. package/dist/templates/modules/admin-dashboard/files/tests/ui/email.ui.spec.ts +42 -0
  156. package/dist/templates/modules/admin-dashboard/files/tests/ui/guards.ui.spec.ts +22 -0
  157. package/dist/templates/modules/admin-dashboard/files/tests/ui/i18n.ui.spec.ts +13 -0
  158. package/dist/templates/modules/admin-dashboard/files/tests/ui/oidc.ui.spec.ts +57 -0
  159. package/dist/templates/modules/admin-dashboard/files/tests/ui/organizations.ui.spec.ts +52 -0
  160. package/dist/templates/modules/admin-dashboard/files/tests/ui/passkey.ui.spec.ts +36 -0
  161. package/dist/templates/modules/admin-dashboard/files/tests/ui/sessions.ui.spec.ts +22 -0
  162. package/dist/templates/modules/admin-dashboard/files/tests/ui/settings.ui.spec.ts +34 -0
  163. package/dist/templates/modules/admin-dashboard/files/tests/ui/sidebar.ui.spec.ts +16 -0
  164. package/dist/templates/modules/admin-dashboard/files/tests/ui/table-toolbar.ui.spec.ts +32 -0
  165. package/dist/templates/modules/admin-dashboard/files/tests/ui/users-crud.ui.spec.ts +145 -0
  166. package/dist/templates/modules/admin-dashboard/files/tests/ui/users.ui.spec.ts +51 -0
  167. package/dist/templates/modules/admin-dashboard/module.manifest.json +47 -0
  168. package/dist/templates/modules/api-key-auth/module.manifest.json +3 -3
  169. package/dist/templates/modules/audit-log/files/apps/api/src/audit/README.md +59 -0
  170. package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit-enabled.ts +16 -0
  171. package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit-events.ts +34 -0
  172. package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit-hook.ts +74 -0
  173. package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit-log.entity.ts +29 -8
  174. package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.controller.ts +7 -2
  175. package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.decorator.ts +23 -0
  176. package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.interceptor.ts +38 -21
  177. package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.module.ts +2 -1
  178. package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.service.ts +36 -0
  179. package/dist/templates/modules/audit-log/files/apps/api/src/migrations/1720300000000-InitAuditLogs.ts +10 -4
  180. package/dist/templates/modules/audit-log/module.manifest.json +37 -8
  181. package/dist/templates/modules/auth/files/apps/api/src/account/account.controller.ts +85 -3
  182. package/dist/templates/modules/auth/files/apps/api/src/account/account.module.ts +2 -1
  183. package/dist/templates/modules/auth/files/apps/api/src/auth/auth-provider.ts +73 -0
  184. package/dist/templates/modules/auth/files/apps/api/src/auth/auth.ts +199 -31
  185. package/dist/templates/modules/auth/files/apps/api/src/auth/db.ts +12 -0
  186. package/dist/templates/modules/auth/files/apps/api/src/auth/feature-gate.ts +56 -0
  187. package/dist/templates/modules/auth/files/apps/api/src/auth/org-permissions.ts +22 -0
  188. package/dist/templates/modules/auth/files/apps/api/src/auth/permissions.ts +23 -0
  189. package/dist/templates/modules/auth/files/apps/api/src/auth-config/auth-config.controller.ts +33 -0
  190. package/dist/templates/modules/auth/files/apps/api/src/auth-config/auth-config.entity.ts +26 -0
  191. package/dist/templates/modules/auth/files/apps/api/src/auth-config/auth-config.module.ts +12 -0
  192. package/dist/templates/modules/auth/files/apps/api/src/auth-config/auth-config.service.ts +118 -0
  193. package/dist/templates/modules/auth/files/apps/api/src/mail/mailer.ts +65 -0
  194. package/dist/templates/modules/auth/files/apps/api/src/migrations/1720400000000-InitAppSettings.ts +38 -0
  195. package/dist/templates/modules/auth/files/apps/api/src/migrations/1720500000000-InitAuthConfig.ts +27 -0
  196. package/dist/templates/modules/auth/files/apps/api/src/settings/app-setting.entity.ts +16 -0
  197. package/dist/templates/modules/auth/files/apps/api/src/settings/flag-defaults.ts +25 -0
  198. package/dist/templates/modules/auth/files/apps/api/src/settings/settings.module.ts +11 -0
  199. package/dist/templates/modules/auth/files/apps/api/src/settings/settings.service.ts +57 -0
  200. package/dist/templates/modules/auth/files/apps/api/src/sms/sms.ts +23 -0
  201. package/dist/templates/modules/auth/module.manifest.json +54 -20
  202. package/dist/templates/modules/bullmq/files/apps/api/src/jobs/worker.module.ts +6 -3
  203. package/dist/templates/modules/bullmq/module.manifest.json +4 -4
  204. package/dist/templates/modules/file-upload/module.manifest.json +3 -3
  205. package/dist/templates/modules/job-progress/module.manifest.json +9 -9
  206. package/dist/templates/modules/logging/module.manifest.json +3 -3
  207. package/dist/templates/modules/object-storage-s3/files/infra/docker/minio.compose.yml +5 -3
  208. package/dist/templates/modules/object-storage-s3/module.manifest.json +5 -5
  209. package/dist/templates/modules/rate-limit/module.manifest.json +2 -2
  210. package/dist/templates/modules/redis/module.manifest.json +4 -4
  211. package/dist/templates/modules/sse/module.manifest.json +4 -4
  212. package/dist/templates/todo/README.md +2 -2
  213. package/dist/templates/todo/apps/api/package.json +21 -2
  214. package/dist/templates/todo/apps/api/src/app.module.ts +6 -3
  215. package/dist/templates/todo/apps/api/src/common/app-exception.ts +4 -12
  216. package/dist/templates/todo/apps/api/src/config/env.validation.ts +1 -1
  217. package/dist/templates/todo/apps/api/src/main.ts +1 -1
  218. package/dist/templates/todo/apps/api/tsconfig.json +14 -5
  219. package/dist/templates/todo/apps/web/package.json +4 -3
  220. package/dist/templates/todo/apps/web/src/lib/api.ts +5 -0
  221. package/dist/templates/todo/apps/web/src/lib/components/ui/switch/index.ts +7 -0
  222. package/dist/templates/todo/apps/web/src/lib/components/ui/switch/switch.svelte +29 -0
  223. package/dist/templates/todo/apps/web/src/lib/server/api.ts +19 -0
  224. package/dist/templates/todo/apps/web/src/lib/server/backend-proxy.ts +44 -5
  225. package/dist/templates/todo/apps/web/src/routes/+page.svelte +19 -18
  226. package/dist/templates/todo/apps/web/src/routes/api/[...path]/+server.ts +12 -0
  227. package/dist/templates/todo/apps/web/src/routes/api/auth/[...all]/+server.ts +12 -0
  228. package/dist/templates/todo/dot-env.example +10 -5
  229. package/dist/templates/todo/dot-gitignore +5 -0
  230. package/dist/templates/todo/infra/docker/docker-compose.yml +4 -0
  231. package/dist/templates/todo/package.json +11 -3
  232. package/dist/templates/todo/tests/README.md +15 -0
  233. package/dist/templates/todo/tests/api/health.api.spec.ts +14 -0
  234. package/dist/templates/todo/tests/package.json +15 -0
  235. package/dist/templates/todo/tests/playwright.config.ts +25 -0
  236. package/dist/templates/todo/tests/ui/smoke.ui.spec.ts +8 -0
  237. package/dist/update.d.ts +49 -0
  238. package/dist/update.js +158 -0
  239. package/package.json +2 -2
  240. package/dist/templates/fullstack-nest-svelte/apps/api/test/health.e2e-spec.ts +0 -23
  241. package/dist/templates/fullstack-nest-svelte/apps/web/src/routes/api/health/+server.ts +0 -12
  242. package/dist/templates/todo/apps/api/test/health.e2e-spec.ts +0 -23
  243. package/dist/templates/todo/apps/web/src/routes/api/health/+server.ts +0 -12
  244. package/dist/templates/todo/apps/web/src/routes/api/todos/+server.ts +0 -24
  245. 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>
@@ -0,0 +1,7 @@
1
+ import type { PageServerLoad } from "./$types";
2
+ import { requireAdmin } from "$lib/server/guards";
3
+
4
+ export const load: PageServerLoad = ({ locals }) => {
5
+ requireAdmin(locals.user);
6
+ return { currentUserId: locals.user!.id };
7
+ };