create-nexwire 4.3.0 → 4.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 (326) hide show
  1. package/README.md +10 -5
  2. package/express/src/framework/facade.ts +1 -0
  3. package/express/src/framework/http/logger.ts +13 -4
  4. package/express/src/framework/maker-cli/runtime/core.mjs +1 -2
  5. package/express/src/framework/notification/channels/mail.ts +8 -6
  6. package/express/src/middlewares/auth-middleware.ts +1 -2
  7. package/express/src/middlewares/role-middleware.ts +3 -13
  8. package/express/src/modules/auth/routes/index.ts +1 -2
  9. package/express/src/providers/notification.ts +2 -2
  10. package/express/src/resources/src/components/Switch.vue +60 -65
  11. package/express/src/resources/src/pages/auth/forgetPassword.vue +23 -30
  12. package/express/src/resources/src/pages/auth/login.vue +25 -28
  13. package/express/src/resources/src/pages/auth/register.vue +35 -39
  14. package/express/src/resources/src/pages/auth/resetPassword.vue +29 -37
  15. package/express/src/resources/src/pages/auth/verifyEmail.vue +15 -19
  16. package/express/src/resources/src/plugins/gum.ts +519 -203
  17. package/express/src/resources/src/stores/auth.ts +9 -6
  18. package/express/src/storage/database/nexwire.sqlite +0 -0
  19. package/fastify/.dockerignore +14 -0
  20. package/fastify/.env +44 -0
  21. package/fastify/.env.example +42 -0
  22. package/fastify/.vscode/extensions.json +6 -0
  23. package/fastify/.vscode/settings.json +37 -0
  24. package/fastify/LICENSE +21 -0
  25. package/fastify/README.md +232 -0
  26. package/fastify/biome.json +99 -0
  27. package/fastify/drizzle.config.ts +25 -0
  28. package/fastify/gitignore-stub +31 -0
  29. package/fastify/package.json +130 -0
  30. package/fastify/public/.gitkeep +0 -0
  31. package/fastify/src/config/app.ts +41 -0
  32. package/fastify/src/config/auth.ts +12 -0
  33. package/fastify/src/config/cache.ts +16 -0
  34. package/fastify/src/config/cookie.ts +15 -0
  35. package/fastify/src/config/cors.ts +12 -0
  36. package/fastify/src/config/database.ts +48 -0
  37. package/fastify/src/config/index.ts +83 -0
  38. package/fastify/src/config/jwt.ts +19 -0
  39. package/fastify/src/config/logging.ts +13 -0
  40. package/fastify/src/config/mail.ts +24 -0
  41. package/fastify/src/config/openapi.ts +36 -0
  42. package/fastify/src/config/queue.ts +30 -0
  43. package/fastify/src/config/rateLimit.ts +17 -0
  44. package/fastify/src/config/realtime.ts +15 -0
  45. package/fastify/src/config/redis.ts +18 -0
  46. package/fastify/src/config/security.ts +44 -0
  47. package/fastify/src/config/session.ts +17 -0
  48. package/fastify/src/config/storage.ts +22 -0
  49. package/fastify/src/config/validate.ts +50 -0
  50. package/fastify/src/database/migrations/sqlite/0000_init.sql +48 -0
  51. package/fastify/src/database/migrations/sqlite/meta/0000_snapshot.json +324 -0
  52. package/fastify/src/database/migrations/sqlite/meta/_journal.json +13 -0
  53. package/fastify/src/database/schema.ts +2 -0
  54. package/fastify/src/env.ts +76 -0
  55. package/fastify/src/framework/cache/cache.ts +114 -0
  56. package/fastify/src/framework/circuit-breaker/cb.ts +88 -0
  57. package/fastify/src/framework/database/app-schema.ts +37 -0
  58. package/fastify/src/framework/database/connection.ts +223 -0
  59. package/fastify/src/framework/database/migrate-hooks.ts +87 -0
  60. package/fastify/src/framework/database/paginate.ts +226 -0
  61. package/fastify/src/framework/database/schema.ts +20 -0
  62. package/fastify/src/framework/database/seed.ts +152 -0
  63. package/fastify/src/framework/events/dispatcher.ts +61 -0
  64. package/fastify/src/framework/facade.ts +96 -0
  65. package/fastify/src/framework/http/app.ts +176 -0
  66. package/fastify/src/framework/http/cors.ts +13 -0
  67. package/fastify/src/framework/http/fastify.d.ts +13 -0
  68. package/fastify/src/framework/http/logger.ts +110 -0
  69. package/fastify/src/framework/http/metrics.ts +22 -0
  70. package/fastify/src/framework/http/openapi.ts +48 -0
  71. package/fastify/src/framework/http/ratelimiter.ts +47 -0
  72. package/fastify/src/framework/http/security.ts +25 -0
  73. package/fastify/src/framework/http/static.ts +83 -0
  74. package/fastify/src/framework/http/validation.ts +49 -0
  75. package/fastify/src/framework/kernel.ts +43 -0
  76. package/fastify/src/framework/maker-cli/db/core.mjs +291 -0
  77. package/fastify/src/framework/maker-cli/db/index.mjs +204 -0
  78. package/fastify/src/framework/maker-cli/deploy/core.mjs +1046 -0
  79. package/fastify/src/framework/maker-cli/deploy/index.mjs +91 -0
  80. package/fastify/src/framework/maker-cli/index.mjs +65 -0
  81. package/fastify/src/framework/maker-cli/middleware/core.mjs +41 -0
  82. package/fastify/src/framework/maker-cli/middleware/index.mjs +17 -0
  83. package/fastify/src/framework/maker-cli/module/core.mjs +1066 -0
  84. package/fastify/src/framework/maker-cli/module/index.mjs +211 -0
  85. package/fastify/src/framework/maker-cli/provider/core.mjs +49 -0
  86. package/fastify/src/framework/maker-cli/provider/index.mjs +12 -0
  87. package/fastify/src/framework/maker-cli/runtime/build-ui.mjs +16 -0
  88. package/fastify/src/framework/maker-cli/runtime/core.mjs +560 -0
  89. package/fastify/src/framework/maker-cli/runtime/index.mjs +120 -0
  90. package/fastify/src/framework/maker-cli/runtime/redis-commander-patch.cjs +20 -0
  91. package/fastify/src/framework/maker-cli/stubs/controller/openapi.ts.stub +74 -0
  92. package/fastify/src/framework/maker-cli/stubs/controller/plain.ts.stub +77 -0
  93. package/fastify/src/framework/maker-cli/stubs/controller/standalone.ts.stub +51 -0
  94. package/fastify/src/framework/maker-cli/stubs/deploy/Dockerfile.bun.stub +52 -0
  95. package/fastify/src/framework/maker-cli/stubs/deploy/Dockerfile.pnpm.stub +55 -0
  96. package/fastify/src/framework/maker-cli/stubs/deploy/Dockerfile.stub +51 -0
  97. package/fastify/src/framework/maker-cli/stubs/deploy/Dockerfile.yarn.stub +55 -0
  98. package/fastify/src/framework/maker-cli/stubs/deploy/README.stub +59 -0
  99. package/fastify/src/framework/maker-cli/stubs/deploy/compose/mysql.server.stub +45 -0
  100. package/fastify/src/framework/maker-cli/stubs/deploy/compose/postgres.server.stub +45 -0
  101. package/fastify/src/framework/maker-cli/stubs/deploy/compose/sqlite.stub +45 -0
  102. package/fastify/src/framework/maker-cli/stubs/deploy/env/mysql.server.stub +68 -0
  103. package/fastify/src/framework/maker-cli/stubs/deploy/env/postgres.server.stub +68 -0
  104. package/fastify/src/framework/maker-cli/stubs/deploy/env/sqlite.stub +67 -0
  105. package/fastify/src/framework/maker-cli/stubs/deploy/scripts/auto-migrate.sh.stub +41 -0
  106. package/fastify/src/framework/maker-cli/stubs/deploy/server/README.stub +78 -0
  107. package/fastify/src/framework/maker-cli/stubs/deploy/server/compose/noredis.stub +120 -0
  108. package/fastify/src/framework/maker-cli/stubs/deploy/server/compose/redis.dev.stub +133 -0
  109. package/fastify/src/framework/maker-cli/stubs/deploy/server/compose/redis.stub +131 -0
  110. package/fastify/src/framework/maker-cli/stubs/deploy/server/env/local.example.stub +11 -0
  111. package/fastify/src/framework/maker-cli/stubs/deploy/server/env/noredis.stub +25 -0
  112. package/fastify/src/framework/maker-cli/stubs/deploy/server/env/redis.stub +25 -0
  113. package/fastify/src/framework/maker-cli/stubs/deploy/server/nginx-vhost/README.stub +16 -0
  114. package/fastify/src/framework/maker-cli/stubs/deploy/server/nginx-vhost/app.example.com.stub +13 -0
  115. package/fastify/src/framework/maker-cli/stubs/deploy/server/pgadmin/servers.stub +14 -0
  116. package/fastify/src/framework/maker-cli/stubs/deploy/server/redis/redis.conf.stub +7 -0
  117. package/fastify/src/framework/maker-cli/stubs/deploy/supervisor/noredis.stub +54 -0
  118. package/fastify/src/framework/maker-cli/stubs/deploy/supervisor/redis.stub +70 -0
  119. package/fastify/src/framework/maker-cli/stubs/deploy/workflow/local.json.stub +25 -0
  120. package/fastify/src/framework/maker-cli/stubs/deploy/workflow/remote.json.stub +24 -0
  121. package/fastify/src/framework/maker-cli/stubs/facade/name.ts.stub +12 -0
  122. package/fastify/src/framework/maker-cli/stubs/helper/name.ts.stub +14 -0
  123. package/fastify/src/framework/maker-cli/stubs/job/name.ts.stub +25 -0
  124. package/fastify/src/framework/maker-cli/stubs/middleware/name.local.ts.stub +15 -0
  125. package/fastify/src/framework/maker-cli/stubs/middleware/name.ts.stub +15 -0
  126. package/fastify/src/framework/maker-cli/stubs/model/name.mysql.ts.stub +10 -0
  127. package/fastify/src/framework/maker-cli/stubs/model/name.postgresql.ts.stub +10 -0
  128. package/fastify/src/framework/maker-cli/stubs/model/name.sqlite.ts.stub +10 -0
  129. package/fastify/src/framework/maker-cli/stubs/notification/controller.ts.stub +79 -0
  130. package/fastify/src/framework/maker-cli/stubs/notification/job.ts.stub +11 -0
  131. package/fastify/src/framework/maker-cli/stubs/notification/model.ts.stub +29 -0
  132. package/fastify/src/framework/maker-cli/stubs/notification/provider.ts.stub +174 -0
  133. package/fastify/src/framework/maker-cli/stubs/notification/route.api.ts.stub +71 -0
  134. package/fastify/src/framework/maker-cli/stubs/notification/route.plain.ts.stub +15 -0
  135. package/fastify/src/framework/maker-cli/stubs/notification/schema.plain.ts.stub +26 -0
  136. package/fastify/src/framework/maker-cli/stubs/notification/schema.ts.stub +26 -0
  137. package/fastify/src/framework/maker-cli/stubs/provider/module-barrel.ts.stub +19 -0
  138. package/fastify/src/framework/maker-cli/stubs/provider/module.ts.stub +21 -0
  139. package/fastify/src/framework/maker-cli/stubs/provider/name.ts.stub +21 -0
  140. package/fastify/src/framework/maker-cli/stubs/route/api.ts.stub +101 -0
  141. package/fastify/src/framework/maker-cli/stubs/route/plain.ts.stub +23 -0
  142. package/fastify/src/framework/maker-cli/stubs/route/standalone.api.ts.stub +120 -0
  143. package/fastify/src/framework/maker-cli/stubs/route/standalone.plain.ts.stub +50 -0
  144. package/fastify/src/framework/maker-cli/stubs/schedule/name.ts.stub +36 -0
  145. package/fastify/src/framework/maker-cli/stubs/schema/name.plain.ts.stub +14 -0
  146. package/fastify/src/framework/maker-cli/stubs/schema/name.ts.stub +33 -0
  147. package/fastify/src/framework/maker-cli/stubs/seeder/name.ts.stub +18 -0
  148. package/fastify/src/framework/maker-cli/stubs/seeder/standalone.ts.stub +22 -0
  149. package/fastify/src/framework/maker-cli/stubs/service/name.plain.ts.stub +67 -0
  150. package/fastify/src/framework/maker-cli/stubs/service/name.ts.stub +65 -0
  151. package/fastify/src/framework/maker-cli/stubs/test/unit.ts.stub +22 -0
  152. package/fastify/src/framework/maker-cli/stubs/type/name.ts.stub +22 -0
  153. package/fastify/src/framework/maker-cli/utils/env-config.mjs +44 -0
  154. package/fastify/src/framework/maker-cli/utils/env-db.mjs +61 -0
  155. package/fastify/src/framework/maker-cli/utils/file-ops.mjs +30 -0
  156. package/fastify/src/framework/maker-cli/utils/flags.mjs +14 -0
  157. package/fastify/src/framework/maker-cli/utils/help.mjs +379 -0
  158. package/fastify/src/framework/maker-cli/utils/naming.mjs +119 -0
  159. package/fastify/src/framework/maker-cli/utils/process.mjs +52 -0
  160. package/fastify/src/framework/maker-cli/utils/project-commands.mjs +201 -0
  161. package/fastify/src/framework/modules/discover.ts +57 -0
  162. package/fastify/src/framework/modules/routes.ts +31 -0
  163. package/fastify/src/framework/notification/channels/broadcast.ts +15 -0
  164. package/fastify/src/framework/notification/channels/mail.ts +42 -0
  165. package/fastify/src/framework/notification/contracts.ts +29 -0
  166. package/fastify/src/framework/notification/index.ts +59 -0
  167. package/fastify/src/framework/notification/registry.ts +15 -0
  168. package/fastify/src/framework/providers/adapter.ts +149 -0
  169. package/fastify/src/framework/providers/contracts.ts +126 -0
  170. package/fastify/src/framework/providers/loader.ts +105 -0
  171. package/fastify/src/framework/providers/registry.ts +88 -0
  172. package/fastify/src/framework/queue/clear.ts +22 -0
  173. package/fastify/src/framework/queue/queue.ts +401 -0
  174. package/fastify/src/framework/queue/ui.ts +194 -0
  175. package/fastify/src/framework/queue/worker.ts +61 -0
  176. package/fastify/src/framework/realtime/broadcast.ts +70 -0
  177. package/fastify/src/framework/realtime/index.ts +2 -0
  178. package/fastify/src/framework/realtime/socket-cookie.ts +72 -0
  179. package/fastify/src/framework/realtime/socket.ts +149 -0
  180. package/fastify/src/framework/realtime/types.ts +6 -0
  181. package/fastify/src/framework/realtime/ui.ts +16 -0
  182. package/fastify/src/framework/redis/client.ts +148 -0
  183. package/fastify/src/framework/runtime/runtime.ts +23 -0
  184. package/fastify/src/framework/runtime/server-adapter.ts +33 -0
  185. package/fastify/src/framework/scheduler/lock.ts +128 -0
  186. package/fastify/src/framework/scheduler/run.ts +48 -0
  187. package/fastify/src/framework/scheduler/scheduler.ts +155 -0
  188. package/fastify/src/framework/server.ts +162 -0
  189. package/fastify/src/framework/session/session.ts +160 -0
  190. package/fastify/src/framework/storage/storage.ts +744 -0
  191. package/fastify/src/framework/support/cookie.ts +71 -0
  192. package/fastify/src/framework/support/jwt.ts +33 -0
  193. package/fastify/src/framework/support/lifecycle.ts +35 -0
  194. package/fastify/src/framework/support/logger.ts +148 -0
  195. package/fastify/src/framework/support/mail.ts +56 -0
  196. package/fastify/src/framework/support/password.ts +23 -0
  197. package/fastify/src/framework/support/status-codes.ts +75 -0
  198. package/fastify/src/framework/support/upload.ts +181 -0
  199. package/fastify/src/framework/support/url.ts +41 -0
  200. package/fastify/src/middlewares/auth-middleware.ts +134 -0
  201. package/fastify/src/middlewares/role-middleware.ts +19 -0
  202. package/fastify/src/modules/auth/__tests__/forgetpass.test.ts +200 -0
  203. package/fastify/src/modules/auth/__tests__/login.test.ts +171 -0
  204. package/fastify/src/modules/auth/__tests__/logout.test.ts +60 -0
  205. package/fastify/src/modules/auth/__tests__/logoutalldevices.test.ts +49 -0
  206. package/fastify/src/modules/auth/__tests__/me.test.ts +91 -0
  207. package/fastify/src/modules/auth/__tests__/refreshtoken.test.ts +305 -0
  208. package/fastify/src/modules/auth/__tests__/register.test.ts +291 -0
  209. package/fastify/src/modules/auth/__tests__/resetpass.test.ts +206 -0
  210. package/fastify/src/modules/auth/__tests__/verifyemail.test.ts +183 -0
  211. package/fastify/src/modules/auth/controllers/auth.ts +252 -0
  212. package/fastify/src/modules/auth/controllers/role.ts +35 -0
  213. package/fastify/src/modules/auth/database/models/role.ts +14 -0
  214. package/fastify/src/modules/auth/database/models/user.ts +46 -0
  215. package/fastify/src/modules/auth/database/seeders/role.ts +18 -0
  216. package/fastify/src/modules/auth/database/seeders/user.ts +35 -0
  217. package/fastify/src/modules/auth/facade.ts +12 -0
  218. package/fastify/src/modules/auth/helpers/auth.ts +115 -0
  219. package/fastify/src/modules/auth/jobs/forgetpass.ts +20 -0
  220. package/fastify/src/modules/auth/jobs/registeruser.ts +23 -0
  221. package/fastify/src/modules/auth/jobs/verifyemail.ts +18 -0
  222. package/fastify/src/modules/auth/routes/index.ts +185 -0
  223. package/fastify/src/modules/auth/routes/role.ts +56 -0
  224. package/fastify/src/modules/auth/schemas/auth.ts +116 -0
  225. package/fastify/src/modules/auth/services/forgetpassword.ts +66 -0
  226. package/fastify/src/modules/auth/services/login.ts +63 -0
  227. package/fastify/src/modules/auth/services/logout.ts +25 -0
  228. package/fastify/src/modules/auth/services/logoutalldevices.ts +19 -0
  229. package/fastify/src/modules/auth/services/me.ts +31 -0
  230. package/fastify/src/modules/auth/services/refreshtoken.ts +110 -0
  231. package/fastify/src/modules/auth/services/register.ts +141 -0
  232. package/fastify/src/modules/auth/services/resetpassword.ts +69 -0
  233. package/fastify/src/modules/auth/services/verifyemail.ts +77 -0
  234. package/fastify/src/modules/auth/types/auth.ts +31 -0
  235. package/fastify/src/providers/README.md +107 -0
  236. package/fastify/src/providers/notification.ts +4 -0
  237. package/fastify/src/resources/index.html +17 -0
  238. package/fastify/src/resources/src/App.vue +5 -0
  239. package/fastify/src/resources/src/assets/css/styles.css +14761 -0
  240. package/fastify/src/resources/src/assets/css/styles.css.map +1 -0
  241. package/fastify/src/resources/src/assets/fonts/public-sans/public-sans-latin-ext.woff2 +0 -0
  242. package/fastify/src/resources/src/assets/fonts/public-sans/public-sans-latin.woff2 +0 -0
  243. package/fastify/src/resources/src/assets/images/favicon/favicon.ico +0 -0
  244. package/fastify/src/resources/src/assets/images/logo.png +0 -0
  245. package/fastify/src/resources/src/assets/scss/custom.css +217 -0
  246. package/fastify/src/resources/src/assets/scss/custom.css.map +1 -0
  247. package/fastify/src/resources/src/assets/scss/custom.scss +1346 -0
  248. package/fastify/src/resources/src/components/Button.vue +32 -0
  249. package/fastify/src/resources/src/components/Checkbox.vue +32 -0
  250. package/fastify/src/resources/src/components/Datepicker.vue +106 -0
  251. package/fastify/src/resources/src/components/FeatureButton.vue +53 -0
  252. package/fastify/src/resources/src/components/FloatButton.vue +33 -0
  253. package/fastify/src/resources/src/components/Href.vue +32 -0
  254. package/fastify/src/resources/src/components/Input.vue +243 -0
  255. package/fastify/src/resources/src/components/InputGroup.vue +147 -0
  256. package/fastify/src/resources/src/components/InputPasswordToggle.vue +229 -0
  257. package/fastify/src/resources/src/components/Modal.vue +71 -0
  258. package/fastify/src/resources/src/components/Pagebar.vue +26 -0
  259. package/fastify/src/resources/src/components/Refresh.vue +19 -0
  260. package/fastify/src/resources/src/components/Select.vue +384 -0
  261. package/fastify/src/resources/src/components/Spinner.vue +42 -0
  262. package/fastify/src/resources/src/components/Switch.vue +60 -0
  263. package/fastify/src/resources/src/components/TextArea.vue +117 -0
  264. package/fastify/src/resources/src/components/Toast.vue +56 -0
  265. package/fastify/src/resources/src/components/datatable/DataTableSkeleton.vue +98 -0
  266. package/fastify/src/resources/src/components/datatable/Pagination.vue +147 -0
  267. package/fastify/src/resources/src/components/datatable/SelectOpption.vue +64 -0
  268. package/fastify/src/resources/src/components/datatable/index.vue +199 -0
  269. package/fastify/src/resources/src/composables/useAuth.ts +52 -0
  270. package/fastify/src/resources/src/helpers/assets.ts +51 -0
  271. package/fastify/src/resources/src/helpers/nformatter.ts +10 -0
  272. package/fastify/src/resources/src/helpers/utils.ts +75 -0
  273. package/fastify/src/resources/src/layouts/AuthLayout.vue +20 -0
  274. package/fastify/src/resources/src/layouts/Layout/Footer.vue +23 -0
  275. package/fastify/src/resources/src/layouts/Layout/Header.vue +124 -0
  276. package/fastify/src/resources/src/layouts/Layout/Sidebar.vue +137 -0
  277. package/fastify/src/resources/src/layouts/Layout/index.vue +87 -0
  278. package/fastify/src/resources/src/main.ts +28 -0
  279. package/fastify/src/resources/src/pages/auth/forgetPassword.vue +76 -0
  280. package/fastify/src/resources/src/pages/auth/login.vue +96 -0
  281. package/fastify/src/resources/src/pages/auth/register.vue +129 -0
  282. package/fastify/src/resources/src/pages/auth/resetPassword.vue +110 -0
  283. package/fastify/src/resources/src/pages/auth/verifyEmail.vue +64 -0
  284. package/fastify/src/resources/src/pages/dashboard/index.vue +66 -0
  285. package/fastify/src/resources/src/plugins/axios.ts +31 -0
  286. package/fastify/src/resources/src/plugins/browserDetect.ts +55 -0
  287. package/fastify/src/resources/src/plugins/dialog.ts +216 -0
  288. package/fastify/src/resources/src/plugins/gum.ts +683 -0
  289. package/fastify/src/resources/src/plugins/pulse.ts +157 -0
  290. package/fastify/src/resources/src/plugins/routeProgress.ts +87 -0
  291. package/fastify/src/resources/src/router/index.ts +85 -0
  292. package/fastify/src/resources/src/stores/admin-ui.ts +148 -0
  293. package/fastify/src/resources/src/stores/auth.ts +187 -0
  294. package/fastify/src/resources/tsconfig.json +19 -0
  295. package/fastify/src/resources/vite.config.ts +64 -0
  296. package/fastify/src/storage/app/private/.gitkeep +0 -0
  297. package/fastify/src/storage/app/public/.gitkeep +0 -0
  298. package/fastify/src/storage/app/tmp/generated/.gitkeep +0 -0
  299. package/fastify/src/storage/database/nexwire.sqlite +0 -0
  300. package/fastify/src/storage/logs/.gitkeep +0 -0
  301. package/fastify/src/storage/logs/app.log +0 -0
  302. package/fastify/src/storage/logs/fatal.log +0 -0
  303. package/fastify/src/storage/tmp/.gitkeep +0 -0
  304. package/fastify/src/storage/trash/modules/.gitkeep +0 -0
  305. package/fastify/src/types/global.d.ts +32 -0
  306. package/fastify/tsconfig.json +19 -0
  307. package/fastify/vitest.config.ts +38 -0
  308. package/hono/src/framework/facade.ts +1 -0
  309. package/hono/src/framework/http/logger.ts +86 -81
  310. package/hono/src/framework/maker-cli/runtime/core.mjs +1 -2
  311. package/hono/src/framework/notification/channels/mail.ts +8 -6
  312. package/hono/src/middlewares/auth-middleware.ts +1 -2
  313. package/hono/src/middlewares/role-middleware.ts +3 -13
  314. package/hono/src/modules/auth/routes/index.ts +1 -2
  315. package/hono/src/providers/notification.ts +2 -2
  316. package/hono/src/resources/src/components/Switch.vue +60 -65
  317. package/hono/src/resources/src/pages/auth/forgetPassword.vue +23 -29
  318. package/hono/src/resources/src/pages/auth/login.vue +26 -29
  319. package/hono/src/resources/src/pages/auth/register.vue +36 -40
  320. package/hono/src/resources/src/pages/auth/resetPassword.vue +29 -37
  321. package/hono/src/resources/src/pages/auth/verifyEmail.vue +15 -19
  322. package/hono/src/resources/src/plugins/gum.ts +519 -203
  323. package/hono/src/resources/src/stores/auth.ts +9 -6
  324. package/hono/src/storage/database/nexwire.sqlite +0 -0
  325. package/package.json +3 -2
  326. package/src/index.js +11 -8
@@ -0,0 +1,66 @@
1
+ import { and, eq, lt } from "drizzle-orm";
2
+ import { db, dispatchEvent, urls } from "@/framework/facade.js";
3
+ import { passwordResetTokens, users } from "@/modules/auth/database/models/user.js";
4
+ import { hashResetToken, makeResetToken } from "@/modules/auth/helpers/auth.js";
5
+ import type { ForgotPasswordInput } from "@/modules/auth/types/auth.js";
6
+
7
+ export type ForgotPasswordResult = {
8
+ kind: "sent";
9
+ message: string;
10
+ };
11
+
12
+ const TOKEN_TTL_MS = 15 * 60 * 1000;
13
+
14
+ export const forgotPasswordService = {
15
+ forgotPassword: async (body: ForgotPasswordInput): Promise<ForgotPasswordResult> => {
16
+ const now = new Date();
17
+
18
+ // Cleanup + replace happen atomically so a crash mid-flow can't leave
19
+ // the user without a valid token.
20
+ const issued = await db.transaction(async (tx) => {
21
+ // 1. Drop this email's expired tokens (housekeeping, not critical)
22
+ await tx.delete(passwordResetTokens).where(and(eq(passwordResetTokens.email, body.email), lt(passwordResetTokens.expiresAt, now)));
23
+
24
+ const user = await tx.query.users.findFirst({
25
+ where: eq(users.email, body.email)
26
+ });
27
+
28
+ if (!user) return null;
29
+
30
+ // 2. Revoke any existing unexpired token for this email
31
+ await tx.delete(passwordResetTokens).where(eq(passwordResetTokens.email, body.email));
32
+
33
+ // 3. Issue a fresh one
34
+ const plainToken = makeResetToken();
35
+ const expiresAt = new Date(now.getTime() + TOKEN_TTL_MS);
36
+
37
+ await tx.insert(passwordResetTokens).values({
38
+ email: user.email,
39
+ token: hashResetToken(plainToken),
40
+ expiresAt,
41
+ createdAt: now
42
+ });
43
+
44
+ return { user, plainToken };
45
+ });
46
+
47
+ // Same response whether or not the user exists (no enumeration).
48
+ if (!issued) {
49
+ return { kind: "sent", message: "Reset link has been sent" };
50
+ }
51
+
52
+ const resetUrl = urls.url(`/reset-password?token=${issued.plainToken}&email=${encodeURIComponent(issued.user.email)}`);
53
+
54
+ await dispatchEvent(
55
+ "user:forget-password",
56
+ {
57
+ email: issued.user.email,
58
+ name: issued.user.name,
59
+ resetUrl
60
+ },
61
+ { queue: "mail" }
62
+ );
63
+
64
+ return { kind: "sent", message: "Reset link has been sent" };
65
+ }
66
+ };
@@ -0,0 +1,63 @@
1
+ import { eq } from "drizzle-orm";
2
+ import type { FastifyReply, FastifyRequest } from "fastify";
3
+ import { authConfig } from "@/config/index.js";
4
+ import { db, password } from "@/framework/facade.js";
5
+ import { users } from "@/modules/auth/database/models/user.js";
6
+ import { issueTokens, revokeCurrentRefreshToken, sanitizeUser } from "@/modules/auth/helpers/auth.js";
7
+ import type { LoginInput } from "@/modules/auth/types/auth.js";
8
+
9
+ export type LoginResult =
10
+ | { kind: "invalid_credentials"; message: string }
11
+ | { kind: "email_not_verified"; message: string }
12
+ | {
13
+ kind: "logged_in";
14
+ message: string;
15
+ data: {
16
+ user: ReturnType<typeof sanitizeUser>;
17
+ access_token: string;
18
+ refresh_token: string;
19
+ token_type: "Bearer";
20
+ };
21
+ };
22
+
23
+ export const loginService = {
24
+ login: async (req: FastifyRequest, reply: FastifyReply, body: LoginInput): Promise<LoginResult> => {
25
+ const user = await db.query.users.findFirst({
26
+ where: eq(users.email, body.email),
27
+ with: { role: true }
28
+ });
29
+
30
+ // Generic message for both "user missing" and "wrong password" —
31
+ // never tell an attacker which one it was.
32
+ const passwordOk = user && (await password.verifyPassword(body.password, user.password));
33
+
34
+ if (!user || !passwordOk) {
35
+ return { kind: "invalid_credentials", message: "Invalid credentials" };
36
+ }
37
+
38
+ if (authConfig.requireEmailVerification && !user.emailVerifiedAt) {
39
+ return {
40
+ kind: "email_not_verified",
41
+ message: "Please verify your email before logging in"
42
+ };
43
+ }
44
+
45
+ // Rotate the current refresh token before issuing a new pair.
46
+ await revokeCurrentRefreshToken(req);
47
+
48
+ const tokens = await issueTokens(req, reply, user, {
49
+ remember: !!body.remember
50
+ });
51
+
52
+ return {
53
+ kind: "logged_in",
54
+ message: "User logged in successfully",
55
+ data: {
56
+ user: sanitizeUser(user),
57
+ access_token: tokens.accessToken,
58
+ refresh_token: tokens.refreshToken,
59
+ token_type: "Bearer"
60
+ }
61
+ };
62
+ }
63
+ };
@@ -0,0 +1,25 @@
1
+ import type { FastifyReply, FastifyRequest } from "fastify";
2
+ import { cookie, logger } from "@/framework/facade.js";
3
+ import { revokeCurrentRefreshToken } from "@/modules/auth/helpers/auth.js";
4
+
5
+ export type LogoutResult = {
6
+ kind: "logged_out";
7
+ message: string;
8
+ };
9
+
10
+ export const logoutService = {
11
+ logout: async (req: FastifyRequest, reply: FastifyReply): Promise<LogoutResult> => {
12
+ try {
13
+ await revokeCurrentRefreshToken(req);
14
+ } catch (err) {
15
+ // Log and continue — the user is logging out either way.
16
+ // The server-side token will expire on its own.
17
+ logger.error("failed to revoke refresh token on logout", { err });
18
+ }
19
+
20
+ cookie.deleteAuth(reply);
21
+ cookie.deleteRefresh(reply);
22
+
23
+ return { kind: "logged_out", message: "Logged out successfully" };
24
+ }
25
+ };
@@ -0,0 +1,19 @@
1
+ import { eq } from "drizzle-orm";
2
+ import { db } from "@/framework/facade.js";
3
+ import { refreshTokens } from "@/modules/auth/database/models/user.js";
4
+
5
+ export type LogoutAllDevicesResult = {
6
+ kind: "logged_out";
7
+ message: string;
8
+ };
9
+
10
+ export const logoutAllDevicesService = {
11
+ logoutAllDevices: async (userId: number): Promise<LogoutAllDevicesResult> => {
12
+ await db.delete(refreshTokens).where(eq(refreshTokens.userId, userId));
13
+
14
+ return {
15
+ kind: "logged_out",
16
+ message: "Logged out from all devices successfully"
17
+ };
18
+ }
19
+ };
@@ -0,0 +1,31 @@
1
+ import { eq } from "drizzle-orm";
2
+ import { db } from "@/framework/facade.js";
3
+ import { users } from "@/modules/auth/database/models/user.js";
4
+ import { sanitizeUser } from "@/modules/auth/helpers/auth.js";
5
+
6
+ export type MeResult =
7
+ | { kind: "not_found"; message: string }
8
+ | {
9
+ kind: "found";
10
+ message: string;
11
+ data: ReturnType<typeof sanitizeUser>;
12
+ };
13
+
14
+ export const meService = {
15
+ me: async (userId: number): Promise<MeResult> => {
16
+ const user = await db.query.users.findFirst({
17
+ where: eq(users.id, userId),
18
+ with: { role: true }
19
+ });
20
+
21
+ if (!user) {
22
+ return { kind: "not_found", message: "User not found" };
23
+ }
24
+
25
+ return {
26
+ kind: "found",
27
+ message: "Authenticated user fetched successfully",
28
+ data: sanitizeUser(user)
29
+ };
30
+ }
31
+ };
@@ -0,0 +1,110 @@
1
+ import { and, eq } from "drizzle-orm";
2
+ import { jwtConfig } from "@/config/index.js";
3
+ import { db, jwt } from "@/framework/facade.js";
4
+ import { refreshTokens, users } from "@/modules/auth/database/models/user.js";
5
+ import { sanitizeUser } from "@/modules/auth/helpers/auth.js";
6
+ import type { RefreshTokenInput } from "@/modules/auth/types/auth.js";
7
+
8
+ export type RefreshTokenResult =
9
+ | { kind: "invalid_token"; message: string }
10
+ | { kind: "revoked"; message: string }
11
+ | { kind: "expired"; message: string }
12
+ | { kind: "user_not_found"; message: string }
13
+ | {
14
+ kind: "refreshed";
15
+ message: string;
16
+ data: {
17
+ user: ReturnType<typeof sanitizeUser>;
18
+ access_token: string;
19
+ refresh_token: string;
20
+ token_type: "Bearer";
21
+ };
22
+ cookies: {
23
+ accessToken: string;
24
+ refreshToken: string;
25
+ refreshExpiry?: number;
26
+ };
27
+ };
28
+
29
+ export const refreshTokenService = {
30
+ refreshToken: async (body: RefreshTokenInput): Promise<RefreshTokenResult> => {
31
+ const payload = await jwt.verifyToken(body.refresh_token, "refresh");
32
+
33
+ if (!payload?.jti || typeof payload.jti !== "string") {
34
+ return { kind: "invalid_token", message: "Invalid refresh token" };
35
+ }
36
+
37
+ const userId = Number(payload.id);
38
+ if (!Number.isInteger(userId) || userId <= 0) {
39
+ return { kind: "invalid_token", message: "Invalid refresh token" };
40
+ }
41
+
42
+ const storedToken = await db.query.refreshTokens.findFirst({
43
+ where: eq(refreshTokens.jti, payload.jti)
44
+ });
45
+
46
+ if (!storedToken || storedToken.revoked === 1) {
47
+ return { kind: "revoked", message: "Refresh token revoked" };
48
+ }
49
+
50
+ if (storedToken.expiresAt.getTime() < Date.now()) {
51
+ await db.delete(refreshTokens).where(eq(refreshTokens.id, storedToken.id));
52
+ return { kind: "expired", message: "Refresh token expired" };
53
+ }
54
+
55
+ const user = await db.query.users.findFirst({
56
+ where: eq(users.id, userId),
57
+ with: { role: true }
58
+ });
59
+
60
+ if (!user) {
61
+ return { kind: "user_not_found", message: "User not found" };
62
+ }
63
+
64
+ const remember = !!payload.remember;
65
+ const refreshExpiry = remember ? jwtConfig.refreshRememberExpirySeconds : undefined;
66
+
67
+ const tokenPayload = {
68
+ id: user.id,
69
+ email: user.email,
70
+ roleId: user.role?.id,
71
+ role: user.role?.name,
72
+ remember
73
+ };
74
+
75
+ const accessToken = await jwt.generateToken(tokenPayload, "access");
76
+ const newRefreshToken = await jwt.generateToken(tokenPayload, "refresh", refreshExpiry);
77
+
78
+ // Compare-and-swap: only rotate if the jti is still the one we read.
79
+ // Two concurrent requests with the same old token → exactly one wins.
80
+ const [rotated] = await db
81
+ .update(refreshTokens)
82
+ .set({
83
+ jti: newRefreshToken.jti,
84
+ expiresAt: new Date(newRefreshToken.exp * 1000),
85
+ revoked: 0
86
+ })
87
+ .where(and(eq(refreshTokens.id, storedToken.id), eq(refreshTokens.jti, payload.jti)))
88
+ .returning();
89
+
90
+ if (!rotated) {
91
+ return { kind: "revoked", message: "Refresh token revoked" };
92
+ }
93
+
94
+ return {
95
+ kind: "refreshed",
96
+ message: "Token refreshed successfully",
97
+ data: {
98
+ user: sanitizeUser(user),
99
+ access_token: accessToken.token,
100
+ refresh_token: newRefreshToken.token,
101
+ token_type: "Bearer"
102
+ },
103
+ cookies: {
104
+ accessToken: accessToken.token,
105
+ refreshToken: newRefreshToken.token,
106
+ refreshExpiry
107
+ }
108
+ };
109
+ }
110
+ };
@@ -0,0 +1,141 @@
1
+ import { eq } from "drizzle-orm";
2
+ import type { FastifyReply, FastifyRequest } from "fastify";
3
+ import { authConfig } from "@/config/index.js";
4
+ import { db, dispatchEvent, password, urls } from "@/framework/facade.js";
5
+ import { roles } from "@/modules/auth/database/models/role.js";
6
+ import { emailVerificationTokens, users } from "@/modules/auth/database/models/user.js";
7
+ import {
8
+ hashEmailVerificationToken,
9
+ issueTokens,
10
+ makeEmailVerificationToken,
11
+ revokeCurrentRefreshToken,
12
+ sanitizeUser
13
+ } from "@/modules/auth/helpers/auth.js";
14
+ import type { RegisterInput } from "@/modules/auth/types/auth.js";
15
+
16
+ export type RegisterResult =
17
+ | { kind: "email_exists"; message: string }
18
+ | { kind: "verification_required"; message: string }
19
+ | {
20
+ kind: "registered";
21
+ message: string;
22
+ data: {
23
+ user: ReturnType<typeof sanitizeUser>;
24
+ access_token: string;
25
+ refresh_token: string;
26
+ token_type: "Bearer";
27
+ };
28
+ };
29
+
30
+ const VERIFY_TOKEN_TTL_MS = 24 * 60 * 60 * 1000;
31
+
32
+ export const registerService = {
33
+ register: async (req: FastifyRequest, reply: FastifyReply, body: RegisterInput): Promise<RegisterResult> => {
34
+ const defaultRole = await db.query.roles.findFirst({
35
+ where: eq(roles.name, "user")
36
+ });
37
+
38
+ if (!defaultRole) {
39
+ throw new Error('Default role "user" not found — check your seeds');
40
+ }
41
+
42
+ const existingUser = await db.query.users.findFirst({
43
+ where: eq(users.email, body.email)
44
+ });
45
+
46
+ if (existingUser) {
47
+ return { kind: "email_exists", message: "Email already exists" };
48
+ }
49
+
50
+ const hashedPassword = await password.hashPassword(body.password);
51
+ const now = new Date();
52
+
53
+ const created = await db.transaction(async (tx: any) => {
54
+ const [inserted] = await tx
55
+ .insert(users)
56
+ .values({
57
+ name: body.name,
58
+ email: body.email,
59
+ password: hashedPassword,
60
+ roleId: defaultRole.id
61
+ })
62
+ .returning({ id: users.id });
63
+
64
+ if (!inserted) throw new Error("Failed to insert user");
65
+
66
+ const user = await tx.query.users.findFirst({
67
+ where: eq(users.id, inserted.id),
68
+ with: { role: true }
69
+ });
70
+
71
+ if (!user) throw new Error("Inserted user not found");
72
+
73
+ let plainVerifyToken: string | null = null;
74
+
75
+ if (authConfig.requireEmailVerification) {
76
+ plainVerifyToken = makeEmailVerificationToken();
77
+ const expiresAt = new Date(now.getTime() + VERIFY_TOKEN_TTL_MS);
78
+
79
+ // One active verification token per email
80
+ await tx.delete(emailVerificationTokens).where(eq(emailVerificationTokens.email, user.email));
81
+
82
+ await tx.insert(emailVerificationTokens).values({
83
+ email: user.email,
84
+ token: hashEmailVerificationToken(plainVerifyToken),
85
+ expiresAt,
86
+ createdAt: now
87
+ });
88
+ }
89
+
90
+ return { user, plainVerifyToken };
91
+ });
92
+
93
+ // ---- Side effects AFTER commit ----
94
+
95
+ if (created.plainVerifyToken) {
96
+ const verifyUrl = urls.url(`/verify-email?token=${created.plainVerifyToken}&email=${encodeURIComponent(created.user.email)}`);
97
+
98
+ await dispatchEvent(
99
+ "user:verify-email",
100
+ {
101
+ email: created.user.email,
102
+ name: created.user.name,
103
+ verifyUrl
104
+ },
105
+ { queue: "mail" }
106
+ );
107
+
108
+ return {
109
+ kind: "verification_required",
110
+ message: "User registered successfully. Please verify your email before logging in."
111
+ };
112
+ }
113
+
114
+ await revokeCurrentRefreshToken(req);
115
+ const tokens = await issueTokens(req, reply, created.user, {
116
+ remember: !!body.remember
117
+ });
118
+
119
+ await dispatchEvent(
120
+ "user:signup",
121
+ {
122
+ userId: created.user.id,
123
+ email: created.user.email,
124
+ name: created.user.name
125
+ // NO password — see explanation below
126
+ },
127
+ { queue: "mail" }
128
+ );
129
+
130
+ return {
131
+ kind: "registered",
132
+ message: "User registered successfully",
133
+ data: {
134
+ user: sanitizeUser(created.user),
135
+ access_token: tokens.accessToken,
136
+ refresh_token: tokens.refreshToken,
137
+ token_type: "Bearer"
138
+ }
139
+ };
140
+ }
141
+ };
@@ -0,0 +1,69 @@
1
+ import { and, eq, gt } from "drizzle-orm";
2
+ import { db, password } from "@/framework/facade.js";
3
+ import { passwordResetTokens, refreshTokens, users } from "@/modules/auth/database/models/user.js";
4
+ import { hashResetToken } from "@/modules/auth/helpers/auth.js";
5
+ import type { ResetPasswordInput } from "@/modules/auth/types/auth.js";
6
+
7
+ export type ResetPasswordResult = { kind: "invalid_token"; message: string } | { kind: "reset"; message: string };
8
+
9
+ export const resetPasswordService = {
10
+ resetPassword: async (body: ResetPasswordInput): Promise<ResetPasswordResult> => {
11
+ const user = await db.query.users.findFirst({
12
+ where: eq(users.email, body.email)
13
+ });
14
+
15
+ if (!user) {
16
+ return {
17
+ kind: "invalid_token",
18
+ message: "Invalid or expired reset token"
19
+ };
20
+ }
21
+
22
+ const hashedPassword = await password.hashPassword(body.password);
23
+ const hashedToken = hashResetToken(body.token);
24
+ const now = new Date();
25
+
26
+ try {
27
+ const consumed = await db.transaction(async (tx) => {
28
+ // Consume the token atomically inside the transaction.
29
+ // If anything below fails, the delete rolls back and the
30
+ // user can retry with the same token.
31
+ const [row] = await tx
32
+ .delete(passwordResetTokens)
33
+ .where(
34
+ and(
35
+ eq(passwordResetTokens.email, body.email),
36
+ eq(passwordResetTokens.token, hashedToken),
37
+ gt(passwordResetTokens.expiresAt, now)
38
+ )
39
+ )
40
+ .returning();
41
+
42
+ if (!row) return false;
43
+
44
+ await tx.update(users).set({ password: hashedPassword, updatedAt: now }).where(eq(users.id, user.id));
45
+
46
+ await tx.update(refreshTokens).set({ revoked: 1 }).where(eq(refreshTokens.userId, user.id));
47
+
48
+ return true;
49
+ });
50
+
51
+ if (!consumed) {
52
+ return {
53
+ kind: "invalid_token",
54
+ message: "Invalid or expired reset token"
55
+ };
56
+ }
57
+
58
+ return { kind: "reset", message: "Password reset successfully" };
59
+ } catch (err) {
60
+ // Token was NOT consumed — the transaction rolled back, so the
61
+ // user can retry with the same token. Log so failures aren't silent.
62
+ // logger.error({ err, email: body.email }, "password reset failed");
63
+ return {
64
+ kind: "invalid_token",
65
+ message: "Invalid or expired reset token"
66
+ };
67
+ }
68
+ }
69
+ };
@@ -0,0 +1,77 @@
1
+ import { and, eq, gt } from "drizzle-orm";
2
+ import { authConfig } from "@/config/index.js";
3
+ import { db } from "@/framework/facade.js";
4
+ import { emailVerificationTokens, users } from "@/modules/auth/database/models/user.js";
5
+ import { hashEmailVerificationToken } from "@/modules/auth/helpers/auth.js";
6
+ import type { VerifyEmailInput } from "@/modules/auth/types/auth.js";
7
+
8
+ export type VerifyEmailResult =
9
+ | { kind: "not_required"; message: string }
10
+ | { kind: "invalid_token"; message: string }
11
+ | { kind: "verified"; message: string };
12
+
13
+ export const verifyEmailService = {
14
+ verifyEmail: async (body: VerifyEmailInput): Promise<VerifyEmailResult> => {
15
+ if (!authConfig.requireEmailVerification) {
16
+ return {
17
+ kind: "not_required",
18
+ message: "Email verification is not required"
19
+ };
20
+ }
21
+
22
+ const user = await db.query.users.findFirst({
23
+ where: eq(users.email, body.email)
24
+ });
25
+
26
+ if (!user) {
27
+ return {
28
+ kind: "invalid_token",
29
+ message: "Invalid or expired verification token"
30
+ };
31
+ }
32
+
33
+ const hashedToken = hashEmailVerificationToken(body.token);
34
+ const now = new Date();
35
+
36
+ try {
37
+ const verified = await db.transaction(async (tx) => {
38
+ // Consume the token atomically INSIDE the transaction.
39
+ // If the update below fails, the delete rolls back and the
40
+ // user can retry with the same token.
41
+ const [consumed] = await tx
42
+ .delete(emailVerificationTokens)
43
+ .where(
44
+ and(
45
+ eq(emailVerificationTokens.email, body.email),
46
+ eq(emailVerificationTokens.token, hashedToken),
47
+ gt(emailVerificationTokens.expiresAt, now)
48
+ )
49
+ )
50
+ .returning();
51
+
52
+ if (!consumed) return false;
53
+
54
+ await tx.update(users).set({ emailVerifiedAt: now, updatedAt: now }).where(eq(users.id, user.id));
55
+
56
+ return true;
57
+ });
58
+
59
+ if (!verified) {
60
+ return {
61
+ kind: "invalid_token",
62
+ message: "Invalid or expired verification token"
63
+ };
64
+ }
65
+
66
+ return { kind: "verified", message: "Email verified successfully" };
67
+ } catch (err) {
68
+ // Token was NOT consumed — the transaction rolled back, so the
69
+ // user can retry with the same token. Log so failures aren't silent.
70
+ // logger.error({ err, email: body.email }, "email verification failed");
71
+ return {
72
+ kind: "invalid_token",
73
+ message: "Invalid or expired verification token"
74
+ };
75
+ }
76
+ }
77
+ };
@@ -0,0 +1,31 @@
1
+ export type RegisterInput = {
2
+ name: string;
3
+ email: string;
4
+ password: string;
5
+ remember?: boolean;
6
+ };
7
+
8
+ export type LoginInput = {
9
+ email: string;
10
+ password: string;
11
+ remember?: boolean;
12
+ };
13
+
14
+ export type ForgotPasswordInput = {
15
+ email: string;
16
+ };
17
+
18
+ export type ResetPasswordInput = {
19
+ email: string;
20
+ token: string;
21
+ password: string;
22
+ };
23
+
24
+ export type VerifyEmailInput = {
25
+ email: string;
26
+ token: string;
27
+ };
28
+
29
+ export type RefreshTokenInput = {
30
+ refresh_token: string;
31
+ };