create-nexwire 4.2.5 → 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 (399) hide show
  1. package/README.md +29 -12
  2. package/express/.env +1 -1
  3. package/express/package.json +1 -1
  4. package/express/src/config/database.ts +48 -29
  5. package/express/src/config/index.ts +83 -83
  6. package/express/src/config/openapi.ts +6 -24
  7. package/express/src/config/queue.ts +30 -30
  8. package/express/src/config/validate.ts +50 -40
  9. package/express/src/database/migrations/sqlite/0000_init.sql +47 -47
  10. package/express/src/database/migrations/sqlite/meta/0000_snapshot.json +1 -1
  11. package/express/src/database/migrations/sqlite/meta/_journal.json +1 -1
  12. package/express/src/framework/cache/cache.ts +40 -8
  13. package/express/src/framework/circuit-breaker/cb.ts +88 -60
  14. package/express/src/framework/database/connection.ts +52 -27
  15. package/express/src/framework/events/dispatcher.ts +61 -49
  16. package/express/src/framework/facade.ts +47 -3
  17. package/express/src/framework/http/app.ts +28 -22
  18. package/express/src/framework/http/logger.ts +37 -17
  19. package/express/src/framework/http/openapi.ts +9 -11
  20. package/express/src/framework/maker-cli/runtime/core.mjs +1 -2
  21. package/express/src/framework/modules/routes.ts +26 -21
  22. package/express/src/framework/notification/channels/broadcast.ts +15 -0
  23. package/express/src/framework/notification/channels/mail.ts +42 -0
  24. package/express/src/framework/notification/contracts.ts +29 -0
  25. package/express/src/framework/notification/index.ts +19 -94
  26. package/express/src/framework/notification/registry.ts +15 -0
  27. package/express/src/framework/providers/adapter.ts +27 -17
  28. package/express/src/framework/queue/queue.ts +43 -44
  29. package/express/src/framework/realtime/broadcast.ts +29 -5
  30. package/express/src/framework/realtime/socket.ts +149 -119
  31. package/express/src/framework/redis/client.ts +148 -151
  32. package/express/src/framework/runtime/server-adapter.ts +39 -34
  33. package/express/src/framework/scheduler/lock.ts +128 -116
  34. package/express/src/framework/server.ts +33 -49
  35. package/express/src/framework/session/session.ts +69 -14
  36. package/express/src/framework/storage/storage.ts +744 -785
  37. package/express/src/framework/support/mail.ts +15 -7
  38. package/express/src/middlewares/auth-middleware.ts +1 -2
  39. package/express/src/middlewares/role-middleware.ts +3 -13
  40. package/express/src/modules/auth/routes/index.ts +1 -2
  41. package/express/src/providers/notification.ts +4 -0
  42. package/express/src/resources/src/components/Switch.vue +60 -65
  43. package/express/src/resources/src/layouts/Layout/Header.vue +124 -105
  44. package/express/src/resources/src/pages/auth/forgetPassword.vue +23 -30
  45. package/express/src/resources/src/pages/auth/login.vue +25 -28
  46. package/express/src/resources/src/pages/auth/register.vue +35 -39
  47. package/express/src/resources/src/pages/auth/resetPassword.vue +29 -37
  48. package/express/src/resources/src/pages/auth/verifyEmail.vue +15 -19
  49. package/express/src/resources/src/plugins/gum.ts +519 -203
  50. package/express/src/resources/src/plugins/pulse.ts +157 -147
  51. package/express/src/resources/src/stores/auth.ts +9 -6
  52. package/express/src/resources/vite.config.ts +54 -43
  53. package/express/src/storage/database/nexwire.sqlite +0 -0
  54. package/express/src/storage/logs/app.log +0 -0
  55. package/express/src/storage/logs/fatal.log +0 -0
  56. package/express/src/types/global.d.ts +32 -26
  57. package/fastify/.dockerignore +14 -0
  58. package/fastify/.env +44 -0
  59. package/fastify/.env.example +42 -0
  60. package/fastify/.vscode/extensions.json +6 -0
  61. package/fastify/.vscode/settings.json +37 -0
  62. package/fastify/LICENSE +21 -0
  63. package/fastify/README.md +232 -0
  64. package/fastify/biome.json +99 -0
  65. package/fastify/drizzle.config.ts +25 -0
  66. package/fastify/gitignore-stub +31 -0
  67. package/fastify/package.json +130 -0
  68. package/fastify/public/.gitkeep +0 -0
  69. package/fastify/src/config/app.ts +41 -0
  70. package/fastify/src/config/auth.ts +12 -0
  71. package/fastify/src/config/cache.ts +16 -0
  72. package/fastify/src/config/cookie.ts +15 -0
  73. package/fastify/src/config/cors.ts +12 -0
  74. package/fastify/src/config/database.ts +48 -0
  75. package/fastify/src/config/index.ts +83 -0
  76. package/fastify/src/config/jwt.ts +19 -0
  77. package/fastify/src/config/logging.ts +13 -0
  78. package/fastify/src/config/mail.ts +24 -0
  79. package/fastify/src/config/openapi.ts +36 -0
  80. package/fastify/src/config/queue.ts +30 -0
  81. package/fastify/src/config/rateLimit.ts +17 -0
  82. package/fastify/src/config/realtime.ts +15 -0
  83. package/fastify/src/config/redis.ts +18 -0
  84. package/fastify/src/config/security.ts +44 -0
  85. package/fastify/src/config/session.ts +17 -0
  86. package/fastify/src/config/storage.ts +22 -0
  87. package/fastify/src/config/validate.ts +50 -0
  88. package/fastify/src/database/migrations/sqlite/0000_init.sql +48 -0
  89. package/fastify/src/database/migrations/sqlite/meta/0000_snapshot.json +324 -0
  90. package/fastify/src/database/migrations/sqlite/meta/_journal.json +13 -0
  91. package/fastify/src/database/schema.ts +2 -0
  92. package/fastify/src/env.ts +76 -0
  93. package/fastify/src/framework/cache/cache.ts +114 -0
  94. package/fastify/src/framework/circuit-breaker/cb.ts +88 -0
  95. package/fastify/src/framework/database/app-schema.ts +37 -0
  96. package/fastify/src/framework/database/connection.ts +223 -0
  97. package/fastify/src/framework/database/migrate-hooks.ts +87 -0
  98. package/fastify/src/framework/database/paginate.ts +226 -0
  99. package/fastify/src/framework/database/schema.ts +20 -0
  100. package/fastify/src/framework/database/seed.ts +152 -0
  101. package/fastify/src/framework/events/dispatcher.ts +61 -0
  102. package/fastify/src/framework/facade.ts +96 -0
  103. package/fastify/src/framework/http/app.ts +176 -0
  104. package/fastify/src/framework/http/cors.ts +13 -0
  105. package/fastify/src/framework/http/fastify.d.ts +13 -0
  106. package/fastify/src/framework/http/logger.ts +110 -0
  107. package/fastify/src/framework/http/metrics.ts +22 -0
  108. package/fastify/src/framework/http/openapi.ts +48 -0
  109. package/fastify/src/framework/http/ratelimiter.ts +47 -0
  110. package/fastify/src/framework/http/security.ts +25 -0
  111. package/fastify/src/framework/http/static.ts +83 -0
  112. package/fastify/src/framework/http/validation.ts +49 -0
  113. package/fastify/src/framework/kernel.ts +43 -0
  114. package/fastify/src/framework/maker-cli/db/core.mjs +291 -0
  115. package/fastify/src/framework/maker-cli/db/index.mjs +204 -0
  116. package/fastify/src/framework/maker-cli/deploy/core.mjs +1046 -0
  117. package/fastify/src/framework/maker-cli/deploy/index.mjs +91 -0
  118. package/fastify/src/framework/maker-cli/index.mjs +65 -0
  119. package/fastify/src/framework/maker-cli/middleware/core.mjs +41 -0
  120. package/fastify/src/framework/maker-cli/middleware/index.mjs +17 -0
  121. package/fastify/src/framework/maker-cli/module/core.mjs +1066 -0
  122. package/fastify/src/framework/maker-cli/module/index.mjs +211 -0
  123. package/fastify/src/framework/maker-cli/provider/core.mjs +49 -0
  124. package/fastify/src/framework/maker-cli/provider/index.mjs +12 -0
  125. package/fastify/src/framework/maker-cli/runtime/build-ui.mjs +16 -0
  126. package/fastify/src/framework/maker-cli/runtime/core.mjs +560 -0
  127. package/fastify/src/framework/maker-cli/runtime/index.mjs +120 -0
  128. package/fastify/src/framework/maker-cli/runtime/redis-commander-patch.cjs +20 -0
  129. package/fastify/src/framework/maker-cli/stubs/controller/openapi.ts.stub +74 -0
  130. package/fastify/src/framework/maker-cli/stubs/controller/plain.ts.stub +77 -0
  131. package/fastify/src/framework/maker-cli/stubs/controller/standalone.ts.stub +51 -0
  132. package/fastify/src/framework/maker-cli/stubs/deploy/Dockerfile.bun.stub +52 -0
  133. package/fastify/src/framework/maker-cli/stubs/deploy/Dockerfile.pnpm.stub +55 -0
  134. package/fastify/src/framework/maker-cli/stubs/deploy/Dockerfile.stub +51 -0
  135. package/fastify/src/framework/maker-cli/stubs/deploy/Dockerfile.yarn.stub +55 -0
  136. package/fastify/src/framework/maker-cli/stubs/deploy/README.stub +59 -0
  137. package/fastify/src/framework/maker-cli/stubs/deploy/compose/mysql.server.stub +45 -0
  138. package/fastify/src/framework/maker-cli/stubs/deploy/compose/postgres.server.stub +45 -0
  139. package/fastify/src/framework/maker-cli/stubs/deploy/compose/sqlite.stub +45 -0
  140. package/fastify/src/framework/maker-cli/stubs/deploy/env/mysql.server.stub +68 -0
  141. package/fastify/src/framework/maker-cli/stubs/deploy/env/postgres.server.stub +68 -0
  142. package/fastify/src/framework/maker-cli/stubs/deploy/env/sqlite.stub +67 -0
  143. package/fastify/src/framework/maker-cli/stubs/deploy/scripts/auto-migrate.sh.stub +41 -0
  144. package/fastify/src/framework/maker-cli/stubs/deploy/server/README.stub +78 -0
  145. package/fastify/src/framework/maker-cli/stubs/deploy/server/compose/noredis.stub +120 -0
  146. package/fastify/src/framework/maker-cli/stubs/deploy/server/compose/redis.dev.stub +133 -0
  147. package/fastify/src/framework/maker-cli/stubs/deploy/server/compose/redis.stub +131 -0
  148. package/fastify/src/framework/maker-cli/stubs/deploy/server/env/local.example.stub +11 -0
  149. package/fastify/src/framework/maker-cli/stubs/deploy/server/env/noredis.stub +25 -0
  150. package/fastify/src/framework/maker-cli/stubs/deploy/server/env/redis.stub +25 -0
  151. package/fastify/src/framework/maker-cli/stubs/deploy/server/nginx-vhost/README.stub +16 -0
  152. package/fastify/src/framework/maker-cli/stubs/deploy/server/nginx-vhost/app.example.com.stub +13 -0
  153. package/fastify/src/framework/maker-cli/stubs/deploy/server/pgadmin/servers.stub +14 -0
  154. package/fastify/src/framework/maker-cli/stubs/deploy/server/redis/redis.conf.stub +7 -0
  155. package/fastify/src/framework/maker-cli/stubs/deploy/supervisor/noredis.stub +54 -0
  156. package/fastify/src/framework/maker-cli/stubs/deploy/supervisor/redis.stub +70 -0
  157. package/fastify/src/framework/maker-cli/stubs/deploy/workflow/local.json.stub +25 -0
  158. package/fastify/src/framework/maker-cli/stubs/deploy/workflow/remote.json.stub +24 -0
  159. package/fastify/src/framework/maker-cli/stubs/facade/name.ts.stub +12 -0
  160. package/fastify/src/framework/maker-cli/stubs/helper/name.ts.stub +14 -0
  161. package/fastify/src/framework/maker-cli/stubs/job/name.ts.stub +25 -0
  162. package/fastify/src/framework/maker-cli/stubs/middleware/name.local.ts.stub +15 -0
  163. package/fastify/src/framework/maker-cli/stubs/middleware/name.ts.stub +15 -0
  164. package/fastify/src/framework/maker-cli/stubs/model/name.mysql.ts.stub +10 -0
  165. package/fastify/src/framework/maker-cli/stubs/model/name.postgresql.ts.stub +10 -0
  166. package/fastify/src/framework/maker-cli/stubs/model/name.sqlite.ts.stub +10 -0
  167. package/fastify/src/framework/maker-cli/stubs/notification/controller.ts.stub +79 -0
  168. package/fastify/src/framework/maker-cli/stubs/notification/job.ts.stub +11 -0
  169. package/fastify/src/framework/maker-cli/stubs/notification/model.ts.stub +29 -0
  170. package/fastify/src/framework/maker-cli/stubs/notification/provider.ts.stub +174 -0
  171. package/fastify/src/framework/maker-cli/stubs/notification/route.api.ts.stub +71 -0
  172. package/fastify/src/framework/maker-cli/stubs/notification/route.plain.ts.stub +15 -0
  173. package/fastify/src/framework/maker-cli/stubs/notification/schema.plain.ts.stub +26 -0
  174. package/fastify/src/framework/maker-cli/stubs/notification/schema.ts.stub +26 -0
  175. package/fastify/src/framework/maker-cli/stubs/provider/module-barrel.ts.stub +19 -0
  176. package/fastify/src/framework/maker-cli/stubs/provider/module.ts.stub +21 -0
  177. package/fastify/src/framework/maker-cli/stubs/provider/name.ts.stub +21 -0
  178. package/fastify/src/framework/maker-cli/stubs/route/api.ts.stub +101 -0
  179. package/fastify/src/framework/maker-cli/stubs/route/plain.ts.stub +23 -0
  180. package/fastify/src/framework/maker-cli/stubs/route/standalone.api.ts.stub +120 -0
  181. package/fastify/src/framework/maker-cli/stubs/route/standalone.plain.ts.stub +50 -0
  182. package/fastify/src/framework/maker-cli/stubs/schedule/name.ts.stub +36 -0
  183. package/fastify/src/framework/maker-cli/stubs/schema/name.plain.ts.stub +14 -0
  184. package/fastify/src/framework/maker-cli/stubs/schema/name.ts.stub +33 -0
  185. package/fastify/src/framework/maker-cli/stubs/seeder/name.ts.stub +18 -0
  186. package/fastify/src/framework/maker-cli/stubs/seeder/standalone.ts.stub +22 -0
  187. package/fastify/src/framework/maker-cli/stubs/service/name.plain.ts.stub +67 -0
  188. package/fastify/src/framework/maker-cli/stubs/service/name.ts.stub +65 -0
  189. package/fastify/src/framework/maker-cli/stubs/test/unit.ts.stub +22 -0
  190. package/fastify/src/framework/maker-cli/stubs/type/name.ts.stub +22 -0
  191. package/fastify/src/framework/maker-cli/utils/env-config.mjs +44 -0
  192. package/fastify/src/framework/maker-cli/utils/env-db.mjs +61 -0
  193. package/fastify/src/framework/maker-cli/utils/file-ops.mjs +30 -0
  194. package/fastify/src/framework/maker-cli/utils/flags.mjs +14 -0
  195. package/fastify/src/framework/maker-cli/utils/help.mjs +379 -0
  196. package/fastify/src/framework/maker-cli/utils/naming.mjs +119 -0
  197. package/fastify/src/framework/maker-cli/utils/process.mjs +52 -0
  198. package/fastify/src/framework/maker-cli/utils/project-commands.mjs +201 -0
  199. package/fastify/src/framework/modules/discover.ts +57 -0
  200. package/fastify/src/framework/modules/routes.ts +31 -0
  201. package/fastify/src/framework/notification/channels/broadcast.ts +15 -0
  202. package/fastify/src/framework/notification/channels/mail.ts +42 -0
  203. package/fastify/src/framework/notification/contracts.ts +29 -0
  204. package/fastify/src/framework/notification/index.ts +59 -0
  205. package/fastify/src/framework/notification/registry.ts +15 -0
  206. package/fastify/src/framework/providers/adapter.ts +149 -0
  207. package/fastify/src/framework/providers/contracts.ts +126 -0
  208. package/fastify/src/framework/providers/loader.ts +105 -0
  209. package/fastify/src/framework/providers/registry.ts +88 -0
  210. package/fastify/src/framework/queue/clear.ts +22 -0
  211. package/fastify/src/framework/queue/queue.ts +401 -0
  212. package/fastify/src/framework/queue/ui.ts +194 -0
  213. package/fastify/src/framework/queue/worker.ts +61 -0
  214. package/fastify/src/framework/realtime/broadcast.ts +70 -0
  215. package/fastify/src/framework/realtime/index.ts +2 -0
  216. package/fastify/src/framework/realtime/socket-cookie.ts +72 -0
  217. package/fastify/src/framework/realtime/socket.ts +149 -0
  218. package/fastify/src/framework/realtime/types.ts +6 -0
  219. package/fastify/src/framework/realtime/ui.ts +16 -0
  220. package/fastify/src/framework/redis/client.ts +148 -0
  221. package/fastify/src/framework/runtime/runtime.ts +23 -0
  222. package/fastify/src/framework/runtime/server-adapter.ts +33 -0
  223. package/fastify/src/framework/scheduler/lock.ts +128 -0
  224. package/fastify/src/framework/scheduler/run.ts +48 -0
  225. package/fastify/src/framework/scheduler/scheduler.ts +155 -0
  226. package/fastify/src/framework/server.ts +162 -0
  227. package/fastify/src/framework/session/session.ts +160 -0
  228. package/fastify/src/framework/storage/storage.ts +744 -0
  229. package/fastify/src/framework/support/cookie.ts +71 -0
  230. package/fastify/src/framework/support/jwt.ts +33 -0
  231. package/fastify/src/framework/support/lifecycle.ts +35 -0
  232. package/fastify/src/framework/support/logger.ts +148 -0
  233. package/fastify/src/framework/support/mail.ts +56 -0
  234. package/fastify/src/framework/support/password.ts +23 -0
  235. package/fastify/src/framework/support/status-codes.ts +75 -0
  236. package/fastify/src/framework/support/upload.ts +181 -0
  237. package/fastify/src/framework/support/url.ts +41 -0
  238. package/fastify/src/middlewares/auth-middleware.ts +134 -0
  239. package/fastify/src/middlewares/role-middleware.ts +19 -0
  240. package/fastify/src/modules/auth/__tests__/forgetpass.test.ts +200 -0
  241. package/fastify/src/modules/auth/__tests__/login.test.ts +171 -0
  242. package/fastify/src/modules/auth/__tests__/logout.test.ts +60 -0
  243. package/fastify/src/modules/auth/__tests__/logoutalldevices.test.ts +49 -0
  244. package/fastify/src/modules/auth/__tests__/me.test.ts +91 -0
  245. package/fastify/src/modules/auth/__tests__/refreshtoken.test.ts +305 -0
  246. package/fastify/src/modules/auth/__tests__/register.test.ts +291 -0
  247. package/fastify/src/modules/auth/__tests__/resetpass.test.ts +206 -0
  248. package/fastify/src/modules/auth/__tests__/verifyemail.test.ts +183 -0
  249. package/fastify/src/modules/auth/controllers/auth.ts +252 -0
  250. package/fastify/src/modules/auth/controllers/role.ts +35 -0
  251. package/fastify/src/modules/auth/database/models/role.ts +14 -0
  252. package/fastify/src/modules/auth/database/models/user.ts +46 -0
  253. package/fastify/src/modules/auth/database/seeders/role.ts +18 -0
  254. package/fastify/src/modules/auth/database/seeders/user.ts +35 -0
  255. package/fastify/src/modules/auth/facade.ts +12 -0
  256. package/fastify/src/modules/auth/helpers/auth.ts +115 -0
  257. package/fastify/src/modules/auth/jobs/forgetpass.ts +20 -0
  258. package/fastify/src/modules/auth/jobs/registeruser.ts +23 -0
  259. package/fastify/src/modules/auth/jobs/verifyemail.ts +18 -0
  260. package/fastify/src/modules/auth/routes/index.ts +185 -0
  261. package/fastify/src/modules/auth/routes/role.ts +56 -0
  262. package/fastify/src/modules/auth/schemas/auth.ts +116 -0
  263. package/fastify/src/modules/auth/services/forgetpassword.ts +66 -0
  264. package/fastify/src/modules/auth/services/login.ts +63 -0
  265. package/fastify/src/modules/auth/services/logout.ts +25 -0
  266. package/fastify/src/modules/auth/services/logoutalldevices.ts +19 -0
  267. package/fastify/src/modules/auth/services/me.ts +31 -0
  268. package/fastify/src/modules/auth/services/refreshtoken.ts +110 -0
  269. package/fastify/src/modules/auth/services/register.ts +141 -0
  270. package/fastify/src/modules/auth/services/resetpassword.ts +69 -0
  271. package/fastify/src/modules/auth/services/verifyemail.ts +77 -0
  272. package/fastify/src/modules/auth/types/auth.ts +31 -0
  273. package/fastify/src/providers/README.md +107 -0
  274. package/fastify/src/providers/notification.ts +4 -0
  275. package/fastify/src/resources/index.html +17 -0
  276. package/fastify/src/resources/src/App.vue +5 -0
  277. package/fastify/src/resources/src/assets/css/styles.css +14761 -0
  278. package/fastify/src/resources/src/assets/css/styles.css.map +1 -0
  279. package/fastify/src/resources/src/assets/fonts/public-sans/public-sans-latin-ext.woff2 +0 -0
  280. package/fastify/src/resources/src/assets/fonts/public-sans/public-sans-latin.woff2 +0 -0
  281. package/fastify/src/resources/src/assets/images/favicon/favicon.ico +0 -0
  282. package/fastify/src/resources/src/assets/images/logo.png +0 -0
  283. package/fastify/src/resources/src/assets/scss/custom.css +217 -0
  284. package/fastify/src/resources/src/assets/scss/custom.css.map +1 -0
  285. package/fastify/src/resources/src/assets/scss/custom.scss +1346 -0
  286. package/fastify/src/resources/src/components/Button.vue +32 -0
  287. package/fastify/src/resources/src/components/Checkbox.vue +32 -0
  288. package/fastify/src/resources/src/components/Datepicker.vue +106 -0
  289. package/fastify/src/resources/src/components/FeatureButton.vue +53 -0
  290. package/fastify/src/resources/src/components/FloatButton.vue +33 -0
  291. package/fastify/src/resources/src/components/Href.vue +32 -0
  292. package/fastify/src/resources/src/components/Input.vue +243 -0
  293. package/fastify/src/resources/src/components/InputGroup.vue +147 -0
  294. package/fastify/src/resources/src/components/InputPasswordToggle.vue +229 -0
  295. package/fastify/src/resources/src/components/Modal.vue +71 -0
  296. package/fastify/src/resources/src/components/Pagebar.vue +26 -0
  297. package/fastify/src/resources/src/components/Refresh.vue +19 -0
  298. package/fastify/src/resources/src/components/Select.vue +384 -0
  299. package/fastify/src/resources/src/components/Spinner.vue +42 -0
  300. package/fastify/src/resources/src/components/Switch.vue +60 -0
  301. package/fastify/src/resources/src/components/TextArea.vue +117 -0
  302. package/fastify/src/resources/src/components/Toast.vue +56 -0
  303. package/fastify/src/resources/src/components/datatable/DataTableSkeleton.vue +98 -0
  304. package/fastify/src/resources/src/components/datatable/Pagination.vue +147 -0
  305. package/fastify/src/resources/src/components/datatable/SelectOpption.vue +64 -0
  306. package/fastify/src/resources/src/components/datatable/index.vue +199 -0
  307. package/fastify/src/resources/src/composables/useAuth.ts +52 -0
  308. package/fastify/src/resources/src/helpers/assets.ts +51 -0
  309. package/fastify/src/resources/src/helpers/nformatter.ts +10 -0
  310. package/fastify/src/resources/src/helpers/utils.ts +75 -0
  311. package/fastify/src/resources/src/layouts/AuthLayout.vue +20 -0
  312. package/fastify/src/resources/src/layouts/Layout/Footer.vue +23 -0
  313. package/fastify/src/resources/src/layouts/Layout/Header.vue +124 -0
  314. package/fastify/src/resources/src/layouts/Layout/Sidebar.vue +137 -0
  315. package/fastify/src/resources/src/layouts/Layout/index.vue +87 -0
  316. package/fastify/src/resources/src/main.ts +28 -0
  317. package/fastify/src/resources/src/pages/auth/forgetPassword.vue +76 -0
  318. package/fastify/src/resources/src/pages/auth/login.vue +96 -0
  319. package/fastify/src/resources/src/pages/auth/register.vue +129 -0
  320. package/fastify/src/resources/src/pages/auth/resetPassword.vue +110 -0
  321. package/fastify/src/resources/src/pages/auth/verifyEmail.vue +64 -0
  322. package/fastify/src/resources/src/pages/dashboard/index.vue +66 -0
  323. package/fastify/src/resources/src/plugins/axios.ts +31 -0
  324. package/fastify/src/resources/src/plugins/browserDetect.ts +55 -0
  325. package/fastify/src/resources/src/plugins/dialog.ts +216 -0
  326. package/fastify/src/resources/src/plugins/gum.ts +683 -0
  327. package/fastify/src/resources/src/plugins/pulse.ts +157 -0
  328. package/fastify/src/resources/src/plugins/routeProgress.ts +87 -0
  329. package/fastify/src/resources/src/router/index.ts +85 -0
  330. package/fastify/src/resources/src/stores/admin-ui.ts +148 -0
  331. package/fastify/src/resources/src/stores/auth.ts +187 -0
  332. package/fastify/src/resources/tsconfig.json +19 -0
  333. package/fastify/src/resources/vite.config.ts +64 -0
  334. package/fastify/src/storage/app/private/.gitkeep +0 -0
  335. package/fastify/src/storage/app/public/.gitkeep +0 -0
  336. package/fastify/src/storage/app/tmp/generated/.gitkeep +0 -0
  337. package/fastify/src/storage/database/nexwire.sqlite +0 -0
  338. package/fastify/src/storage/logs/.gitkeep +0 -0
  339. package/fastify/src/storage/logs/app.log +0 -0
  340. package/fastify/src/storage/logs/fatal.log +0 -0
  341. package/fastify/src/storage/tmp/.gitkeep +0 -0
  342. package/fastify/src/storage/trash/modules/.gitkeep +0 -0
  343. package/fastify/src/types/global.d.ts +32 -0
  344. package/fastify/tsconfig.json +19 -0
  345. package/fastify/vitest.config.ts +38 -0
  346. package/hono/.env +1 -1
  347. package/hono/package.json +1 -1
  348. package/hono/src/config/database.ts +48 -29
  349. package/hono/src/config/index.ts +83 -83
  350. package/hono/src/config/openapi.ts +6 -24
  351. package/hono/src/config/queue.ts +30 -30
  352. package/hono/src/config/validate.ts +50 -40
  353. package/hono/src/database/migrations/sqlite/0000_init.sql +47 -47
  354. package/hono/src/database/migrations/sqlite/meta/0000_snapshot.json +1 -1
  355. package/hono/src/database/migrations/sqlite/meta/_journal.json +1 -1
  356. package/hono/src/framework/cache/cache.ts +40 -8
  357. package/hono/src/framework/circuit-breaker/cb.ts +88 -60
  358. package/hono/src/framework/database/connection.ts +52 -27
  359. package/hono/src/framework/events/dispatcher.ts +61 -49
  360. package/hono/src/framework/facade.ts +39 -1
  361. package/hono/src/framework/http/logger.ts +86 -81
  362. package/hono/src/framework/http/openapi.ts +7 -9
  363. package/hono/src/framework/maker-cli/runtime/core.mjs +1 -2
  364. package/hono/src/framework/notification/channels/broadcast.ts +15 -0
  365. package/hono/src/framework/notification/channels/mail.ts +42 -0
  366. package/hono/src/framework/notification/contracts.ts +29 -0
  367. package/hono/src/framework/notification/index.ts +19 -94
  368. package/hono/src/framework/notification/registry.ts +15 -0
  369. package/hono/src/framework/providers/adapter.ts +27 -17
  370. package/hono/src/framework/queue/queue.ts +43 -44
  371. package/hono/src/framework/realtime/broadcast.ts +29 -5
  372. package/hono/src/framework/realtime/socket.ts +217 -196
  373. package/hono/src/framework/redis/client.ts +148 -151
  374. package/hono/src/framework/scheduler/lock.ts +128 -116
  375. package/hono/src/framework/server.ts +27 -29
  376. package/hono/src/framework/session/session.ts +62 -14
  377. package/hono/src/framework/storage/storage.ts +744 -785
  378. package/hono/src/framework/support/mail.ts +56 -48
  379. package/hono/src/middlewares/auth-middleware.ts +1 -2
  380. package/hono/src/middlewares/role-middleware.ts +3 -13
  381. package/hono/src/modules/auth/routes/index.ts +1 -2
  382. package/hono/src/providers/notification.ts +4 -0
  383. package/hono/src/resources/src/components/Switch.vue +60 -65
  384. package/hono/src/resources/src/layouts/Layout/Header.vue +124 -105
  385. package/hono/src/resources/src/pages/auth/forgetPassword.vue +23 -29
  386. package/hono/src/resources/src/pages/auth/login.vue +26 -29
  387. package/hono/src/resources/src/pages/auth/register.vue +36 -40
  388. package/hono/src/resources/src/pages/auth/resetPassword.vue +29 -37
  389. package/hono/src/resources/src/pages/auth/verifyEmail.vue +15 -19
  390. package/hono/src/resources/src/plugins/gum.ts +519 -203
  391. package/hono/src/resources/src/plugins/pulse.ts +157 -147
  392. package/hono/src/resources/src/stores/auth.ts +9 -6
  393. package/hono/src/resources/vite.config.ts +54 -43
  394. package/hono/src/storage/database/nexwire.sqlite +0 -0
  395. package/hono/src/storage/logs/app.log +0 -0
  396. package/hono/src/storage/logs/fatal.log +0 -0
  397. package/hono/src/types/global.d.ts +32 -26
  398. package/package.json +4 -3
  399. package/src/index.js +143 -15
@@ -1,8 +1,9 @@
1
1
  import { type Job, type JobsOptions, Queue, QueueEvents, Worker } from "bullmq";
2
2
  import { type DurableContext, type DurableJob, DurableWorker, RedisStateStore } from "bullmq-durable";
3
- import { queueConfig } from "@/config/index.js";
3
+ import { queueConfig, redisConfig } from "@/config/index.js";
4
4
  import { discoverModuleFiles, importFile } from "@/framework/modules/discover.js";
5
5
  import { redisClientIfReady } from "@/framework/redis/client.js";
6
+ import { logger } from "@/framework/support/logger.js";
6
7
 
7
8
  export type QueueJob = {
8
9
  name: string;
@@ -10,9 +11,7 @@ export type QueueJob = {
10
11
  };
11
12
 
12
13
  export type QueueHandler = (job: Job) => Promise<any>;
13
-
14
14
  export type DurableQueueHandler = (job: DurableJob, ctx: DurableContext) => Promise<any>;
15
-
16
15
  export type AnyQueueHandler = QueueHandler | DurableQueueHandler;
17
16
 
18
17
  const queues = new Map<string, Queue>();
@@ -48,6 +47,14 @@ function key(queue: string, job: string) {
48
47
  return `${queue}:${job}`;
49
48
  }
50
49
 
50
+ /**
51
+ * Why: BullMQ manages its own connections. We pass the URL string,
52
+ * not the shared client, so BullMQ doesn't crash if the shared client dies.
53
+ */
54
+ function connectionOptions() {
55
+ return { url: redisConfig.url };
56
+ }
57
+
51
58
  /**
52
59
  * Why: Registers a job handler for a queue + job name.
53
60
  * When: Module job files are loaded during boot.
@@ -73,14 +80,14 @@ export function shouldQueue(job: string, queue: string, handler: AnyQueueHandler
73
80
  * How: Reuses shared Redis connection and memoized queue map.
74
81
  */
75
82
  export function getQueue(queue = "default") {
76
- const client = redisClientIfReady();
77
- if (!client) return null;
83
+ // Check if Redis is supposed to be enabled
84
+ if (!redisConfig.enabled) return null;
78
85
 
79
86
  if (!queues.has(queue)) {
80
87
  queues.set(
81
88
  queue,
82
89
  new Queue(queue, {
83
- connection: client as any,
90
+ connection: connectionOptions(), // Let BullMQ handle its own connection
84
91
  prefix: queuePrefix(),
85
92
  defaultJobOptions: {
86
93
  attempts: 3,
@@ -197,15 +204,20 @@ export async function queueJob(
197
204
  return null;
198
205
  }
199
206
 
200
- return await queue.add(job, data, {
201
- delay: options.delay ? options.delay * 1000 : 0,
202
- attempts: options.attempts ?? 3,
203
- jobId: options.jobId,
204
- priority: options.priority,
205
- removeOnComplete: options.removeOnComplete ?? 1000,
206
- removeOnFail: options.removeOnFail ?? 5000,
207
- backoff: options.backoff ?? { type: "exponential", delay: 3000 }
208
- });
207
+ try {
208
+ return await queue.add(job, data, {
209
+ delay: options.delay ? options.delay * 1000 : 0,
210
+ attempts: options.attempts ?? 3,
211
+ jobId: options.jobId,
212
+ priority: options.priority,
213
+ removeOnComplete: options.removeOnComplete ?? 1000,
214
+ removeOnFail: options.removeOnFail ?? 5000,
215
+ backoff: options.backoff ?? { type: "exponential", delay: 3000 }
216
+ });
217
+ } catch (error) {
218
+ logger.error(`Failed to queue job ${job}`, { error });
219
+ return null;
220
+ }
209
221
  }
210
222
 
211
223
  /**
@@ -234,7 +246,7 @@ export async function bootQueueJobs() {
234
246
  * queue's keys on demand if any code path still dispatches to it.
235
247
  */
236
248
  export async function pruneStaleQueues(protectedQueues: string[] = []) {
237
- if (!queueConfig.autoPruneQueues) return;
249
+ if (!queueConfig.autoPruneQueues || !redisConfig.enabled) return;
238
250
 
239
251
  const client = redisClientIfReady();
240
252
  if (!client) return;
@@ -264,13 +276,13 @@ export async function pruneStaleQueues(protectedQueues: string[] = []) {
264
276
  if (live !== 0) continue;
265
277
 
266
278
  await queue.obliterate({ force: true });
267
- console.log(`Pruned stale queue "${name}"`);
279
+ logger.info(`Pruned stale queue "${name}"`);
268
280
  } catch (error: any) {
269
- console.warn(`Could not prune stale queue "${name}": ${error?.message || error}`);
281
+ logger.warn(`Could not prune stale queue "${name}": ${error?.message || error}`);
270
282
  }
271
283
  }
272
284
  } catch (error: any) {
273
- console.warn(`Could not scan queues for stale cleanup: ${error?.message || error}`);
285
+ logger.warn(`Could not scan queues for stale cleanup: ${error?.message || error}`);
274
286
  }
275
287
  }
276
288
 
@@ -282,23 +294,21 @@ export async function pruneStaleQueues(protectedQueues: string[] = []) {
282
294
  */
283
295
  export async function startQueueWorker(queueNames = ["default"]) {
284
296
  await bootQueueJobs();
285
- const client = redisClientIfReady();
286
- if (!client) throw new Error("Redis is required for queue workers");
297
+ if (!redisConfig.enabled) throw new Error("Redis is required for queue workers");
287
298
 
288
299
  for (const queueName of queueNames) {
289
- // Already working this queue in this process: a second worker would add a
290
- // second blocking connection and a second copy of every log line, and would
291
- // never process a job the first one was not already getting.
292
300
  if (workedQueues.has(queueName)) continue;
293
301
  workedQueues.add(queueName);
294
302
 
295
303
  if (!events.has(queueName)) {
296
- events.set(queueName, new QueueEvents(queueName, { connection: client as any, prefix: queuePrefix() }));
304
+ // Let BullMQ manage its own connection for events
305
+ events.set(queueName, new QueueEvents(queueName, { connection: connectionOptions(), prefix: queuePrefix() }));
297
306
  }
298
307
 
299
308
  const durable = [...durableHandlers.keys()].some((k) => k.startsWith(`${queueName}:`));
300
309
 
301
310
  if (durable) {
311
+ // Durable worker still needs a shared client for its state store in this specific lib
302
312
  const store = durableStateStoreIfReady();
303
313
  if (!store) throw new Error("Redis is required for durable queue workers");
304
314
 
@@ -311,20 +321,14 @@ export async function startQueueWorker(queueNames = ["default"]) {
311
321
  }
312
322
 
313
323
  const worker = new DurableWorker(queueName, map as never, {
314
- connection: client as any,
324
+ connection: connectionOptions(),
315
325
  prefix: queuePrefix(),
316
326
  stateStore: store,
317
327
  concurrency: 10
318
328
  });
319
329
 
320
- worker.on("completed", (job) => {
321
- console.log(`[${new Date().toLocaleTimeString()}] Processed: ${job.name} (${queueName})`);
322
- });
323
-
324
- worker.on("failed", (job, error) => {
325
- const jobName = job?.name ?? "unknown";
326
- console.log(`[${new Date().toLocaleTimeString()}] Failed: ${jobName} (${queueName}) - ${error.message}`);
327
- });
330
+ worker.on("completed", (job) => logger.info(`Processed: ${job.name} (${queueName})`));
331
+ worker.on("failed", (job, error) => logger.warn(`Failed: ${job?.name ?? "unknown"} (${queueName}) - ${error.message}`));
328
332
 
329
333
  workers.push(worker as unknown as Worker);
330
334
  continue;
@@ -333,22 +337,16 @@ export async function startQueueWorker(queueNames = ["default"]) {
333
337
  const worker = new Worker(
334
338
  queueName,
335
339
  async (job) => {
336
- console.log(`[${new Date().toLocaleTimeString()}] Processing: ${job.name} (${queueName})`);
340
+ logger.info(`Processing: ${job.name} (${queueName})`);
337
341
  const handler = handlers.get(key(queueName, job.name));
338
342
  if (!handler) throw new Error(`No handler registered for ${queueName}:${job.name}`);
339
343
  return await handler(job);
340
344
  },
341
- { connection: client as any, prefix: queuePrefix(), concurrency: 10 }
345
+ { connection: connectionOptions(), prefix: queuePrefix(), concurrency: 10 }
342
346
  );
343
347
 
344
- worker.on("completed", (job) => {
345
- console.log(`[${new Date().toLocaleTimeString()}] Processed: ${job.name} (${queueName})`);
346
- });
347
-
348
- worker.on("failed", (job, error) => {
349
- const jobName = job?.name ?? "unknown";
350
- console.log(`[${new Date().toLocaleTimeString()}] Failed: ${jobName} (${queueName}) - ${error.message}`);
351
- });
348
+ worker.on("completed", (job) => logger.info(`Processed: ${job.name} (${queueName})`));
349
+ worker.on("failed", (job, error) => logger.warn(`Failed: ${job?.name ?? "unknown"} (${queueName}) - ${error.message}`));
352
350
 
353
351
  workers.push(worker);
354
352
  }
@@ -364,6 +362,7 @@ export async function startQueueWorker(queueNames = ["default"]) {
364
362
  * Cache/session/rate-limit keys are intentionally left alone.
365
363
  */
366
364
  export async function clearQueue() {
365
+ if (!redisConfig.enabled) return;
367
366
  const client = redisClientIfReady();
368
367
  if (!client) return;
369
368
 
@@ -1,6 +1,8 @@
1
1
  import { env } from "@/env.js";
2
+ import { createCircuitBreaker, isCircuitBreakerError } from "@/framework/circuit-breaker/cb.js";
2
3
  import { socketServer } from "@/framework/realtime/socket.js";
3
4
  import { redisClientIfReady } from "@/framework/redis/client.js";
5
+ import { logger } from "@/framework/support/logger.js";
4
6
 
5
7
  export type BroadcastOptions = {
6
8
  users?: Array<string | number>;
@@ -10,6 +12,13 @@ export type BroadcastOptions = {
10
12
  all?: boolean;
11
13
  };
12
14
 
15
+ // Circuit breaker for the pub/sub relay (used by workers that don't have Socket.IO)
16
+ const broadcastBreaker = createCircuitBreaker({
17
+ name: "Realtime Broadcast Relay",
18
+ failureThreshold: 3,
19
+ resetTimeout: 30000
20
+ });
21
+
13
22
  /**
14
23
  * Why: Emits realtime events to targeted audiences.
15
24
  * When: Domain events need websocket fan-out.
@@ -29,18 +38,33 @@ function emitViaIo(event: string, payload: any, options: BroadcastOptions) {
29
38
  for (const room of options.rooms || []) io.to(room).emit(event, payload);
30
39
  }
31
40
 
32
- export function broadcast(event: string, payload: any, options: BroadcastOptions = {}) {
41
+ export async function broadcast(event: string, payload: any, options: BroadcastOptions = {}) {
33
42
  const io = socketServer();
34
43
 
44
+ // If this process has Socket.IO (the main API server), emit directly
35
45
  if (io) {
36
- emitViaIo(event, payload, options);
46
+ try {
47
+ emitViaIo(event, payload, options);
48
+ } catch (error) {
49
+ logger.error("Failed to broadcast event via Socket.IO", { error, event });
50
+ }
37
51
  return;
38
52
  }
39
53
 
40
- // No Socket.IO in this process — relay through Redis so the main process can broadcast
54
+ // If this is a background worker, relay through Redis
41
55
  const redis = redisClientIfReady();
42
56
  if (!redis) return;
43
57
 
44
- const channel = `${env.REDIS_PREFIX}:broadcast`;
45
- redis.publish(channel, JSON.stringify({ event, payload, options }));
58
+ try {
59
+ await broadcastBreaker.exec(async () => {
60
+ const channel = `${env.REDIS_PREFIX}:broadcast`;
61
+ await redis.publish(channel, JSON.stringify({ event, payload, options }));
62
+ });
63
+ } catch (error) {
64
+ if (isCircuitBreakerError(error)) {
65
+ logger.warn("Broadcast relay skipped: Circuit breaker is open");
66
+ } else {
67
+ logger.error("Failed to relay broadcast via Redis", { error, event });
68
+ }
69
+ }
46
70
  }
@@ -1,119 +1,149 @@
1
- import { createAdapter } from "@socket.io/redis-adapter";
2
- import { Server as SocketIOServer } from "socket.io";
3
- import { appConfig, realtimeConfig, redisConfig } from "@/config/index.js";
4
- import { authFromSocketHandshake, unauthenticatedRealtimeAuth } from "@/framework/realtime/socket-cookie.js";
5
- import type { RealtimeAuthContext } from "@/framework/realtime/types.js";
6
- import { redisClientIfReady } from "@/framework/redis/client.js";
7
- import { logger } from "@/framework/support/logger.js";
8
-
9
- export type RealtimeServeWebSocket = Record<string, never>;
10
-
11
- export type RealtimeInit = {
12
- io: SocketIOServer;
13
- websocketServe: RealtimeServeWebSocket;
14
- };
15
-
16
- export const WS_PATH = "/socket.io";
17
-
18
- let io: SocketIOServer | null = null;
19
- let adapterSubClient: ReturnType<typeof redisClientIfReady> | null = null;
20
-
21
- function socketAllowedOrigins() {
22
- const origins = [appConfig.url, appConfig.frontendUrl].filter(Boolean) as string[];
23
- if (realtimeConfig.enabled) origins.push("https://admin.socket.io");
24
- return [...new Set(origins)];
25
- }
26
-
27
- async function attachRedisAdapter() {
28
- if (!redisConfig.enabled) return;
29
-
30
- const pubClient = redisClientIfReady();
31
- if (!pubClient) return;
32
-
33
- adapterSubClient = pubClient.duplicate();
34
- await adapterSubClient.connect();
35
- io?.adapter(createAdapter(pubClient, adapterSubClient));
36
- }
37
-
38
- function registerAuthAndRooms() {
39
- io?.use(async (socket, next) => {
40
- const auth = await authFromSocketHandshake(socket);
41
- socket.data.auth = auth as RealtimeAuthContext;
42
-
43
- if (auth.isAuthenticated) {
44
- socket.join("auth");
45
- if (auth.userId) socket.join(`user:${auth.userId}`);
46
- for (const role of auth.roles) socket.join(`role:${role}`);
47
- } else {
48
- socket.join("guest");
49
- }
50
-
51
- next();
52
- });
53
-
54
- io?.on("connection", (socket) => {
55
- const auth = (socket.data.auth || unauthenticatedRealtimeAuth()) as RealtimeAuthContext;
56
-
57
- logger.debug("Socket connected", {
58
- socketId: socket.id,
59
- authenticated: auth.isAuthenticated,
60
- userId: auth.userId
61
- });
62
-
63
- socket.on("join", (room) => socket.join(String(room)));
64
- socket.on("disconnect", (reason) => {
65
- logger.info("Socket disconnected", {
66
- socketId: socket.id,
67
- reason,
68
- authenticated: auth.isAuthenticated,
69
- userId: auth.userId
70
- });
71
- });
72
- });
73
- }
74
-
75
- export async function initRealtime(target: { httpServer: any }): Promise<RealtimeInit | null> {
76
- if (!realtimeConfig.enabled) {
77
- logger.info("Socket.IO disabled (disabled in config)");
78
- return null;
79
- }
80
-
81
- if (io) return { io, websocketServe: {} };
82
-
83
- io = new SocketIOServer(target.httpServer, {
84
- cors: { origin: socketAllowedOrigins(), credentials: true }
85
- });
86
-
87
- await attachRedisAdapter();
88
- registerAuthAndRooms();
89
-
90
- logger.info(`Socket.IO enabled at ${WS_PATH}`);
91
- return { io, websocketServe: {} };
92
- }
93
-
94
- export function socketServer() {
95
- return io;
96
- }
97
-
98
- export function ioServer() {
99
- return io;
100
- }
101
-
102
- export async function closeRealtime() {
103
- if (!io) return;
104
-
105
- await new Promise<void>((resolve) => {
106
- io?.close(() => resolve());
107
- });
108
-
109
- if (adapterSubClient) {
110
- try {
111
- await adapterSubClient.quit();
112
- } catch {
113
- adapterSubClient.disconnect();
114
- }
115
- adapterSubClient = null;
116
- }
117
-
118
- io = null;
119
- }
1
+ import { createAdapter } from "@socket.io/redis-adapter";
2
+ import { Server as SocketIOServer } from "socket.io";
3
+ import { appConfig, realtimeConfig, redisConfig } from "@/config/index.js";
4
+ import { createCircuitBreaker, isCircuitBreakerError } from "@/framework/circuit-breaker/cb.js";
5
+ import { authFromSocketHandshake, unauthenticatedRealtimeAuth } from "@/framework/realtime/socket-cookie.js";
6
+ import type { RealtimeAuthContext } from "@/framework/realtime/types.js";
7
+ import { redisClientIfReady } from "@/framework/redis/client.js";
8
+ import { logger } from "@/framework/support/logger.js";
9
+
10
+ export const WS_PATH = "/socket.io";
11
+
12
+ // Create a breaker for the Socket.IO Redis adapter
13
+ const socketAdapterBreaker = createCircuitBreaker({
14
+ name: "Socket.IO Redis Adapter",
15
+ failureThreshold: 3,
16
+ resetTimeout: 30000
17
+ });
18
+
19
+ export type RealtimeServeWebSocket = Record<string, never>;
20
+
21
+ export type RealtimeInit = {
22
+ io: SocketIOServer;
23
+ websocketServe: RealtimeServeWebSocket;
24
+ };
25
+
26
+ let io: SocketIOServer | null = null;
27
+ let adapterSubClient: ReturnType<typeof redisClientIfReady> | null = null;
28
+
29
+ function socketAllowedOrigins() {
30
+ const origins = [appConfig.url, appConfig.frontendUrl].filter(Boolean) as string[];
31
+ if (realtimeConfig.enabled) origins.push("https://admin.socket.io");
32
+ return [...new Set(origins)];
33
+ }
34
+
35
+ async function attachRedisAdapter() {
36
+ if (!redisConfig.enabled) return;
37
+
38
+ try {
39
+ await socketAdapterBreaker.exec(async () => {
40
+ const pubClient = redisClientIfReady();
41
+ if (!pubClient) throw new Error("Redis client not ready");
42
+
43
+ // Socket.IO requires a duplicate connection for subscriptions
44
+ adapterSubClient = pubClient.duplicate();
45
+ await adapterSubClient.connect();
46
+
47
+ io?.adapter(createAdapter(pubClient, adapterSubClient));
48
+ logger.debug("Socket.IO Redis adapter attached successfully");
49
+ });
50
+ } catch (error) {
51
+ if (isCircuitBreakerError(error)) {
52
+ logger.warn("Socket.IO Redis adapter skipped due to circuit breaker");
53
+ } else {
54
+ logger.error("Failed to attach Socket.IO Redis adapter", { error });
55
+ }
56
+ // Fallback: Socket.IO will automatically use its default in-memory adapter.
57
+ }
58
+ }
59
+
60
+ function registerAuthAndRooms() {
61
+ io?.use(async (socket, next) => {
62
+ const auth = await authFromSocketHandshake(socket);
63
+ socket.data.auth = auth as RealtimeAuthContext;
64
+
65
+ if (auth.isAuthenticated) {
66
+ socket.join("auth");
67
+ if (auth.userId) socket.join(`user:${auth.userId}`);
68
+ for (const role of auth.roles) socket.join(`role:${role}`);
69
+ } else {
70
+ socket.join("guest");
71
+ }
72
+
73
+ next();
74
+ });
75
+
76
+ io?.on("connection", (socket) => {
77
+ const auth = (socket.data.auth || unauthenticatedRealtimeAuth()) as RealtimeAuthContext;
78
+
79
+ logger.debug("Socket connected", {
80
+ socketId: socket.id,
81
+ authenticated: auth.isAuthenticated,
82
+ userId: auth.userId
83
+ });
84
+
85
+ socket.on("join", (room) => socket.join(String(room)));
86
+ socket.on("disconnect", (reason) => {
87
+ logger.info("Socket disconnected", {
88
+ socketId: socket.id,
89
+ reason,
90
+ authenticated: auth.isAuthenticated,
91
+ userId: auth.userId
92
+ });
93
+ });
94
+ });
95
+ }
96
+
97
+ /**
98
+ * Why: Boots the singleton Socket.IO server for Express.
99
+ * When: Express HTTP server starts and realtime is enabled.
100
+ * Where: Framework server lifecycle.
101
+ * How: Attaches Socket.IO directly to the Express/Node httpServer.
102
+ */
103
+ export async function initRealtime(target: { httpServer: any }): Promise<RealtimeInit | null> {
104
+ if (!realtimeConfig.enabled) {
105
+ logger.debug("Socket.IO disabled (disabled in config)");
106
+ return null;
107
+ }
108
+
109
+ if (io) return { io, websocketServe: {} };
110
+
111
+ // Express uses the native Node.js httpServer
112
+ io = new SocketIOServer(target.httpServer, {
113
+ path: WS_PATH, // Explicitly set the path to match Hono
114
+ cors: { origin: socketAllowedOrigins(), credentials: true }
115
+ });
116
+
117
+ await attachRedisAdapter();
118
+ registerAuthAndRooms();
119
+
120
+ logger.debug(`Socket.IO enabled at ${WS_PATH}`);
121
+ return { io, websocketServe: {} };
122
+ }
123
+
124
+ export function socketServer() {
125
+ return io;
126
+ }
127
+
128
+ export function ioServer() {
129
+ return io;
130
+ }
131
+
132
+ export async function closeRealtime() {
133
+ if (!io) return;
134
+
135
+ await new Promise<void>((resolve) => {
136
+ io?.close(() => resolve());
137
+ });
138
+
139
+ if (adapterSubClient) {
140
+ try {
141
+ await adapterSubClient.quit();
142
+ } catch {
143
+ adapterSubClient.disconnect();
144
+ }
145
+ adapterSubClient = null;
146
+ }
147
+
148
+ io = null;
149
+ }