@podosoft/podokit 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (342) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +1 -1
  3. package/dist/templates/fullstack-nest-svelte/apps/api/package.json +29 -11
  4. package/dist/templates/fullstack-nest-svelte/apps/api/src/app.module.ts +3 -0
  5. package/dist/templates/fullstack-nest-svelte/apps/api/tsconfig.json +14 -5
  6. package/dist/templates/fullstack-nest-svelte/apps/web/package.json +8 -6
  7. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/api.ts +5 -0
  8. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/alert/alert-action.svelte +20 -0
  9. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/alert/alert-description.svelte +23 -0
  10. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/alert/alert-title.svelte +23 -0
  11. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/alert/alert.svelte +43 -0
  12. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/alert/index.ts +17 -0
  13. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/avatar/avatar-badge.svelte +26 -0
  14. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/avatar/avatar-fallback.svelte +20 -0
  15. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/avatar/avatar-group-count.svelte +23 -0
  16. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/avatar/avatar-group.svelte +23 -0
  17. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/avatar/avatar-image.svelte +17 -0
  18. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/avatar/avatar.svelte +26 -0
  19. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/avatar/index.ts +22 -0
  20. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/badge/badge.svelte +49 -0
  21. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/badge/index.ts +2 -0
  22. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/dialog-close.svelte +11 -0
  23. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/dialog-content.svelte +48 -0
  24. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/dialog-description.svelte +17 -0
  25. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/dialog-footer.svelte +32 -0
  26. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/dialog-header.svelte +20 -0
  27. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/dialog-overlay.svelte +17 -0
  28. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/dialog-portal.svelte +7 -0
  29. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/dialog-title.svelte +17 -0
  30. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/dialog-trigger.svelte +11 -0
  31. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/dialog.svelte +7 -0
  32. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dialog/index.ts +34 -0
  33. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
  34. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +44 -0
  35. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte +31 -0
  36. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
  37. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte +7 -0
  38. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte +27 -0
  39. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte +24 -0
  40. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte +7 -0
  41. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
  42. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +34 -0
  43. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte +17 -0
  44. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte +20 -0
  45. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +17 -0
  46. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
  47. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte +7 -0
  48. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
  49. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte +7 -0
  50. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/dropdown-menu/index.ts +54 -0
  51. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/index.ts +37 -0
  52. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-content.svelte +45 -0
  53. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-group-heading.svelte +21 -0
  54. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-group.svelte +17 -0
  55. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-item.svelte +40 -0
  56. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-label.svelte +20 -0
  57. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-portal.svelte +7 -0
  58. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-scroll-down-button.svelte +20 -0
  59. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-scroll-up-button.svelte +20 -0
  60. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-separator.svelte +18 -0
  61. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-trigger.svelte +29 -0
  62. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select.svelte +11 -0
  63. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/separator/index.ts +7 -0
  64. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/separator/separator.svelte +23 -0
  65. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/skeleton/index.ts +7 -0
  66. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/skeleton/skeleton.svelte +17 -0
  67. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/sonner/index.ts +1 -0
  68. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/sonner/sonner.svelte +34 -0
  69. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/table/index.ts +28 -0
  70. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/table/table-body.svelte +15 -0
  71. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/table/table-caption.svelte +20 -0
  72. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/table/table-cell.svelte +15 -0
  73. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/table/table-footer.svelte +20 -0
  74. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/table/table-head.svelte +15 -0
  75. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/table/table-header.svelte +20 -0
  76. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/table/table-row.svelte +15 -0
  77. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/table/table.svelte +17 -0
  78. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tabs/index.ts +18 -0
  79. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tabs/tabs-content.svelte +17 -0
  80. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tabs/tabs-list.svelte +40 -0
  81. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tabs/tabs-trigger.svelte +23 -0
  82. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tabs/tabs.svelte +19 -0
  83. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tooltip/index.ts +19 -0
  84. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tooltip/tooltip-content.svelte +52 -0
  85. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tooltip/tooltip-portal.svelte +7 -0
  86. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tooltip/tooltip-provider.svelte +7 -0
  87. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tooltip/tooltip-trigger.svelte +7 -0
  88. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tooltip/tooltip.svelte +7 -0
  89. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/i18n/ko.ts +5 -2
  90. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/server/api.ts +19 -0
  91. package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/server/backend-proxy.ts +43 -4
  92. package/dist/templates/fullstack-nest-svelte/apps/web/src/routes/+page.svelte +8 -3
  93. package/dist/templates/fullstack-nest-svelte/apps/web/src/routes/api/[...path]/+server.ts +12 -0
  94. package/dist/templates/fullstack-nest-svelte/apps/web/src/routes/api/auth/[...all]/+server.ts +12 -0
  95. package/dist/templates/fullstack-nest-svelte/dot-gitignore +5 -0
  96. package/dist/templates/fullstack-nest-svelte/package.json +11 -3
  97. package/dist/templates/fullstack-nest-svelte/tests/README.md +15 -0
  98. package/dist/templates/fullstack-nest-svelte/tests/api/health.api.spec.ts +14 -0
  99. package/dist/templates/fullstack-nest-svelte/tests/package.json +14 -0
  100. package/dist/templates/fullstack-nest-svelte/tests/playwright.config.ts +25 -0
  101. package/dist/templates/fullstack-nest-svelte/tests/ui/smoke.ui.spec.ts +8 -0
  102. package/dist/templates/modules/admin-dashboard/files/apps/web/src/app.d.ts +23 -0
  103. package/dist/templates/modules/admin-dashboard/files/apps/web/src/hooks.server.ts +20 -0
  104. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/app-sidebar.svelte +59 -0
  105. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/language-switch.svelte +33 -0
  106. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/sidebar-user-menu.svelte +44 -0
  107. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/table-pagination.svelte +40 -0
  108. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/theme-toggle.svelte +27 -0
  109. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/collapsible/collapsible-content.svelte +7 -0
  110. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/collapsible/collapsible-trigger.svelte +7 -0
  111. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/collapsible/collapsible.svelte +11 -0
  112. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/collapsible/index.ts +13 -0
  113. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/index.ts +31 -0
  114. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-content.svelte +20 -0
  115. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-ellipsis.svelte +22 -0
  116. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-item.svelte +14 -0
  117. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-link.svelte +42 -0
  118. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-next-button.svelte +31 -0
  119. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-next.svelte +26 -0
  120. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-prev-button.svelte +31 -0
  121. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination-previous.svelte +26 -0
  122. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/pagination/pagination.svelte +28 -0
  123. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/index.ts +34 -0
  124. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-close.svelte +7 -0
  125. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-content.svelte +55 -0
  126. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-description.svelte +17 -0
  127. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-footer.svelte +20 -0
  128. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-header.svelte +20 -0
  129. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-overlay.svelte +17 -0
  130. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-portal.svelte +7 -0
  131. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-title.svelte +17 -0
  132. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet-trigger.svelte +7 -0
  133. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sheet/sheet.svelte +7 -0
  134. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/constants.ts +6 -0
  135. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/context.svelte.ts +81 -0
  136. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/index.ts +75 -0
  137. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-content.svelte +24 -0
  138. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-footer.svelte +21 -0
  139. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-group-action.svelte +33 -0
  140. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-group-content.svelte +21 -0
  141. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-group-label.svelte +33 -0
  142. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-group.svelte +21 -0
  143. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-header.svelte +21 -0
  144. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-input.svelte +21 -0
  145. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-inset.svelte +20 -0
  146. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-action.svelte +37 -0
  147. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte +24 -0
  148. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-button.svelte +102 -0
  149. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-item.svelte +21 -0
  150. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte +36 -0
  151. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte +39 -0
  152. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte +21 -0
  153. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte +21 -0
  154. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-menu.svelte +21 -0
  155. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-provider.svelte +53 -0
  156. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-rail.svelte +36 -0
  157. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-separator.svelte +19 -0
  158. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar-trigger.svelte +36 -0
  159. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/ui/sidebar/sidebar.svelte +108 -0
  160. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/user-avatar.svelte +12 -0
  161. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/hooks/is-mobile.svelte.ts +9 -0
  162. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/index.ts +27 -0
  163. package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/messages.ts +359 -0
  164. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/+layout.server.ts +14 -0
  165. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/+layout.svelte +50 -0
  166. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/+page.svelte +25 -0
  167. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/account/+page.server.ts +5 -0
  168. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/account/+page.svelte +374 -0
  169. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/audit/+page.server.ts +8 -0
  170. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/audit/+page.svelte +78 -0
  171. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/sessions/+page.server.ts +8 -0
  172. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/sessions/+page.svelte +122 -0
  173. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/users/+page.server.ts +9 -0
  174. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/users/+page.svelte +406 -0
  175. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/+layout.svelte +13 -0
  176. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/forgot-password/+page.svelte +54 -0
  177. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/login/+page.svelte +59 -0
  178. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/reset-password/+page.svelte +51 -0
  179. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/signup/+page.svelte +60 -0
  180. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/+layout.server.ts +22 -0
  181. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/+layout.svelte +20 -0
  182. package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/+page.svelte +22 -0
  183. package/dist/templates/modules/admin-dashboard/files/tests/api/audit.api.spec.ts +48 -0
  184. package/dist/templates/modules/admin-dashboard/files/tests/api/auth.api.spec.ts +43 -0
  185. package/dist/templates/modules/admin-dashboard/files/tests/api/users-admin.api.spec.ts +134 -0
  186. package/dist/templates/modules/admin-dashboard/files/tests/helpers/accounts.ts +6 -0
  187. package/dist/templates/modules/admin-dashboard/files/tests/helpers/hydration.ts +9 -0
  188. package/dist/templates/modules/admin-dashboard/files/tests/playwright.config.ts +26 -0
  189. package/dist/templates/modules/admin-dashboard/files/tests/seed.setup.ts +29 -0
  190. package/dist/templates/modules/admin-dashboard/files/tests/ui/account.ui.spec.ts +55 -0
  191. package/dist/templates/modules/admin-dashboard/files/tests/ui/audit.ui.spec.ts +16 -0
  192. package/dist/templates/modules/admin-dashboard/files/tests/ui/auth.ui.spec.ts +45 -0
  193. package/dist/templates/modules/admin-dashboard/files/tests/ui/dashboard.ui.spec.ts +25 -0
  194. package/dist/templates/modules/admin-dashboard/files/tests/ui/guards.ui.spec.ts +21 -0
  195. package/dist/templates/modules/admin-dashboard/files/tests/ui/i18n.ui.spec.ts +13 -0
  196. package/dist/templates/modules/admin-dashboard/files/tests/ui/sessions.ui.spec.ts +15 -0
  197. package/dist/templates/modules/admin-dashboard/files/tests/ui/sidebar.ui.spec.ts +16 -0
  198. package/dist/templates/modules/admin-dashboard/files/tests/ui/users-crud.ui.spec.ts +129 -0
  199. package/dist/templates/modules/admin-dashboard/files/tests/ui/users.ui.spec.ts +19 -0
  200. package/dist/templates/modules/admin-dashboard/module.manifest.json +41 -0
  201. package/dist/templates/modules/api-key-auth/files/apps/api/src/api-key/api-key.decorator.ts +9 -0
  202. package/dist/templates/modules/api-key-auth/files/apps/api/src/api-key/api-key.guard.ts +29 -0
  203. package/dist/templates/modules/api-key-auth/files/apps/api/src/api-key/api-key.module.ts +9 -0
  204. package/dist/templates/modules/api-key-auth/files/apps/api/src/machine/machine.controller.ts +14 -0
  205. package/dist/templates/modules/api-key-auth/module.manifest.json +19 -0
  206. package/dist/templates/modules/audit-log/files/apps/api/src/audit/README.md +59 -0
  207. package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit-events.ts +34 -0
  208. package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit-hook.ts +70 -0
  209. package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit-log.entity.ts +46 -0
  210. package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.controller.ts +21 -0
  211. package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.decorator.ts +23 -0
  212. package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.interceptor.ts +54 -0
  213. package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.module.ts +14 -0
  214. package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.service.ts +36 -0
  215. package/dist/templates/modules/audit-log/files/apps/api/src/migrations/1720300000000-InitAuditLogs.ts +31 -0
  216. package/dist/templates/modules/audit-log/module.manifest.json +39 -0
  217. package/dist/templates/modules/auth/files/apps/api/src/account/account.controller.ts +30 -0
  218. package/dist/templates/modules/auth/files/apps/api/src/account/account.module.ts +5 -0
  219. package/dist/templates/modules/auth/files/apps/api/src/auth/auth.ts +59 -0
  220. package/dist/templates/modules/auth/files/apps/api/src/health/health.controller.ts +26 -0
  221. package/dist/templates/modules/auth/module.manifest.json +70 -0
  222. package/dist/templates/modules/bullmq/module.manifest.json +11 -3
  223. package/dist/templates/modules/logging/files/apps/api/src/logging/logging.module.ts +29 -0
  224. package/dist/templates/modules/logging/module.manifest.json +27 -0
  225. package/dist/templates/modules/rate-limit/files/apps/api/src/rate-limit/rate-limit.module.ts +26 -0
  226. package/dist/templates/modules/rate-limit/module.manifest.json +25 -0
  227. package/dist/templates/todo/apps/api/package.json +29 -11
  228. package/dist/templates/todo/apps/api/src/app.module.ts +3 -0
  229. package/dist/templates/todo/apps/api/tsconfig.json +14 -5
  230. package/dist/templates/todo/apps/web/package.json +8 -6
  231. package/dist/templates/todo/apps/web/src/lib/api.ts +5 -0
  232. package/dist/templates/todo/apps/web/src/lib/components/ui/alert/alert-action.svelte +20 -0
  233. package/dist/templates/todo/apps/web/src/lib/components/ui/alert/alert-description.svelte +23 -0
  234. package/dist/templates/todo/apps/web/src/lib/components/ui/alert/alert-title.svelte +23 -0
  235. package/dist/templates/todo/apps/web/src/lib/components/ui/alert/alert.svelte +43 -0
  236. package/dist/templates/todo/apps/web/src/lib/components/ui/alert/index.ts +17 -0
  237. package/dist/templates/todo/apps/web/src/lib/components/ui/avatar/avatar-badge.svelte +26 -0
  238. package/dist/templates/todo/apps/web/src/lib/components/ui/avatar/avatar-fallback.svelte +20 -0
  239. package/dist/templates/todo/apps/web/src/lib/components/ui/avatar/avatar-group-count.svelte +23 -0
  240. package/dist/templates/todo/apps/web/src/lib/components/ui/avatar/avatar-group.svelte +23 -0
  241. package/dist/templates/todo/apps/web/src/lib/components/ui/avatar/avatar-image.svelte +17 -0
  242. package/dist/templates/todo/apps/web/src/lib/components/ui/avatar/avatar.svelte +26 -0
  243. package/dist/templates/todo/apps/web/src/lib/components/ui/avatar/index.ts +22 -0
  244. package/dist/templates/todo/apps/web/src/lib/components/ui/badge/badge.svelte +49 -0
  245. package/dist/templates/todo/apps/web/src/lib/components/ui/badge/index.ts +2 -0
  246. package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/dialog-close.svelte +11 -0
  247. package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/dialog-content.svelte +48 -0
  248. package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/dialog-description.svelte +17 -0
  249. package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/dialog-footer.svelte +32 -0
  250. package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/dialog-header.svelte +20 -0
  251. package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/dialog-overlay.svelte +17 -0
  252. package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/dialog-portal.svelte +7 -0
  253. package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/dialog-title.svelte +17 -0
  254. package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/dialog-trigger.svelte +11 -0
  255. package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/dialog.svelte +7 -0
  256. package/dist/templates/todo/apps/web/src/lib/components/ui/dialog/index.ts +34 -0
  257. package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
  258. package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +44 -0
  259. package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte +31 -0
  260. package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
  261. package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte +7 -0
  262. package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte +27 -0
  263. package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte +24 -0
  264. package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte +7 -0
  265. package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
  266. package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +34 -0
  267. package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte +17 -0
  268. package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte +20 -0
  269. package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +17 -0
  270. package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
  271. package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte +7 -0
  272. package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
  273. package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte +7 -0
  274. package/dist/templates/todo/apps/web/src/lib/components/ui/dropdown-menu/index.ts +54 -0
  275. package/dist/templates/todo/apps/web/src/lib/components/ui/select/index.ts +37 -0
  276. package/dist/templates/todo/apps/web/src/lib/components/ui/select/select-content.svelte +45 -0
  277. package/dist/templates/todo/apps/web/src/lib/components/ui/select/select-group-heading.svelte +21 -0
  278. package/dist/templates/todo/apps/web/src/lib/components/ui/select/select-group.svelte +17 -0
  279. package/dist/templates/todo/apps/web/src/lib/components/ui/select/select-item.svelte +40 -0
  280. package/dist/templates/todo/apps/web/src/lib/components/ui/select/select-label.svelte +20 -0
  281. package/dist/templates/todo/apps/web/src/lib/components/ui/select/select-portal.svelte +7 -0
  282. package/dist/templates/todo/apps/web/src/lib/components/ui/select/select-scroll-down-button.svelte +20 -0
  283. package/dist/templates/todo/apps/web/src/lib/components/ui/select/select-scroll-up-button.svelte +20 -0
  284. package/dist/templates/todo/apps/web/src/lib/components/ui/select/select-separator.svelte +18 -0
  285. package/dist/templates/todo/apps/web/src/lib/components/ui/select/select-trigger.svelte +29 -0
  286. package/dist/templates/todo/apps/web/src/lib/components/ui/select/select.svelte +11 -0
  287. package/dist/templates/todo/apps/web/src/lib/components/ui/separator/index.ts +7 -0
  288. package/dist/templates/todo/apps/web/src/lib/components/ui/separator/separator.svelte +23 -0
  289. package/dist/templates/todo/apps/web/src/lib/components/ui/skeleton/index.ts +7 -0
  290. package/dist/templates/todo/apps/web/src/lib/components/ui/skeleton/skeleton.svelte +17 -0
  291. package/dist/templates/todo/apps/web/src/lib/components/ui/sonner/index.ts +1 -0
  292. package/dist/templates/todo/apps/web/src/lib/components/ui/sonner/sonner.svelte +34 -0
  293. package/dist/templates/todo/apps/web/src/lib/components/ui/table/index.ts +28 -0
  294. package/dist/templates/todo/apps/web/src/lib/components/ui/table/table-body.svelte +15 -0
  295. package/dist/templates/todo/apps/web/src/lib/components/ui/table/table-caption.svelte +20 -0
  296. package/dist/templates/todo/apps/web/src/lib/components/ui/table/table-cell.svelte +15 -0
  297. package/dist/templates/todo/apps/web/src/lib/components/ui/table/table-footer.svelte +20 -0
  298. package/dist/templates/todo/apps/web/src/lib/components/ui/table/table-head.svelte +15 -0
  299. package/dist/templates/todo/apps/web/src/lib/components/ui/table/table-header.svelte +20 -0
  300. package/dist/templates/todo/apps/web/src/lib/components/ui/table/table-row.svelte +15 -0
  301. package/dist/templates/todo/apps/web/src/lib/components/ui/table/table.svelte +17 -0
  302. package/dist/templates/todo/apps/web/src/lib/components/ui/tabs/index.ts +18 -0
  303. package/dist/templates/todo/apps/web/src/lib/components/ui/tabs/tabs-content.svelte +17 -0
  304. package/dist/templates/todo/apps/web/src/lib/components/ui/tabs/tabs-list.svelte +40 -0
  305. package/dist/templates/todo/apps/web/src/lib/components/ui/tabs/tabs-trigger.svelte +23 -0
  306. package/dist/templates/todo/apps/web/src/lib/components/ui/tabs/tabs.svelte +19 -0
  307. package/dist/templates/todo/apps/web/src/lib/components/ui/tooltip/index.ts +19 -0
  308. package/dist/templates/todo/apps/web/src/lib/components/ui/tooltip/tooltip-content.svelte +52 -0
  309. package/dist/templates/todo/apps/web/src/lib/components/ui/tooltip/tooltip-portal.svelte +7 -0
  310. package/dist/templates/todo/apps/web/src/lib/components/ui/tooltip/tooltip-provider.svelte +7 -0
  311. package/dist/templates/todo/apps/web/src/lib/components/ui/tooltip/tooltip-trigger.svelte +7 -0
  312. package/dist/templates/todo/apps/web/src/lib/components/ui/tooltip/tooltip.svelte +7 -0
  313. package/dist/templates/todo/apps/web/src/lib/i18n/ko.ts +5 -2
  314. package/dist/templates/todo/apps/web/src/lib/server/api.ts +19 -0
  315. package/dist/templates/todo/apps/web/src/lib/server/backend-proxy.ts +43 -4
  316. package/dist/templates/todo/apps/web/src/routes/+page.svelte +19 -18
  317. package/dist/templates/todo/apps/web/src/routes/api/[...path]/+server.ts +12 -0
  318. package/dist/templates/todo/apps/web/src/routes/api/auth/[...all]/+server.ts +12 -0
  319. package/dist/templates/todo/dot-gitignore +5 -0
  320. package/dist/templates/todo/package.json +11 -3
  321. package/dist/templates/todo/tests/README.md +15 -0
  322. package/dist/templates/todo/tests/api/health.api.spec.ts +14 -0
  323. package/dist/templates/todo/tests/package.json +14 -0
  324. package/dist/templates/todo/tests/playwright.config.ts +25 -0
  325. package/dist/templates/todo/tests/ui/smoke.ui.spec.ts +8 -0
  326. package/package.json +2 -2
  327. package/dist/templates/fullstack-nest-svelte/apps/api/test/health.e2e-spec.ts +0 -23
  328. package/dist/templates/fullstack-nest-svelte/apps/web/src/routes/api/health/+server.ts +0 -12
  329. package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/auth.controller.ts +0 -31
  330. package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/auth.module.ts +0 -22
  331. package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/auth.service.ts +0 -44
  332. package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/dto/login.dto.ts +0 -12
  333. package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/dto/register.dto.ts +0 -13
  334. package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/jwt-auth.guard.ts +0 -5
  335. package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/jwt.strategy.ts +0 -23
  336. package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/user.entity.ts +0 -16
  337. package/dist/templates/modules/auth-jwt/files/apps/api/src/migrations/1720200000000-InitUsers.ts +0 -23
  338. package/dist/templates/modules/auth-jwt/module.manifest.json +0 -31
  339. package/dist/templates/todo/apps/api/test/health.e2e-spec.ts +0 -23
  340. package/dist/templates/todo/apps/web/src/routes/api/health/+server.ts +0 -12
  341. package/dist/templates/todo/apps/web/src/routes/api/todos/+server.ts +0 -24
  342. package/dist/templates/todo/apps/web/src/routes/api/todos/[id]/+server.ts +0 -24
@@ -0,0 +1,31 @@
1
+ import type { MigrationInterface, QueryRunner } from "typeorm";
2
+
3
+ export class InitAuditLogs1720300000000 implements MigrationInterface {
4
+ name = "InitAuditLogs1720300000000";
5
+
6
+ async up(queryRunner: QueryRunner): Promise<void> {
7
+ await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS "pgcrypto"`);
8
+ await queryRunner.query(`
9
+ CREATE TABLE "audit_logs" (
10
+ "id" uuid NOT NULL DEFAULT gen_random_uuid(),
11
+ "action" character varying(128) NOT NULL,
12
+ "actorId" character varying,
13
+ "actorName" character varying,
14
+ "actorEmail" character varying,
15
+ "targetType" character varying,
16
+ "targetId" character varying,
17
+ "targetLabel" character varying,
18
+ "ip" character varying,
19
+ "metadata" jsonb,
20
+ "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
21
+ CONSTRAINT "PK_audit_logs_id" PRIMARY KEY ("id")
22
+ )
23
+ `);
24
+ await queryRunner.query(`CREATE INDEX "IDX_audit_logs_action" ON "audit_logs" ("action")`);
25
+ await queryRunner.query(`CREATE INDEX "IDX_audit_logs_createdAt" ON "audit_logs" ("createdAt")`);
26
+ }
27
+
28
+ async down(queryRunner: QueryRunner): Promise<void> {
29
+ await queryRunner.query(`DROP TABLE "audit_logs"`);
30
+ }
31
+ }
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "audit-log",
3
+ "description": "Audit logging: records who did each mutating request (who/method/path/status) to an audit_logs table — a NestJS interceptor for your own API routes plus a better-auth hook for auth/admin actions (login, ban, role changes, ...). Requires auth.",
4
+ "requires": [
5
+ "auth"
6
+ ],
7
+ "targetApp": "api",
8
+ "env": [
9
+ "# audit-log: surfaces the admin Audit log page in the dashboard",
10
+ "AUDIT_LOG_ENABLED=true"
11
+ ],
12
+ "inject": [
13
+ {
14
+ "file": "apps/api/src/app.module.ts",
15
+ "marker": "// podokit:imports",
16
+ "text": "import { AuditModule } from \"./audit/audit.module\";"
17
+ },
18
+ {
19
+ "file": "apps/api/src/app.module.ts",
20
+ "marker": "// podokit:module-imports",
21
+ "text": "AuditModule,"
22
+ },
23
+ {
24
+ "file": "apps/api/src/auth/auth.ts",
25
+ "marker": "// podokit:auth-imports",
26
+ "text": "import { auditAfterHook } from \"../audit/audit-hook\";"
27
+ },
28
+ {
29
+ "file": "apps/api/src/auth/auth.ts",
30
+ "marker": "// podokit:auth-options",
31
+ "text": "hooks: { after: auditAfterHook },"
32
+ }
33
+ ],
34
+ "instructions": [
35
+ "Run migrations to create the audit_logs table: npm run migration:run -w <app>-api",
36
+ "Auth/admin actions (login, ban, role changes, session revocation, user CRUD) are recorded via a better-auth hook; your own mutating NestJS routes are recorded by the interceptor.",
37
+ "List recent entries (admin session required): curl -b cookies.txt localhost:3000/audit-logs"
38
+ ]
39
+ }
@@ -0,0 +1,30 @@
1
+ import { Controller, Get } from "@nestjs/common";
2
+ import { Session, type UserSession } from "@thallesp/nestjs-better-auth";
3
+ import { ApiTags } from "@nestjs/swagger";
4
+
5
+ type Capabilities = { twoFactor: boolean; providers: string[]; deleteAccount: boolean; auditLog: boolean };
6
+
7
+ // Protected by the global AuthGuard. Use @Session() to read the current user.
8
+ @ApiTags("account")
9
+ @Controller("account")
10
+ export class AccountController {
11
+ @Get("me")
12
+ me(@Session() session: UserSession) {
13
+ return session.user;
14
+ }
15
+
16
+ // Which optional auth features are enabled, so the UI can show/hide sections.
17
+ @Get("capabilities")
18
+ capabilities(): Capabilities {
19
+ const providers = [
20
+ process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET ? "google" : null,
21
+ process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET ? "github" : null,
22
+ ].filter((p): p is string => p !== null);
23
+ return {
24
+ twoFactor: process.env.AUTH_TWO_FACTOR === "true",
25
+ providers,
26
+ deleteAccount: process.env.AUTH_ALLOW_DELETE === "true",
27
+ auditLog: process.env.AUDIT_LOG_ENABLED === "true",
28
+ };
29
+ }
30
+ }
@@ -0,0 +1,5 @@
1
+ import { Module } from "@nestjs/common";
2
+ import { AccountController } from "./account.controller";
3
+
4
+ @Module({ controllers: [AccountController] })
5
+ export class AccountModule {}
@@ -0,0 +1,59 @@
1
+ import { betterAuth, type BetterAuthOptions, type BetterAuthPlugin } from "better-auth";
2
+ import { twoFactor } from "better-auth/plugins";
3
+ import { Pool } from "pg";
4
+ // podokit:auth-imports
5
+
6
+ // OAuth providers are enabled only when their credentials are present.
7
+ function socialProviders(): NonNullable<BetterAuthOptions["socialProviders"]> {
8
+ const providers: NonNullable<BetterAuthOptions["socialProviders"]> = {};
9
+ if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) {
10
+ providers.google = {
11
+ clientId: process.env.GOOGLE_CLIENT_ID,
12
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET,
13
+ };
14
+ }
15
+ if (process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET) {
16
+ providers.github = {
17
+ clientId: process.env.GITHUB_CLIENT_ID,
18
+ clientSecret: process.env.GITHUB_CLIENT_SECRET,
19
+ };
20
+ }
21
+ return providers;
22
+ }
23
+
24
+ // Plugins are collected here so other PodoKit modules can add their own.
25
+ const plugins: BetterAuthPlugin[] = [];
26
+ if (process.env.AUTH_TWO_FACTOR === "true") {
27
+ plugins.push(twoFactor());
28
+ }
29
+ // podokit:auth-plugins
30
+
31
+ export const auth = betterAuth({
32
+ database: new Pool({
33
+ host: process.env.POSTGRES_HOST ?? "localhost",
34
+ port: Number(process.env.POSTGRES_PORT ?? 5432),
35
+ user: process.env.POSTGRES_USER ?? "podokit",
36
+ password: process.env.POSTGRES_PASSWORD ?? "podokit",
37
+ database: process.env.POSTGRES_DB ?? "podokit",
38
+ }),
39
+ emailAndPassword: {
40
+ enabled: true,
41
+ // podokit:auth-email-password
42
+ },
43
+ socialProviders: socialProviders(),
44
+ plugins,
45
+ secret: process.env.BETTER_AUTH_SECRET ?? "change-me-in-production-min-32-characters",
46
+ baseURL: process.env.BETTER_AUTH_URL ?? "http://localhost:3000",
47
+ advanced: {
48
+ ipAddress: {
49
+ // The API sits behind the SvelteKit server proxy, which forwards the
50
+ // resolved client IP as x-forwarded-for; trust it so sessions record an IP.
51
+ ipAddressHeaders: ["x-forwarded-for"],
52
+ },
53
+ },
54
+ user: {
55
+ // Self-service account deletion — opt in with AUTH_ALLOW_DELETE=true.
56
+ deleteUser: { enabled: process.env.AUTH_ALLOW_DELETE === "true" },
57
+ },
58
+ // podokit:auth-options
59
+ });
@@ -0,0 +1,26 @@
1
+ import { Controller, Get } from "@nestjs/common";
2
+ import { InjectDataSource } from "@nestjs/typeorm";
3
+ import { DataSource } from "typeorm";
4
+ import { Public } from "@thallesp/nestjs-better-auth";
5
+
6
+ // Health checks stay public under the global auth guard.
7
+ @Public()
8
+ @Controller("health")
9
+ export class HealthController {
10
+ constructor(@InjectDataSource() private readonly dataSource: DataSource) {}
11
+
12
+ @Get()
13
+ liveness(): { status: string; uptime: number; timestamp: string } {
14
+ return { status: "ok", uptime: process.uptime(), timestamp: new Date().toISOString() };
15
+ }
16
+
17
+ @Get("ready")
18
+ async readiness(): Promise<{ status: string; db: string }> {
19
+ try {
20
+ await this.dataSource.query("SELECT 1");
21
+ return { status: "ready", db: "up" };
22
+ } catch {
23
+ return { status: "degraded", db: "down" };
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "auth",
3
+ "description": "Full authentication (better-auth): email/password, sessions, and \u2014 via config \u2014 OAuth and 2FA. Installs a global guard so the API is secure by default; health and /api/auth stay public.",
4
+ "requires": [],
5
+ "targetApp": "api",
6
+ "dependencies": {
7
+ "better-auth": "^1.6.23",
8
+ "@thallesp/nestjs-better-auth": "^2.7.0",
9
+ "pg": "^8.12.0"
10
+ },
11
+ "devDependencies": {
12
+ "@types/pg": "^8.11.10"
13
+ },
14
+ "env": [
15
+ "# auth (better-auth) \u2014 set a strong secret (>= 32 chars) in production",
16
+ "BETTER_AUTH_SECRET=change-me-in-production-min-32-characters",
17
+ "BETTER_AUTH_URL=http://localhost:3000",
18
+ "# 2FA (TOTP): set true to enable the plugin",
19
+ "AUTH_TWO_FACTOR=false",
20
+ "# OAuth (optional) \u2014 set a provider's id+secret to enable it",
21
+ "# GOOGLE_CLIENT_ID=",
22
+ "# GOOGLE_CLIENT_SECRET=",
23
+ "# GITHUB_CLIENT_ID=",
24
+ "# GITHUB_CLIENT_SECRET="
25
+ ],
26
+ "inject": [
27
+ {
28
+ "file": "apps/api/src/app.module.ts",
29
+ "marker": "// podokit:imports",
30
+ "text": "import { APP_GUARD } from \"@nestjs/core\";"
31
+ },
32
+ {
33
+ "file": "apps/api/src/app.module.ts",
34
+ "marker": "// podokit:imports",
35
+ "text": "import { AuthModule, AuthGuard } from \"@thallesp/nestjs-better-auth\";"
36
+ },
37
+ {
38
+ "file": "apps/api/src/app.module.ts",
39
+ "marker": "// podokit:imports",
40
+ "text": "import { auth } from \"./auth/auth\";"
41
+ },
42
+ {
43
+ "file": "apps/api/src/app.module.ts",
44
+ "marker": "// podokit:imports",
45
+ "text": "import { AccountModule } from \"./account/account.module\";"
46
+ },
47
+ {
48
+ "file": "apps/api/src/app.module.ts",
49
+ "marker": "// podokit:module-imports",
50
+ "text": "AuthModule.forRoot(auth),"
51
+ },
52
+ {
53
+ "file": "apps/api/src/app.module.ts",
54
+ "marker": "// podokit:module-imports",
55
+ "text": "AccountModule,"
56
+ },
57
+ {
58
+ "file": "apps/api/src/app.module.ts",
59
+ "marker": "// podokit:providers",
60
+ "text": "{ provide: APP_GUARD, useClass: AuthGuard },"
61
+ }
62
+ ],
63
+ "instructions": [
64
+ "Set BETTER_AUTH_SECRET in .env (>= 32 chars).",
65
+ "Create the auth tables: npx @better-auth/cli migrate -y --config apps/api/src/auth/auth.ts",
66
+ "Sign up: curl -c cookies.txt -XPOST localhost:3000/api/auth/sign-up/email -H 'content-type: application/json' -d '{\"email\":\"a@example.com\",\"password\":\"password123\",\"name\":\"A\"}'",
67
+ "Call a protected route with the session: curl -b cookies.txt localhost:3000/account/me",
68
+ "Everything is now protected by default; mark public routes with @Public(). Enable OAuth by setting a provider's env vars; enable 2FA with AUTH_TWO_FACTOR=true."
69
+ ]
70
+ }
@@ -4,7 +4,7 @@
4
4
  "requires": [],
5
5
  "targetApp": "api",
6
6
  "dependencies": {
7
- "@nestjs/bullmq": "^10.2.0",
7
+ "@nestjs/bullmq": "^11.0.4",
8
8
  "bullmq": "^5.34.0"
9
9
  },
10
10
  "scripts": {
@@ -15,8 +15,16 @@
15
15
  "# bullmq uses REDIS_HOST / REDIS_PORT (already defined above)"
16
16
  ],
17
17
  "inject": [
18
- { "file": "apps/api/src/app.module.ts", "marker": "// podokit:imports", "text": "import { JobsModule } from \"./jobs/jobs.module\";" },
19
- { "file": "apps/api/src/app.module.ts", "marker": "// podokit:module-imports", "text": "JobsModule," }
18
+ {
19
+ "file": "apps/api/src/app.module.ts",
20
+ "marker": "// podokit:imports",
21
+ "text": "import { JobsModule } from \"./jobs/jobs.module\";"
22
+ },
23
+ {
24
+ "file": "apps/api/src/app.module.ts",
25
+ "marker": "// podokit:module-imports",
26
+ "text": "JobsModule,"
27
+ }
20
28
  ],
21
29
  "instructions": [
22
30
  "Ensure Redis is running: docker compose -f infra/docker/docker-compose.yml up -d",
@@ -0,0 +1,29 @@
1
+ import { Module } from "@nestjs/common";
2
+ import { randomUUID } from "node:crypto";
3
+ import type { IncomingMessage, ServerResponse } from "node:http";
4
+ import { LoggerModule } from "nestjs-pino";
5
+
6
+ const isProduction = process.env.NODE_ENV === "production";
7
+
8
+ // Structured request logging with a correlation id. Importing this module
9
+ // installs the pino-http middleware for all routes.
10
+ @Module({
11
+ imports: [
12
+ LoggerModule.forRoot({
13
+ pinoHttp: {
14
+ level: process.env.LOG_LEVEL ?? "info",
15
+ // Pretty, single-line logs in dev; JSON in production.
16
+ transport: isProduction ? undefined : { target: "pino-pretty", options: { singleLine: true } },
17
+ autoLogging: true,
18
+ // One id per request; honor an inbound x-request-id and echo it back.
19
+ genReqId: (req: IncomingMessage, res: ServerResponse): string => {
20
+ const header = req.headers["x-request-id"];
21
+ const id = (Array.isArray(header) ? header[0] : header) ?? randomUUID();
22
+ res.setHeader("x-request-id", id);
23
+ return id;
24
+ },
25
+ },
26
+ }),
27
+ ],
28
+ })
29
+ export class LoggingModule {}
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "logging",
3
+ "description": "Structured request logging (nestjs-pino) with a per-request correlation id (x-request-id). Pretty in dev, JSON in production.",
4
+ "requires": [],
5
+ "targetApp": "api",
6
+ "dependencies": {
7
+ "nestjs-pino": "^4.3.0",
8
+ "pino-http": "^10.3.0"
9
+ },
10
+ "devDependencies": {
11
+ "pino-pretty": "^13.0.0"
12
+ },
13
+ "env": [
14
+ "# logging",
15
+ "LOG_LEVEL=info"
16
+ ],
17
+ "inject": [
18
+ { "file": "apps/api/src/app.module.ts", "marker": "// podokit:imports", "text": "import { LoggingModule } from \"./logging/logging.module\";" },
19
+ { "file": "apps/api/src/app.module.ts", "marker": "// podokit:module-imports", "text": "LoggingModule," }
20
+ ],
21
+ "instructions": [
22
+ "Every HTTP request is now logged as structured JSON with a correlation id.",
23
+ "Make a request and watch the API logs: curl localhost:3000/health",
24
+ "Set LOG_LEVEL (debug|info|warn|error) in .env. Logs are pretty in dev and JSON when NODE_ENV=production.",
25
+ "To route Nest's own logs through pino too, add to main.ts: NestFactory.create(AppModule, { bufferLogs: true }) then app.useLogger(app.get(Logger))."
26
+ ]
27
+ }
@@ -0,0 +1,26 @@
1
+ import { Module } from "@nestjs/common";
2
+ import { APP_GUARD } from "@nestjs/core";
3
+ import { ThrottlerGuard, ThrottlerModule } from "@nestjs/throttler";
4
+ import { ThrottlerStorageRedisService } from "@nest-lab/throttler-storage-redis";
5
+ import Redis from "ioredis";
6
+
7
+ const ttlSeconds = Number(process.env.RATE_LIMIT_TTL ?? 60);
8
+ const limit = Number(process.env.RATE_LIMIT_MAX ?? 100);
9
+
10
+ // Distributed rate limiting: the counter lives in Redis, so the limit holds
11
+ // across API replicas. Registers a global guard (429 when exceeded).
12
+ @Module({
13
+ imports: [
14
+ ThrottlerModule.forRoot({
15
+ throttlers: [{ ttl: ttlSeconds * 1000, limit }],
16
+ storage: new ThrottlerStorageRedisService(
17
+ new Redis({
18
+ host: process.env.REDIS_HOST ?? "localhost",
19
+ port: Number(process.env.REDIS_PORT ?? 6379),
20
+ }),
21
+ ),
22
+ }),
23
+ ],
24
+ providers: [{ provide: APP_GUARD, useClass: ThrottlerGuard }],
25
+ })
26
+ export class RateLimitModule {}
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "rate-limit",
3
+ "description": "Rate limiting with @nestjs/throttler backed by Redis (correct across API replicas). Installs a global throttler guard; configurable via env.",
4
+ "requires": ["redis"],
5
+ "targetApp": "api",
6
+ "dependencies": {
7
+ "@nestjs/throttler": "^6.5.0",
8
+ "@nest-lab/throttler-storage-redis": "^1.2.0"
9
+ },
10
+ "env": [
11
+ "# rate limit (window in seconds, max requests per window per client)",
12
+ "RATE_LIMIT_TTL=60",
13
+ "RATE_LIMIT_MAX=100"
14
+ ],
15
+ "inject": [
16
+ { "file": "apps/api/src/app.module.ts", "marker": "// podokit:imports", "text": "import { RateLimitModule } from \"./rate-limit/rate-limit.module\";" },
17
+ { "file": "apps/api/src/app.module.ts", "marker": "// podokit:module-imports", "text": "RateLimitModule," }
18
+ ],
19
+ "instructions": [
20
+ "Ensure Redis is running: docker compose -f infra/docker/docker-compose.yml up -d",
21
+ "Tune RATE_LIMIT_TTL / RATE_LIMIT_MAX in .env.",
22
+ "Exceeding the limit returns HTTP 429. Skip a route with @SkipThrottle() or tune it with @Throttle().",
23
+ "The limit is shared across API replicas because it is stored in Redis."
24
+ ]
25
+ }
@@ -7,19 +7,19 @@
7
7
  "build": "nest build",
8
8
  "start": "node dist/main",
9
9
  "lint": "tsc -p tsconfig.json --noEmit",
10
- "test": "jest",
10
+ "test": "jest --passWithNoTests",
11
11
  "typeorm": "typeorm-ts-node-commonjs -d src/database/data-source.ts",
12
12
  "migration:run": "npm run typeorm -- migration:run",
13
13
  "migration:revert": "npm run typeorm -- migration:revert",
14
14
  "migration:generate": "npm run typeorm -- migration:generate"
15
15
  },
16
16
  "dependencies": {
17
- "@nestjs/common": "^10.4.0",
18
- "@nestjs/config": "^3.2.3",
19
- "@nestjs/core": "^10.4.0",
20
- "@nestjs/platform-express": "^10.4.0",
21
- "@nestjs/swagger": "^7.4.0",
22
- "@nestjs/typeorm": "^10.0.2",
17
+ "@nestjs/common": "^11.1.27",
18
+ "@nestjs/config": "^4.0.4",
19
+ "@nestjs/core": "^11.1.27",
20
+ "@nestjs/platform-express": "^11.1.27",
21
+ "@nestjs/swagger": "^11.4.5",
22
+ "@nestjs/typeorm": "^11.0.3",
23
23
  "class-transformer": "^0.5.1",
24
24
  "class-validator": "^0.14.1",
25
25
  "dotenv": "^16.4.5",
@@ -30,15 +30,33 @@
30
30
  "zod": "^3.23.8"
31
31
  },
32
32
  "devDependencies": {
33
- "@nestjs/cli": "^10.4.0",
34
- "@nestjs/testing": "^10.4.0",
35
- "@types/express": "^4.17.21",
33
+ "@nestjs/cli": "^11.0.23",
34
+ "@nestjs/testing": "^11.1.27",
35
+ "@types/express": "^5.0.1",
36
36
  "@types/node": "^20.17.0",
37
37
  "@types/supertest": "^6.0.2",
38
38
  "jest": "^29.7.0",
39
39
  "supertest": "^7.0.0",
40
40
  "ts-jest": "^29.2.5",
41
41
  "ts-node": "^10.9.2",
42
- "typescript": "^5.6.3"
42
+ "typescript": "^6.0.3"
43
+ },
44
+ "jest": {
45
+ "moduleFileExtensions": [
46
+ "js",
47
+ "json",
48
+ "ts"
49
+ ],
50
+ "rootDir": "src",
51
+ "testRegex": ".*\\.spec\\.ts$",
52
+ "transform": {
53
+ "^.+\\.ts$": [
54
+ "ts-jest",
55
+ {
56
+ "tsconfig": "tsconfig.json"
57
+ }
58
+ ]
59
+ },
60
+ "testEnvironment": "node"
43
61
  }
44
62
  }
@@ -15,5 +15,8 @@ import { TodosModule } from "./todos/todos.module";
15
15
  TodosModule,
16
16
  // podokit:module-imports
17
17
  ],
18
+ providers: [
19
+ // podokit:providers
20
+ ],
18
21
  })
19
22
  export class AppModule {}
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "module": "CommonJS",
3
+ "module": "nodenext",
4
4
  "target": "ES2022",
5
- "moduleResolution": "Node",
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": ["src/**/*.ts"],
20
- "exclude": ["node_modules", "dist", "test"]
22
+ "include": [
23
+ "src/**/*.ts"
24
+ ],
25
+ "exclude": [
26
+ "node_modules",
27
+ "dist",
28
+ "test"
29
+ ]
21
30
  }
@@ -8,16 +8,18 @@
8
8
  "build": "vite build",
9
9
  "preview": "vite preview",
10
10
  "lint": "svelte-check --tsconfig ./tsconfig.json",
11
- "test": "echo \"add Playwright tests here\" && exit 0"
11
+ "test": "echo \"unit: add vitest; e2e: npm run test:e2e (see tests/)\" && exit 0"
12
12
  },
13
13
  "dependencies": {
14
- "mode-watcher": "^0.5.0",
14
+ "@internationalized/date": "^3.12.2",
15
+ "@lucide/svelte": "^1.23.0",
16
+ "@podosoft/podokit-api-client": "^0.4.0",
15
17
  "bits-ui": "^2.18.1",
16
18
  "clsx": "^2.1.1",
19
+ "mode-watcher": "^1.1.0",
20
+ "svelte-sonner": "^1.1.1",
17
21
  "tailwind-merge": "^3.6.0",
18
- "tailwind-variants": "^3.2.2",
19
- "@lucide/svelte": "^1.23.0",
20
- "@internationalized/date": "^3.12.2"
22
+ "tailwind-variants": "^3.2.2"
21
23
  },
22
24
  "devDependencies": {
23
25
  "@sveltejs/adapter-node": "^5.2.0",
@@ -28,7 +30,7 @@
28
30
  "svelte-check": "^4.0.0",
29
31
  "tailwindcss": "^4.0.0",
30
32
  "typesafe-i18n": "^5.26.2",
31
- "typescript": "^5.6.3",
33
+ "typescript": "^6.0.3",
32
34
  "vite": "^5.4.0",
33
35
  "tw-animate-css": "^1.4.0"
34
36
  }
@@ -0,0 +1,5 @@
1
+ import { createApiClient } from "@podosoft/podokit-api-client";
2
+
3
+ // Browser client: same-origin. REST calls go to /api/* and auth to /api/auth/*,
4
+ // both proxied to the backend by the SvelteKit server (see routes/api/**).
5
+ export const api = createApiClient();
@@ -0,0 +1,20 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ import { cn, type WithElementRef } from "$lib/utils.js";
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ children,
9
+ ...restProps
10
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
11
+ </script>
12
+
13
+ <div
14
+ bind:this={ref}
15
+ data-slot="alert-action"
16
+ class={cn("absolute top-2 right-2", className)}
17
+ {...restProps}
18
+ >
19
+ {@render children?.()}
20
+ </div>
@@ -0,0 +1,23 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ import { cn, type WithElementRef } from "$lib/utils.js";
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ children,
9
+ ...restProps
10
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
11
+ </script>
12
+
13
+ <div
14
+ bind:this={ref}
15
+ data-slot="alert-description"
16
+ class={cn(
17
+ "text-muted-foreground text-sm text-balance md:text-pretty [&_p:not(:last-child)]:mb-4 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3",
18
+ className
19
+ )}
20
+ {...restProps}
21
+ >
22
+ {@render children?.()}
23
+ </div>
@@ -0,0 +1,23 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ import { cn, type WithElementRef } from "$lib/utils.js";
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ children,
9
+ ...restProps
10
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
11
+ </script>
12
+
13
+ <div
14
+ bind:this={ref}
15
+ data-slot="alert-title"
16
+ class={cn(
17
+ "font-medium group-has-[>svg]/alert:col-start-2 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3",
18
+ className
19
+ )}
20
+ {...restProps}
21
+ >
22
+ {@render children?.()}
23
+ </div>