create-nexwire 4.0.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 (488) hide show
  1. package/README.md +233 -0
  2. package/express/.dockerignore +14 -0
  3. package/express/.env +44 -0
  4. package/express/.env.example +42 -0
  5. package/express/.vscode/settings.json +26 -0
  6. package/express/LICENSE +21 -0
  7. package/express/README.md +211 -0
  8. package/express/biome.json +66 -0
  9. package/express/drizzle.config.ts +25 -0
  10. package/express/gitignore-stub +24 -0
  11. package/express/package.json +108 -0
  12. package/express/public/.gitkeep +0 -0
  13. package/express/src/config/app.ts +41 -0
  14. package/express/src/config/auth.ts +12 -0
  15. package/express/src/config/cache.ts +16 -0
  16. package/express/src/config/cookie.ts +15 -0
  17. package/express/src/config/cors.ts +12 -0
  18. package/express/src/config/database.ts +29 -0
  19. package/express/src/config/index.ts +83 -0
  20. package/express/src/config/jwt.ts +19 -0
  21. package/express/src/config/logging.ts +13 -0
  22. package/express/src/config/mail.ts +24 -0
  23. package/express/src/config/openapi.ts +54 -0
  24. package/express/src/config/queue.ts +30 -0
  25. package/express/src/config/rateLimit.ts +17 -0
  26. package/express/src/config/realtime.ts +15 -0
  27. package/express/src/config/redis.ts +18 -0
  28. package/express/src/config/security.ts +44 -0
  29. package/express/src/config/session.ts +17 -0
  30. package/express/src/config/storage.ts +22 -0
  31. package/express/src/config/validate.ts +40 -0
  32. package/express/src/database/migrations/sqlite/0000_init.sql +61 -0
  33. package/express/src/database/migrations/sqlite/meta/0000_snapshot.json +412 -0
  34. package/express/src/database/migrations/sqlite/meta/_journal.json +13 -0
  35. package/express/src/database/schema.ts +3 -0
  36. package/express/src/env.ts +76 -0
  37. package/express/src/framework/cache/cache.ts +81 -0
  38. package/express/src/framework/circuit-breaker/cb.ts +60 -0
  39. package/express/src/framework/database/connection.ts +196 -0
  40. package/express/src/framework/database/migrate-hooks.ts +87 -0
  41. package/express/src/framework/database/paginate.ts +227 -0
  42. package/express/src/framework/database/schema.ts +20 -0
  43. package/express/src/framework/database/seed.ts +139 -0
  44. package/express/src/framework/events/dispatcher.ts +49 -0
  45. package/express/src/framework/facade.ts +48 -0
  46. package/express/src/framework/http/app.ts +107 -0
  47. package/express/src/framework/http/cors.ts +7 -0
  48. package/express/src/framework/http/logger.ts +69 -0
  49. package/express/src/framework/http/metrics.ts +22 -0
  50. package/express/src/framework/http/openapi.ts +54 -0
  51. package/express/src/framework/http/ratelimiter.ts +16 -0
  52. package/express/src/framework/http/router.ts +178 -0
  53. package/express/src/framework/http/security.ts +23 -0
  54. package/express/src/framework/http/static.ts +23 -0
  55. package/express/src/framework/http/validation.ts +18 -0
  56. package/express/src/framework/kernel.ts +52 -0
  57. package/express/src/framework/maker-cli/db/core.mjs +288 -0
  58. package/express/src/framework/maker-cli/db/index.mjs +204 -0
  59. package/express/src/framework/maker-cli/deploy/core.mjs +1047 -0
  60. package/express/src/framework/maker-cli/deploy/index.mjs +91 -0
  61. package/express/src/framework/maker-cli/index.mjs +53 -0
  62. package/express/src/framework/maker-cli/middleware/core.mjs +47 -0
  63. package/express/src/framework/maker-cli/middleware/index.mjs +17 -0
  64. package/express/src/framework/maker-cli/module/core.mjs +684 -0
  65. package/express/src/framework/maker-cli/module/index.mjs +140 -0
  66. package/express/src/framework/maker-cli/runtime/build-ui.mjs +16 -0
  67. package/express/src/framework/maker-cli/runtime/core.mjs +543 -0
  68. package/express/src/framework/maker-cli/runtime/index.mjs +104 -0
  69. package/express/src/framework/maker-cli/runtime/redis-commander-patch.cjs +20 -0
  70. package/express/src/framework/maker-cli/stubs/controller/openapi.ts.stub +47 -0
  71. package/express/src/framework/maker-cli/stubs/controller/openapi.with-model.ts.stub +48 -0
  72. package/express/src/framework/maker-cli/stubs/controller/plain.ts.stub +54 -0
  73. package/express/src/framework/maker-cli/stubs/controller/schema.plain.ts.stub +14 -0
  74. package/express/src/framework/maker-cli/stubs/controller/schema.ts.stub +33 -0
  75. package/express/src/framework/maker-cli/stubs/deploy/Dockerfile.bun.stub +52 -0
  76. package/express/src/framework/maker-cli/stubs/deploy/Dockerfile.pnpm.stub +55 -0
  77. package/express/src/framework/maker-cli/stubs/deploy/Dockerfile.stub +51 -0
  78. package/express/src/framework/maker-cli/stubs/deploy/Dockerfile.yarn.stub +55 -0
  79. package/express/src/framework/maker-cli/stubs/deploy/README.stub +59 -0
  80. package/express/src/framework/maker-cli/stubs/deploy/compose/mysql.server.stub +45 -0
  81. package/express/src/framework/maker-cli/stubs/deploy/compose/postgres.server.stub +45 -0
  82. package/express/src/framework/maker-cli/stubs/deploy/compose/sqlite.stub +45 -0
  83. package/express/src/framework/maker-cli/stubs/deploy/env/mysql.server.stub +68 -0
  84. package/express/src/framework/maker-cli/stubs/deploy/env/postgres.server.stub +68 -0
  85. package/express/src/framework/maker-cli/stubs/deploy/env/sqlite.stub +67 -0
  86. package/express/src/framework/maker-cli/stubs/deploy/scripts/auto-migrate.sh.stub +41 -0
  87. package/express/src/framework/maker-cli/stubs/deploy/server/README.stub +78 -0
  88. package/express/src/framework/maker-cli/stubs/deploy/server/compose/noredis.stub +120 -0
  89. package/express/src/framework/maker-cli/stubs/deploy/server/compose/redis.dev.stub +133 -0
  90. package/express/src/framework/maker-cli/stubs/deploy/server/compose/redis.stub +131 -0
  91. package/express/src/framework/maker-cli/stubs/deploy/server/env/local.example.stub +11 -0
  92. package/express/src/framework/maker-cli/stubs/deploy/server/env/noredis.stub +25 -0
  93. package/express/src/framework/maker-cli/stubs/deploy/server/env/redis.stub +25 -0
  94. package/express/src/framework/maker-cli/stubs/deploy/server/nginx-vhost/README.stub +16 -0
  95. package/express/src/framework/maker-cli/stubs/deploy/server/nginx-vhost/app.example.com.stub +13 -0
  96. package/express/src/framework/maker-cli/stubs/deploy/server/pgadmin/servers.stub +14 -0
  97. package/express/src/framework/maker-cli/stubs/deploy/server/redis/redis.conf.stub +7 -0
  98. package/express/src/framework/maker-cli/stubs/deploy/supervisor/noredis.stub +54 -0
  99. package/express/src/framework/maker-cli/stubs/deploy/supervisor/redis.stub +70 -0
  100. package/express/src/framework/maker-cli/stubs/deploy/workflow/local.json.stub +25 -0
  101. package/express/src/framework/maker-cli/stubs/deploy/workflow/remote.json.stub +24 -0
  102. package/express/src/framework/maker-cli/stubs/example/console.ts.stub +34 -0
  103. package/express/src/framework/maker-cli/stubs/example/controller.ts.stub +418 -0
  104. package/express/src/framework/maker-cli/stubs/example/job.ts.stub +75 -0
  105. package/express/src/framework/maker-cli/stubs/example/route.api.ts.stub +207 -0
  106. package/express/src/framework/maker-cli/stubs/example/route.plain.ts.stub +18 -0
  107. package/express/src/framework/maker-cli/stubs/example/schema.plain.ts.stub +42 -0
  108. package/express/src/framework/maker-cli/stubs/example/schema.ts.stub +42 -0
  109. package/express/src/framework/maker-cli/stubs/job/name.ts.stub +25 -0
  110. package/express/src/framework/maker-cli/stubs/middleware/name.ts.stub +14 -0
  111. package/express/src/framework/maker-cli/stubs/model/name.mysql.ts.stub +9 -0
  112. package/express/src/framework/maker-cli/stubs/model/name.postgresql.ts.stub +9 -0
  113. package/express/src/framework/maker-cli/stubs/model/name.sqlite.ts.stub +9 -0
  114. package/express/src/framework/maker-cli/stubs/notification/controller.ts.stub +77 -0
  115. package/express/src/framework/maker-cli/stubs/notification/job.ts.stub +13 -0
  116. package/express/src/framework/maker-cli/stubs/notification/route.api.ts.stub +50 -0
  117. package/express/src/framework/maker-cli/stubs/notification/route.plain.ts.stub +12 -0
  118. package/express/src/framework/maker-cli/stubs/notification/schema.plain.ts.stub +26 -0
  119. package/express/src/framework/maker-cli/stubs/notification/schema.ts.stub +26 -0
  120. package/express/src/framework/maker-cli/stubs/route/api.ts.stub +80 -0
  121. package/express/src/framework/maker-cli/stubs/route/plain.ts.stub +11 -0
  122. package/express/src/framework/maker-cli/stubs/schedule/name.ts.stub +36 -0
  123. package/express/src/framework/maker-cli/stubs/seeder/name.ts.stub +20 -0
  124. package/express/src/framework/maker-cli/stubs/test/unit.ts.stub +22 -0
  125. package/express/src/framework/maker-cli/utils/env-config.mjs +44 -0
  126. package/express/src/framework/maker-cli/utils/env-db.mjs +61 -0
  127. package/express/src/framework/maker-cli/utils/file-ops.mjs +30 -0
  128. package/express/src/framework/maker-cli/utils/flags.mjs +14 -0
  129. package/express/src/framework/maker-cli/utils/help.mjs +50 -0
  130. package/express/src/framework/maker-cli/utils/naming.mjs +13 -0
  131. package/express/src/framework/maker-cli/utils/process.mjs +52 -0
  132. package/express/src/framework/modules/discover.ts +54 -0
  133. package/express/src/framework/modules/routes.ts +21 -0
  134. package/express/src/framework/notification/index.ts +116 -0
  135. package/express/src/framework/queue/clear.ts +22 -0
  136. package/express/src/framework/queue/queue.ts +342 -0
  137. package/express/src/framework/queue/ui.ts +187 -0
  138. package/express/src/framework/queue/worker.ts +61 -0
  139. package/express/src/framework/realtime/broadcast.ts +46 -0
  140. package/express/src/framework/realtime/index.ts +2 -0
  141. package/express/src/framework/realtime/socket-cookie.ts +71 -0
  142. package/express/src/framework/realtime/socket.ts +119 -0
  143. package/express/src/framework/realtime/types.ts +6 -0
  144. package/express/src/framework/realtime/ui.ts +16 -0
  145. package/express/src/framework/redis/client.ts +151 -0
  146. package/express/src/framework/runtime/runtime.ts +23 -0
  147. package/express/src/framework/runtime/server-adapter.ts +34 -0
  148. package/express/src/framework/scheduler/lock.ts +116 -0
  149. package/express/src/framework/scheduler/run.ts +48 -0
  150. package/express/src/framework/scheduler/scheduler.ts +155 -0
  151. package/express/src/framework/server.ts +177 -0
  152. package/express/src/framework/session/session.ts +98 -0
  153. package/express/src/framework/storage/storage.ts +785 -0
  154. package/express/src/framework/support/cookie.ts +62 -0
  155. package/express/src/framework/support/jwt.ts +33 -0
  156. package/express/src/framework/support/lifecycle.ts +35 -0
  157. package/express/src/framework/support/logger.ts +148 -0
  158. package/express/src/framework/support/mail.ts +48 -0
  159. package/express/src/framework/support/password.ts +23 -0
  160. package/express/src/framework/support/upload.ts +160 -0
  161. package/express/src/framework/support/url.ts +41 -0
  162. package/express/src/middlewares/auth-middleware.ts +98 -0
  163. package/express/src/middlewares/role-middleware.ts +25 -0
  164. package/express/src/modules/auth/controllers/auth.controller.ts +358 -0
  165. package/express/src/modules/auth/controllers/auth.helpers.ts +104 -0
  166. package/express/src/modules/auth/controllers/auth.schema.ts +102 -0
  167. package/express/src/modules/auth/controllers/role.controller.ts +15 -0
  168. package/express/src/modules/auth/database/models/notifications.ts +24 -0
  169. package/express/src/modules/auth/database/models/role.ts +14 -0
  170. package/express/src/modules/auth/database/models/user.ts +46 -0
  171. package/express/src/modules/auth/database/seeders/role.ts +18 -0
  172. package/express/src/modules/auth/database/seeders/user.ts +35 -0
  173. package/express/src/modules/auth/jobs/forgetpass.ts +20 -0
  174. package/express/src/modules/auth/jobs/registeruser.ts +23 -0
  175. package/express/src/modules/auth/jobs/verifyemail.ts +18 -0
  176. package/express/src/modules/auth/routes/api.ts +150 -0
  177. package/express/src/modules/auth/routes/role.ts +33 -0
  178. package/express/src/resources/index.html +17 -0
  179. package/express/src/resources/src/App.vue +5 -0
  180. package/express/src/resources/src/assets/css/styles.css +14761 -0
  181. package/express/src/resources/src/assets/css/styles.css.map +1 -0
  182. package/express/src/resources/src/assets/fonts/public-sans/public-sans-latin-ext.woff2 +0 -0
  183. package/express/src/resources/src/assets/fonts/public-sans/public-sans-latin.woff2 +0 -0
  184. package/express/src/resources/src/assets/images/favicon/favicon.ico +0 -0
  185. package/express/src/resources/src/assets/images/logo.png +0 -0
  186. package/express/src/resources/src/assets/scss/custom.css +217 -0
  187. package/express/src/resources/src/assets/scss/custom.css.map +1 -0
  188. package/express/src/resources/src/assets/scss/custom.scss +1346 -0
  189. package/express/src/resources/src/components/Button.vue +32 -0
  190. package/express/src/resources/src/components/Checkbox.vue +29 -0
  191. package/express/src/resources/src/components/Datepicker.vue +106 -0
  192. package/express/src/resources/src/components/FeatureButton.vue +53 -0
  193. package/express/src/resources/src/components/FloatButton.vue +33 -0
  194. package/express/src/resources/src/components/Href.vue +32 -0
  195. package/express/src/resources/src/components/Input.vue +223 -0
  196. package/express/src/resources/src/components/InputGroup.vue +145 -0
  197. package/express/src/resources/src/components/InputPasswordToggle.vue +213 -0
  198. package/express/src/resources/src/components/Modal.vue +55 -0
  199. package/express/src/resources/src/components/Pagebar.vue +26 -0
  200. package/express/src/resources/src/components/Select.vue +384 -0
  201. package/express/src/resources/src/components/Spinner.vue +42 -0
  202. package/express/src/resources/src/components/Switch.vue +65 -0
  203. package/express/src/resources/src/components/TextArea.vue +117 -0
  204. package/express/src/resources/src/components/Toast.vue +56 -0
  205. package/express/src/resources/src/components/datatable/DataTableSkeleton.vue +98 -0
  206. package/express/src/resources/src/components/datatable/Pagination.vue +147 -0
  207. package/express/src/resources/src/components/datatable/SelectOpption.vue +64 -0
  208. package/express/src/resources/src/components/datatable/index.vue +199 -0
  209. package/express/src/resources/src/composables/useAuth.ts +52 -0
  210. package/express/src/resources/src/helpers/nformatter.ts +10 -0
  211. package/express/src/resources/src/helpers/utils.ts +75 -0
  212. package/express/src/resources/src/layouts/AuthLayout.vue +20 -0
  213. package/express/src/resources/src/layouts/Layout/Footer.vue +23 -0
  214. package/express/src/resources/src/layouts/Layout/Header.vue +105 -0
  215. package/express/src/resources/src/layouts/Layout/Sidebar.vue +137 -0
  216. package/express/src/resources/src/layouts/Layout/index.vue +87 -0
  217. package/express/src/resources/src/main.ts +26 -0
  218. package/express/src/resources/src/pages/auth/forgetPassword.vue +82 -0
  219. package/express/src/resources/src/pages/auth/login.vue +99 -0
  220. package/express/src/resources/src/pages/auth/register.vue +133 -0
  221. package/express/src/resources/src/pages/auth/resetPassword.vue +118 -0
  222. package/express/src/resources/src/pages/auth/verifyEmail.vue +68 -0
  223. package/express/src/resources/src/pages/dashboard/index.vue +66 -0
  224. package/express/src/resources/src/plugins/axios.ts +31 -0
  225. package/express/src/resources/src/plugins/browserDetect.ts +55 -0
  226. package/express/src/resources/src/plugins/dialog.ts +216 -0
  227. package/express/src/resources/src/plugins/gum.ts +367 -0
  228. package/express/src/resources/src/plugins/pulse.ts +147 -0
  229. package/express/src/resources/src/plugins/routeProgress.ts +87 -0
  230. package/express/src/resources/src/router/index.ts +85 -0
  231. package/express/src/resources/src/stores/admin-ui.ts +148 -0
  232. package/express/src/resources/src/stores/auth.ts +164 -0
  233. package/express/src/resources/tsconfig.json +19 -0
  234. package/express/src/resources/vite.config.ts +46 -0
  235. package/express/src/storage/app/private/.gitkeep +0 -0
  236. package/express/src/storage/app/public/.gitkeep +0 -0
  237. package/express/src/storage/app/tmp/generated/.gitkeep +0 -0
  238. package/express/src/storage/database/nexwire.sqlite +0 -0
  239. package/express/src/storage/logs/.gitkeep +0 -0
  240. package/express/src/storage/tmp/.gitkeep +0 -0
  241. package/express/src/storage/trash/modules/.gitkeep +0 -0
  242. package/express/src/types/global.d.ts +26 -0
  243. package/express/tsconfig.json +19 -0
  244. package/express/vitest.config.ts +23 -0
  245. package/hono/.dockerignore +14 -0
  246. package/hono/.env +44 -0
  247. package/hono/.env.example +42 -0
  248. package/hono/.vscode/settings.json +26 -0
  249. package/hono/LICENSE +21 -0
  250. package/hono/README.md +211 -0
  251. package/hono/biome.json +76 -0
  252. package/hono/drizzle.config.ts +25 -0
  253. package/hono/gitignore-stub +24 -0
  254. package/hono/package.json +99 -0
  255. package/hono/public/.gitkeep +0 -0
  256. package/hono/src/config/app.ts +41 -0
  257. package/hono/src/config/auth.ts +12 -0
  258. package/hono/src/config/cache.ts +16 -0
  259. package/hono/src/config/cookie.ts +15 -0
  260. package/hono/src/config/cors.ts +12 -0
  261. package/hono/src/config/database.ts +29 -0
  262. package/hono/src/config/index.ts +83 -0
  263. package/hono/src/config/jwt.ts +19 -0
  264. package/hono/src/config/logging.ts +13 -0
  265. package/hono/src/config/mail.ts +24 -0
  266. package/hono/src/config/openapi.ts +54 -0
  267. package/hono/src/config/queue.ts +30 -0
  268. package/hono/src/config/rateLimit.ts +17 -0
  269. package/hono/src/config/realtime.ts +15 -0
  270. package/hono/src/config/redis.ts +18 -0
  271. package/hono/src/config/security.ts +44 -0
  272. package/hono/src/config/session.ts +17 -0
  273. package/hono/src/config/storage.ts +22 -0
  274. package/hono/src/config/validate.ts +40 -0
  275. package/hono/src/database/migrations/sqlite/0000_init.sql +61 -0
  276. package/hono/src/database/migrations/sqlite/meta/0000_snapshot.json +412 -0
  277. package/hono/src/database/migrations/sqlite/meta/_journal.json +13 -0
  278. package/hono/src/database/schema.ts +3 -0
  279. package/hono/src/env.ts +76 -0
  280. package/hono/src/framework/cache/cache.ts +81 -0
  281. package/hono/src/framework/circuit-breaker/cb.ts +60 -0
  282. package/hono/src/framework/database/connection.ts +196 -0
  283. package/hono/src/framework/database/migrate-hooks.ts +87 -0
  284. package/hono/src/framework/database/paginate.ts +253 -0
  285. package/hono/src/framework/database/schema.ts +20 -0
  286. package/hono/src/framework/database/seed.ts +139 -0
  287. package/hono/src/framework/events/dispatcher.ts +49 -0
  288. package/hono/src/framework/facade.ts +29 -0
  289. package/hono/src/framework/http/app.ts +109 -0
  290. package/hono/src/framework/http/cors.ts +19 -0
  291. package/hono/src/framework/http/logger.ts +81 -0
  292. package/hono/src/framework/http/metrics.ts +22 -0
  293. package/hono/src/framework/http/openapi.ts +42 -0
  294. package/hono/src/framework/http/ratelimiter.ts +172 -0
  295. package/hono/src/framework/http/router.ts +76 -0
  296. package/hono/src/framework/http/security.ts +23 -0
  297. package/hono/src/framework/http/static.ts +70 -0
  298. package/hono/src/framework/http/validation.ts +21 -0
  299. package/hono/src/framework/kernel.ts +48 -0
  300. package/hono/src/framework/maker-cli/db/core.mjs +288 -0
  301. package/hono/src/framework/maker-cli/db/index.mjs +204 -0
  302. package/hono/src/framework/maker-cli/deploy/core.mjs +1038 -0
  303. package/hono/src/framework/maker-cli/deploy/index.mjs +91 -0
  304. package/hono/src/framework/maker-cli/index.mjs +53 -0
  305. package/hono/src/framework/maker-cli/middleware/core.mjs +47 -0
  306. package/hono/src/framework/maker-cli/middleware/index.mjs +12 -0
  307. package/hono/src/framework/maker-cli/module/core.mjs +684 -0
  308. package/hono/src/framework/maker-cli/module/index.mjs +140 -0
  309. package/hono/src/framework/maker-cli/runtime/build-ui.mjs +17 -0
  310. package/hono/src/framework/maker-cli/runtime/core.mjs +543 -0
  311. package/hono/src/framework/maker-cli/runtime/index.mjs +104 -0
  312. package/hono/src/framework/maker-cli/runtime/redis-commander-patch.cjs +20 -0
  313. package/hono/src/framework/maker-cli/stubs/controller/openapi.ts.stub +57 -0
  314. package/hono/src/framework/maker-cli/stubs/controller/openapi.with-model.ts.stub +58 -0
  315. package/hono/src/framework/maker-cli/stubs/controller/plain.ts.stub +59 -0
  316. package/hono/src/framework/maker-cli/stubs/controller/schema.plain.ts.stub +14 -0
  317. package/hono/src/framework/maker-cli/stubs/controller/schema.ts.stub +33 -0
  318. package/hono/src/framework/maker-cli/stubs/deploy/Dockerfile.bun.stub +52 -0
  319. package/hono/src/framework/maker-cli/stubs/deploy/Dockerfile.pnpm.stub +55 -0
  320. package/hono/src/framework/maker-cli/stubs/deploy/Dockerfile.stub +51 -0
  321. package/hono/src/framework/maker-cli/stubs/deploy/Dockerfile.yarn.stub +55 -0
  322. package/hono/src/framework/maker-cli/stubs/deploy/README.stub +59 -0
  323. package/hono/src/framework/maker-cli/stubs/deploy/compose/mysql.server.stub +45 -0
  324. package/hono/src/framework/maker-cli/stubs/deploy/compose/postgres.server.stub +45 -0
  325. package/hono/src/framework/maker-cli/stubs/deploy/compose/sqlite.stub +45 -0
  326. package/hono/src/framework/maker-cli/stubs/deploy/env/mysql.server.stub +68 -0
  327. package/hono/src/framework/maker-cli/stubs/deploy/env/postgres.server.stub +68 -0
  328. package/hono/src/framework/maker-cli/stubs/deploy/env/sqlite.stub +67 -0
  329. package/hono/src/framework/maker-cli/stubs/deploy/scripts/auto-migrate.sh.stub +41 -0
  330. package/hono/src/framework/maker-cli/stubs/deploy/server/README.stub +78 -0
  331. package/hono/src/framework/maker-cli/stubs/deploy/server/compose/noredis.stub +120 -0
  332. package/hono/src/framework/maker-cli/stubs/deploy/server/compose/redis.dev.stub +133 -0
  333. package/hono/src/framework/maker-cli/stubs/deploy/server/compose/redis.stub +131 -0
  334. package/hono/src/framework/maker-cli/stubs/deploy/server/env/local.example.stub +11 -0
  335. package/hono/src/framework/maker-cli/stubs/deploy/server/env/noredis.stub +25 -0
  336. package/hono/src/framework/maker-cli/stubs/deploy/server/env/redis.stub +25 -0
  337. package/hono/src/framework/maker-cli/stubs/deploy/server/nginx-vhost/README.stub +16 -0
  338. package/hono/src/framework/maker-cli/stubs/deploy/server/nginx-vhost/app.example.com.stub +13 -0
  339. package/hono/src/framework/maker-cli/stubs/deploy/server/pgadmin/servers.stub +14 -0
  340. package/hono/src/framework/maker-cli/stubs/deploy/server/redis/redis.conf.stub +7 -0
  341. package/hono/src/framework/maker-cli/stubs/deploy/supervisor/noredis.stub +54 -0
  342. package/hono/src/framework/maker-cli/stubs/deploy/supervisor/redis.stub +70 -0
  343. package/hono/src/framework/maker-cli/stubs/deploy/workflow/local.json.stub +25 -0
  344. package/hono/src/framework/maker-cli/stubs/deploy/workflow/remote.json.stub +24 -0
  345. package/hono/src/framework/maker-cli/stubs/example/console.ts.stub +34 -0
  346. package/hono/src/framework/maker-cli/stubs/example/controller.ts.stub +505 -0
  347. package/hono/src/framework/maker-cli/stubs/example/job.ts.stub +75 -0
  348. package/hono/src/framework/maker-cli/stubs/example/route.api.ts.stub +207 -0
  349. package/hono/src/framework/maker-cli/stubs/example/route.plain.ts.stub +18 -0
  350. package/hono/src/framework/maker-cli/stubs/example/schema.plain.ts.stub +42 -0
  351. package/hono/src/framework/maker-cli/stubs/example/schema.ts.stub +42 -0
  352. package/hono/src/framework/maker-cli/stubs/job/name.ts.stub +25 -0
  353. package/hono/src/framework/maker-cli/stubs/middleware/name.ts.stub +14 -0
  354. package/hono/src/framework/maker-cli/stubs/model/name.mysql.ts.stub +9 -0
  355. package/hono/src/framework/maker-cli/stubs/model/name.postgresql.ts.stub +9 -0
  356. package/hono/src/framework/maker-cli/stubs/model/name.sqlite.ts.stub +9 -0
  357. package/hono/src/framework/maker-cli/stubs/notification/controller.ts.stub +86 -0
  358. package/hono/src/framework/maker-cli/stubs/notification/job.ts.stub +13 -0
  359. package/hono/src/framework/maker-cli/stubs/notification/route.api.ts.stub +50 -0
  360. package/hono/src/framework/maker-cli/stubs/notification/route.plain.ts.stub +12 -0
  361. package/hono/src/framework/maker-cli/stubs/notification/schema.plain.ts.stub +26 -0
  362. package/hono/src/framework/maker-cli/stubs/notification/schema.ts.stub +26 -0
  363. package/hono/src/framework/maker-cli/stubs/route/api.ts.stub +80 -0
  364. package/hono/src/framework/maker-cli/stubs/route/plain.ts.stub +11 -0
  365. package/hono/src/framework/maker-cli/stubs/schedule/name.ts.stub +36 -0
  366. package/hono/src/framework/maker-cli/stubs/seeder/name.ts.stub +20 -0
  367. package/hono/src/framework/maker-cli/stubs/test/unit.ts.stub +22 -0
  368. package/hono/src/framework/maker-cli/utils/env-config.mjs +44 -0
  369. package/hono/src/framework/maker-cli/utils/env-db.mjs +61 -0
  370. package/hono/src/framework/maker-cli/utils/file-ops.mjs +30 -0
  371. package/hono/src/framework/maker-cli/utils/flags.mjs +14 -0
  372. package/hono/src/framework/maker-cli/utils/help.mjs +50 -0
  373. package/hono/src/framework/maker-cli/utils/naming.mjs +13 -0
  374. package/hono/src/framework/maker-cli/utils/process.mjs +52 -0
  375. package/hono/src/framework/modules/discover.ts +54 -0
  376. package/hono/src/framework/modules/routes.ts +24 -0
  377. package/hono/src/framework/notification/index.ts +116 -0
  378. package/hono/src/framework/queue/clear.ts +22 -0
  379. package/hono/src/framework/queue/queue.ts +342 -0
  380. package/hono/src/framework/queue/ui.ts +214 -0
  381. package/hono/src/framework/queue/worker.ts +61 -0
  382. package/hono/src/framework/realtime/broadcast.ts +46 -0
  383. package/hono/src/framework/realtime/index.ts +2 -0
  384. package/hono/src/framework/realtime/socket-cookie.ts +68 -0
  385. package/hono/src/framework/realtime/socket.ts +196 -0
  386. package/hono/src/framework/realtime/types.ts +6 -0
  387. package/hono/src/framework/realtime/ui.ts +16 -0
  388. package/hono/src/framework/redis/client.ts +151 -0
  389. package/hono/src/framework/runtime/runtime.ts +23 -0
  390. package/hono/src/framework/runtime/server-adapter.ts +94 -0
  391. package/hono/src/framework/scheduler/lock.ts +116 -0
  392. package/hono/src/framework/scheduler/run.ts +48 -0
  393. package/hono/src/framework/scheduler/scheduler.ts +155 -0
  394. package/hono/src/framework/server.ts +183 -0
  395. package/hono/src/framework/session/session.ts +116 -0
  396. package/hono/src/framework/storage/storage.ts +785 -0
  397. package/hono/src/framework/support/cookie.ts +99 -0
  398. package/hono/src/framework/support/jwt.ts +45 -0
  399. package/hono/src/framework/support/lifecycle.ts +35 -0
  400. package/hono/src/framework/support/logger.ts +148 -0
  401. package/hono/src/framework/support/mail.ts +48 -0
  402. package/hono/src/framework/support/password.ts +23 -0
  403. package/hono/src/framework/support/url.ts +41 -0
  404. package/hono/src/middlewares/auth-middleware.ts +98 -0
  405. package/hono/src/middlewares/role-middleware.ts +25 -0
  406. package/hono/src/modules/auth/controllers/auth.controller.ts +413 -0
  407. package/hono/src/modules/auth/controllers/auth.helpers.ts +128 -0
  408. package/hono/src/modules/auth/controllers/auth.schema.ts +102 -0
  409. package/hono/src/modules/auth/controllers/role.controller.ts +25 -0
  410. package/hono/src/modules/auth/database/models/notifications.ts +24 -0
  411. package/hono/src/modules/auth/database/models/role.ts +14 -0
  412. package/hono/src/modules/auth/database/models/user.ts +46 -0
  413. package/hono/src/modules/auth/database/seeders/role.ts +18 -0
  414. package/hono/src/modules/auth/database/seeders/user.ts +35 -0
  415. package/hono/src/modules/auth/jobs/forgetpass.ts +20 -0
  416. package/hono/src/modules/auth/jobs/registeruser.ts +23 -0
  417. package/hono/src/modules/auth/jobs/verifyemail.ts +18 -0
  418. package/hono/src/modules/auth/routes/api.ts +150 -0
  419. package/hono/src/modules/auth/routes/role.ts +33 -0
  420. package/hono/src/resources/index.html +17 -0
  421. package/hono/src/resources/src/App.vue +5 -0
  422. package/hono/src/resources/src/assets/css/styles.css +14913 -0
  423. package/hono/src/resources/src/assets/css/styles.css.map +1 -0
  424. package/hono/src/resources/src/assets/fonts/public-sans/public-sans-latin-ext.woff2 +0 -0
  425. package/hono/src/resources/src/assets/fonts/public-sans/public-sans-latin.woff2 +0 -0
  426. package/hono/src/resources/src/assets/images/favicon/favicon.ico +0 -0
  427. package/hono/src/resources/src/assets/images/logo.png +0 -0
  428. package/hono/src/resources/src/assets/scss/custom.css +217 -0
  429. package/hono/src/resources/src/assets/scss/custom.css.map +1 -0
  430. package/hono/src/resources/src/assets/scss/custom.scss +1346 -0
  431. package/hono/src/resources/src/components/Button.vue +32 -0
  432. package/hono/src/resources/src/components/Checkbox.vue +29 -0
  433. package/hono/src/resources/src/components/Datepicker.vue +106 -0
  434. package/hono/src/resources/src/components/FeatureButton.vue +53 -0
  435. package/hono/src/resources/src/components/FloatButton.vue +33 -0
  436. package/hono/src/resources/src/components/Href.vue +32 -0
  437. package/hono/src/resources/src/components/Input.vue +234 -0
  438. package/hono/src/resources/src/components/InputGroup.vue +149 -0
  439. package/hono/src/resources/src/components/InputPasswordToggle.vue +222 -0
  440. package/hono/src/resources/src/components/Modal.vue +55 -0
  441. package/hono/src/resources/src/components/Pagebar.vue +26 -0
  442. package/hono/src/resources/src/components/Select.vue +415 -0
  443. package/hono/src/resources/src/components/Spinner.vue +42 -0
  444. package/hono/src/resources/src/components/Switch.vue +65 -0
  445. package/hono/src/resources/src/components/TextArea.vue +121 -0
  446. package/hono/src/resources/src/components/Toast.vue +56 -0
  447. package/hono/src/resources/src/components/datatable/DataTableSkeleton.vue +98 -0
  448. package/hono/src/resources/src/components/datatable/Pagination.vue +147 -0
  449. package/hono/src/resources/src/components/datatable/SelectOpption.vue +64 -0
  450. package/hono/src/resources/src/components/datatable/index.vue +199 -0
  451. package/hono/src/resources/src/composables/useAuth.ts +52 -0
  452. package/hono/src/resources/src/helpers/nformatter.ts +10 -0
  453. package/hono/src/resources/src/helpers/utils.ts +75 -0
  454. package/hono/src/resources/src/layouts/AuthLayout.vue +20 -0
  455. package/hono/src/resources/src/layouts/Layout/Footer.vue +23 -0
  456. package/hono/src/resources/src/layouts/Layout/Header.vue +105 -0
  457. package/hono/src/resources/src/layouts/Layout/Sidebar.vue +137 -0
  458. package/hono/src/resources/src/layouts/Layout/index.vue +87 -0
  459. package/hono/src/resources/src/main.ts +26 -0
  460. package/hono/src/resources/src/pages/auth/forgetPassword.vue +82 -0
  461. package/hono/src/resources/src/pages/auth/login.vue +99 -0
  462. package/hono/src/resources/src/pages/auth/register.vue +133 -0
  463. package/hono/src/resources/src/pages/auth/resetPassword.vue +118 -0
  464. package/hono/src/resources/src/pages/auth/verifyEmail.vue +68 -0
  465. package/hono/src/resources/src/pages/dashboard/index.vue +66 -0
  466. package/hono/src/resources/src/plugins/axios.ts +31 -0
  467. package/hono/src/resources/src/plugins/browserDetect.ts +55 -0
  468. package/hono/src/resources/src/plugins/dialog.ts +216 -0
  469. package/hono/src/resources/src/plugins/gum.ts +367 -0
  470. package/hono/src/resources/src/plugins/pulse.ts +147 -0
  471. package/hono/src/resources/src/plugins/routeProgress.ts +87 -0
  472. package/hono/src/resources/src/router/index.ts +85 -0
  473. package/hono/src/resources/src/stores/admin-ui.ts +148 -0
  474. package/hono/src/resources/src/stores/auth.ts +164 -0
  475. package/hono/src/resources/tsconfig.json +19 -0
  476. package/hono/src/resources/vite.config.ts +46 -0
  477. package/hono/src/storage/app/private/.gitkeep +0 -0
  478. package/hono/src/storage/app/public/.gitkeep +0 -0
  479. package/hono/src/storage/app/tmp/generated/.gitkeep +0 -0
  480. package/hono/src/storage/database/nexwire.sqlite +0 -0
  481. package/hono/src/storage/logs/.gitkeep +0 -0
  482. package/hono/src/storage/tmp/.gitkeep +0 -0
  483. package/hono/src/storage/trash/modules/.gitkeep +0 -0
  484. package/hono/src/types/global.d.ts +26 -0
  485. package/hono/tsconfig.json +20 -0
  486. package/hono/vitest.config.ts +23 -0
  487. package/package.json +238 -0
  488. package/src/index.js +204 -0
@@ -0,0 +1,342 @@
1
+ import { type Job, type JobsOptions, Queue, QueueEvents, Worker } from "bullmq";
2
+ import { type DurableContext, type DurableJob, DurableWorker, RedisStateStore } from "bullmq-durable";
3
+ import { queueConfig } from "@/config/index.js";
4
+ import { discoverModuleFiles, importFile } from "@/framework/modules/discover.js";
5
+ import { redisClientIfReady } from "@/framework/redis/client.js";
6
+
7
+ type QueueJob = {
8
+ name: string;
9
+ data: any;
10
+ };
11
+
12
+ type QueueHandler = (job: Job) => Promise<any>;
13
+
14
+ type DurableQueueHandler = (job: DurableJob, ctx: DurableContext) => Promise<any>;
15
+
16
+ type AnyQueueHandler = QueueHandler | DurableQueueHandler;
17
+
18
+ const queues = new Map<string, Queue>();
19
+ const events = new Map<string, QueueEvents>();
20
+ const handlers = new Map<string, QueueHandler>();
21
+ const durableHandlers = new Map<string, DurableQueueHandler>();
22
+ const workers: Worker[] = [];
23
+ let durableStateStore: RedisStateStore | null = null;
24
+
25
+ function queuePrefix() {
26
+ return queueConfig.prefix;
27
+ }
28
+
29
+ function key(queue: string, job: string) {
30
+ return `${queue}:${job}`;
31
+ }
32
+
33
+ /**
34
+ * Why: Registers a job handler for a queue + job name.
35
+ * When: Module job files are loaded during boot.
36
+ * Where: Module job definition files under `src/modules/<module>/jobs`.
37
+ * How: Stores handler in an in-memory lookup key. Pass `{ durable: true }` to
38
+ * register a checkpointed handler `(job, ctx)` powered by bullmq-durable;
39
+ * such a handler can split its work into `ctx.step(...)` checkpoints that
40
+ * survive crashes/restarts instead of re-running from scratch.
41
+ */
42
+ export function shouldQueue(job: string, queue: string, handler: AnyQueueHandler, options: { durable?: boolean } = {}) {
43
+ const target = key(queue || "default", job);
44
+ if (options.durable) {
45
+ durableHandlers.set(target, handler as DurableQueueHandler);
46
+ } else {
47
+ handlers.set(target, handler as QueueHandler);
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Why: Gets or lazily creates a BullMQ queue instance.
53
+ * When: Enqueueing jobs or ensuring queue resources.
54
+ * Where: Queue facade and dispatcher.
55
+ * How: Reuses shared Redis connection and memoized queue map.
56
+ */
57
+ export function getQueue(queue = "default") {
58
+ const client = redisClientIfReady();
59
+ if (!client) return null;
60
+
61
+ if (!queues.has(queue)) {
62
+ queues.set(
63
+ queue,
64
+ new Queue(queue, {
65
+ connection: client as any,
66
+ prefix: queuePrefix(),
67
+ defaultJobOptions: {
68
+ attempts: 3,
69
+ backoff: { type: "exponential", delay: 3000 },
70
+ removeOnComplete: 1000,
71
+ removeOnFail: 5000
72
+ }
73
+ })
74
+ );
75
+ }
76
+
77
+ return queues.get(queue)!;
78
+ }
79
+
80
+ /**
81
+ * Why: Provides the shared Redis-backed state store for durable queues.
82
+ * When: Durable queue/worker construction.
83
+ * Where: Queue facade internals.
84
+ * How: Creates one `RedisStateStore` reusing the shared Redis client so durable
85
+ * state lives in Redis without opening extra connections.
86
+ */
87
+ function durableStateStoreIfReady() {
88
+ const client = redisClientIfReady();
89
+ if (!client) return null;
90
+
91
+ if (!durableStateStore) {
92
+ durableStateStore = new RedisStateStore({ connection: client as never, prefix: queueConfig.durablePrefix });
93
+ }
94
+ return durableStateStore;
95
+ }
96
+
97
+ /**
98
+ * Why: Facade-friendly alias for getQueue.
99
+ * When: Consumers want fluent queue access.
100
+ * Where: Framework facade/API usage.
101
+ * How: Delegates directly to getQueue.
102
+ */
103
+ export function queue(queueName = "default") {
104
+ return getQueue(queueName);
105
+ }
106
+
107
+ /**
108
+ * Why: Ensures configured queues exist before UI/runtime usage.
109
+ * When: Queue dashboard setup or startup warmup.
110
+ * Where: Queue UI/bootstrap code.
111
+ * How: Iterates names and resolves queue instances.
112
+ */
113
+ export function ensureQueues(queueNames: string[]) {
114
+ for (const queueName of queueNames) {
115
+ getQueue(queueName);
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Why: Returns all created queue instances.
121
+ * When: Queue introspection and dashboard binding.
122
+ * Where: Queue dashboard setup code.
123
+ * How: Converts internal queue map to array.
124
+ */
125
+ export function getAllQueues() {
126
+ return Array.from(queues.values());
127
+ }
128
+
129
+ /**
130
+ * Why: Adds a job to BullMQ with project defaults.
131
+ * When: Commands/events need background execution.
132
+ * Where: Dispatcher and feature modules.
133
+ * How: Resolves queue then calls add with retry/backoff options.
134
+ */
135
+ export async function queueJob(
136
+ job: string,
137
+ data: any,
138
+ options: {
139
+ queue?: string;
140
+ delay?: number;
141
+ attempts?: number;
142
+ jobId?: string;
143
+ priority?: number;
144
+ removeOnComplete?: JobsOptions["removeOnComplete"];
145
+ removeOnFail?: JobsOptions["removeOnFail"];
146
+ backoff?: JobsOptions["backoff"];
147
+ } = {}
148
+ ) {
149
+ const queueName = options.queue || "default";
150
+ const queue = getQueue(queueName);
151
+ if (!queue) return null;
152
+
153
+ return await queue.add(job, data, {
154
+ delay: options.delay ? options.delay * 1000 : 0,
155
+ attempts: options.attempts ?? 3,
156
+ jobId: options.jobId,
157
+ priority: options.priority,
158
+ removeOnComplete: options.removeOnComplete ?? 1000,
159
+ removeOnFail: options.removeOnFail ?? 5000,
160
+ backoff: options.backoff ?? { type: "exponential", delay: 3000 }
161
+ });
162
+ }
163
+
164
+ /**
165
+ * Why: Loads module job files so handlers are registered.
166
+ * When: Worker start and explicit queue boot flows.
167
+ * Where: Worker/runtime bootstrap.
168
+ * How: Discovers jobs glob and imports each file.
169
+ */
170
+ export async function bootQueueJobs() {
171
+ const files = await discoverModuleFiles("**/jobs/*.{ts,js}");
172
+ for (const file of files) await importFile(file);
173
+ return files.length;
174
+ }
175
+
176
+ /**
177
+ * Why: Deletes phantom queues whose names are neither configured nor worked by
178
+ * this process.
179
+ * When: Queue worker boot, after workers start.
180
+ * Where: Worker runtime bootstrap.
181
+ * How: Scans Redis for `{prefix}*:meta` keys, and for each queue name that is
182
+ * not in queueConfig.queues nor in the queues this worker was launched
183
+ * with (--queue), obliterates it only when it holds no live jobs
184
+ * (waiting/active/delayed/paused/prioritized/waiting-children). Historical
185
+ * completed/failed entries are ignored so leftover history cannot keep a
186
+ * dead queue alive. Recreates nothing permanent: BullMQ re-creates a
187
+ * queue's keys on demand if any code path still dispatches to it.
188
+ */
189
+ export async function pruneStaleQueues(protectedQueues: string[] = []) {
190
+ if (!queueConfig.autoPruneQueues) return;
191
+
192
+ const client = redisClientIfReady();
193
+ if (!client) return;
194
+
195
+ const configured = new Set([...queueConfig.queues, ...protectedQueues]);
196
+ const prefix = `${queuePrefix()}:`;
197
+ const candidates = new Set<string>();
198
+
199
+ try {
200
+ let cursor = "0";
201
+ do {
202
+ const [nextCursor, keys] = await client.scan(cursor, "MATCH", `${prefix}*:meta`, "COUNT", 100);
203
+ cursor = nextCursor;
204
+ for (const key of keys) {
205
+ const name = key.slice(prefix.length, -5);
206
+ if (name && !configured.has(name)) candidates.add(name);
207
+ }
208
+ } while (cursor !== "0");
209
+
210
+ for (const name of candidates) {
211
+ try {
212
+ const queue = getQueue(name);
213
+ if (!queue) continue;
214
+
215
+ const counts = await queue.getJobCounts("waiting", "active", "delayed", "paused", "prioritized", "waiting-children");
216
+ const live = Object.values(counts).reduce((sum, n) => sum + (Number(n) || 0), 0);
217
+ if (live !== 0) continue;
218
+
219
+ await queue.obliterate({ force: true });
220
+ console.log(`Pruned stale queue "${name}"`);
221
+ } catch (error: any) {
222
+ console.warn(`Could not prune stale queue "${name}": ${error?.message || error}`);
223
+ }
224
+ }
225
+ } catch (error: any) {
226
+ console.warn(`Could not scan queues for stale cleanup: ${error?.message || error}`);
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Why: Starts BullMQ workers for selected queues.
232
+ * When: Queue worker process boots.
233
+ * Where: Worker runtime entrypoint.
234
+ * How: Boots handlers, creates QueueEvents, and creates workers.
235
+ */
236
+ export async function startQueueWorker(queueNames = ["default"]) {
237
+ await bootQueueJobs();
238
+ const client = redisClientIfReady();
239
+ if (!client) throw new Error("Redis is required for queue workers");
240
+
241
+ for (const queueName of queueNames) {
242
+ if (!events.has(queueName)) {
243
+ events.set(queueName, new QueueEvents(queueName, { connection: client as any, prefix: queuePrefix() }));
244
+ }
245
+
246
+ const durable = [...durableHandlers.keys()].some((k) => k.startsWith(`${queueName}:`));
247
+
248
+ if (durable) {
249
+ const store = durableStateStoreIfReady();
250
+ if (!store) throw new Error("Redis is required for durable queue workers");
251
+
252
+ const map: Record<string, AnyQueueHandler> = {};
253
+ for (const [k, h] of handlers) {
254
+ if (k.startsWith(`${queueName}:`)) map[k.slice(queueName.length + 1)] = h;
255
+ }
256
+ for (const [k, h] of durableHandlers) {
257
+ if (k.startsWith(`${queueName}:`)) map[k.slice(queueName.length + 1)] = h;
258
+ }
259
+
260
+ const worker = new DurableWorker(queueName, map as never, {
261
+ connection: client as any,
262
+ prefix: queuePrefix(),
263
+ stateStore: store,
264
+ concurrency: 10
265
+ });
266
+
267
+ worker.on("completed", (job) => {
268
+ console.log(`[${new Date().toLocaleTimeString()}] Processed: ${job.name} (${queueName})`);
269
+ });
270
+
271
+ worker.on("failed", (job, error) => {
272
+ const jobName = job?.name ?? "unknown";
273
+ console.log(`[${new Date().toLocaleTimeString()}] Failed: ${jobName} (${queueName}) - ${error.message}`);
274
+ });
275
+
276
+ workers.push(worker as unknown as Worker);
277
+ continue;
278
+ }
279
+
280
+ const worker = new Worker(
281
+ queueName,
282
+ async (job) => {
283
+ console.log(`[${new Date().toLocaleTimeString()}] Processing: ${job.name} (${queueName})`);
284
+ const handler = handlers.get(key(queueName, job.name));
285
+ if (!handler) throw new Error(`No handler registered for ${queueName}:${job.name}`);
286
+ return await handler(job);
287
+ },
288
+ { connection: client as any, prefix: queuePrefix(), concurrency: 10 }
289
+ );
290
+
291
+ worker.on("completed", (job) => {
292
+ console.log(`[${new Date().toLocaleTimeString()}] Processed: ${job.name} (${queueName})`);
293
+ });
294
+
295
+ worker.on("failed", (job, error) => {
296
+ const jobName = job?.name ?? "unknown";
297
+ console.log(`[${new Date().toLocaleTimeString()}] Failed: ${jobName} (${queueName}) - ${error.message}`);
298
+ });
299
+
300
+ workers.push(worker);
301
+ }
302
+ }
303
+
304
+ /**
305
+ * Why: Hard-clears ALL queue-system state for reset workflows.
306
+ * When: Local cleanup and maintenance commands.
307
+ * Where: `queue:clear` command.
308
+ * How: Wipes every BullMQ queue key (including jobs, job schedulers, and
309
+ * repeat keys for queues not listed in `queues`, e.g. `demo`), plus the
310
+ * durable key space, by scanning each prefix and deleting in batches.
311
+ * Cache/session/rate-limit keys are intentionally left alone.
312
+ */
313
+ export async function clearQueue() {
314
+ const client = redisClientIfReady();
315
+ if (!client) return;
316
+
317
+ for (const prefix of [queuePrefix(), queueConfig.durablePrefix]) {
318
+ const stream = client.scanStream({ match: `${prefix}*`, count: 100 });
319
+ for await (const keys of stream) {
320
+ if (keys.length) await client.del(keys);
321
+ }
322
+ }
323
+ }
324
+
325
+ /**
326
+ * Why: Gracefully stops all workers/events/queues.
327
+ * When: Process shutdown.
328
+ * Where: Server/worker lifecycle hooks.
329
+ * How: Closes resources with Promise.allSettled and clears registries.
330
+ */
331
+ export async function stopQueueRuntime() {
332
+ await Promise.allSettled(workers.map((worker) => worker.close()));
333
+ workers.length = 0;
334
+
335
+ await Promise.allSettled(Array.from(events.values()).map((queueEvents) => queueEvents.close()));
336
+ events.clear();
337
+
338
+ await Promise.allSettled(Array.from(queues.values()).map((queue) => queue.close()));
339
+ queues.clear();
340
+
341
+ durableStateStore = null;
342
+ }
@@ -0,0 +1,187 @@
1
+ import { createBullBoard } from "@bull-board/api";
2
+ import { BullMQAdapter } from "@bull-board/api/bullMQAdapter";
3
+ import { ExpressAdapter } from "@bull-board/express";
4
+ import type { NextFunction, Request, Response } from "express";
5
+ import { queueConfig, redisConfig } from "@/config/index.js";
6
+ import { ensureQueues, getQueue } from "@/framework/queue/queue.js";
7
+ import { redisClientIfReady } from "@/framework/redis/client.js";
8
+ import { cookie } from "@/framework/support/cookie.js";
9
+ import { jwt } from "@/framework/support/jwt.js";
10
+ import { parseCsvOrFallback } from "@/framework/support/lifecycle.js";
11
+
12
+ const BASE_PATH = queueConfig.queueUi || "/queues";
13
+
14
+ const POLL_INTERVAL_MS = 5_000;
15
+
16
+ let pollTimer: ReturnType<typeof setInterval> | null = null;
17
+ let boardApp: any | null = null;
18
+ let syncInFlight = false;
19
+
20
+ const dashboardQueueNames = new Set<string>();
21
+
22
+ const serverAdapter = new ExpressAdapter();
23
+ serverAdapter.setBasePath(BASE_PATH);
24
+
25
+ const bullBoard = createBullBoard({
26
+ queues: [],
27
+ serverAdapter,
28
+ options: {
29
+ uiConfig: {
30
+ boardTitle: "Queue Dashboard"
31
+ }
32
+ }
33
+ });
34
+
35
+ export function stopQueueDashboard() {
36
+ if (pollTimer) {
37
+ clearInterval(pollTimer);
38
+ pollTimer = null;
39
+ }
40
+ }
41
+
42
+ async function refreshDashboardQueues() {
43
+ if (syncInFlight) return;
44
+ syncInFlight = true;
45
+
46
+ try {
47
+ const client = redisClientIfReady();
48
+ if (!client) return;
49
+
50
+ const discovered = (await discoverQueueNames()).sort();
51
+ ensureQueues(discovered);
52
+
53
+ for (const name of discovered) {
54
+ if (dashboardQueueNames.has(name)) continue;
55
+ const queue = getQueue(name);
56
+ if (!queue) continue;
57
+ bullBoard.addQueue(new BullMQAdapter(queue));
58
+ dashboardQueueNames.add(name);
59
+ }
60
+
61
+ for (const name of Array.from(dashboardQueueNames)) {
62
+ if (!discovered.includes(name)) {
63
+ bullBoard.removeQueue(name);
64
+ dashboardQueueNames.delete(name);
65
+ }
66
+ }
67
+ } catch {
68
+ // Keep the previous queue list if a refresh fails.
69
+ } finally {
70
+ syncInFlight = false;
71
+ }
72
+ }
73
+
74
+ function queuePrefix() {
75
+ return queueConfig.prefix;
76
+ }
77
+
78
+ function allowedQueueDashboardEmails() {
79
+ const emails = parseCsvOrFallback(queueConfig.allowedEmails, []);
80
+ return new Set(emails.map((email) => email.toLowerCase()));
81
+ }
82
+
83
+ function dashboardAuth() {
84
+ const allowedEmails = allowedQueueDashboardEmails();
85
+
86
+ return async (req: Request, res: Response, next: NextFunction) => {
87
+ /**
88
+ * Why: The email allowlist is the ONLY gate on the queue dashboard UI.
89
+ * When: No `allowedEmails` configured → the dashboard is open to everyone
90
+ * in both dev and production (no login required). When configured,
91
+ * only the listed email(s) pass — everyone else is blocked.
92
+ * Where: BullMQ dashboard route middleware.
93
+ * How: An empty allowlist short-circuits straight through; otherwise the
94
+ * requester's email (from the validated access token) must be in the
95
+ * set. This keeps BullMQ workers (mail delivery etc.) always running
96
+ * and gating strictly UI-only.
97
+ */
98
+ if (allowedEmails.size === 0) {
99
+ return next();
100
+ }
101
+
102
+ const rawToken = await cookie.getAuth(req);
103
+ if (!rawToken) {
104
+ return res.status(401).json({ message: "Unauthorized" });
105
+ }
106
+
107
+ const payload = await jwt.verifyToken(rawToken, "access");
108
+ if (!payload) {
109
+ return res.status(401).json({ message: "Unauthorized" });
110
+ }
111
+
112
+ const normalized = payload as Record<string, unknown>;
113
+ const email = String(normalized.email ?? "")
114
+ .trim()
115
+ .toLowerCase();
116
+
117
+ if (!allowedEmails.has(email)) {
118
+ return res.status(401).json({ message: "Unauthorized" });
119
+ }
120
+
121
+ return next();
122
+ };
123
+ }
124
+
125
+ async function discoverQueueNames(): Promise<string[]> {
126
+ const client = redisClientIfReady();
127
+ if (!client) return [];
128
+
129
+ const prefix = `${queuePrefix()}:`;
130
+ const names = new Set<string>();
131
+ let cursor = 0;
132
+
133
+ do {
134
+ const [nextCursor, keys] = await client.scan(cursor, "MATCH", `${prefix}*:meta`, "COUNT", 100);
135
+ cursor = Number(nextCursor);
136
+ for (const key of keys) {
137
+ const name = key.slice(prefix.length, -5);
138
+ if (name) names.add(name);
139
+ }
140
+ } while (cursor !== 0);
141
+
142
+ return Array.from(names);
143
+ }
144
+
145
+ function unavailableHtml() {
146
+ return `
147
+ <!doctype html>
148
+ <html>
149
+ <head>
150
+ <title>Queue Dashboard Unavailable</title>
151
+ <style>
152
+ body { font-family: system-ui, sans-serif; padding: 48px; text-align: center; }
153
+ h1 { color: #dc2626; }
154
+ </style>
155
+ </head>
156
+ <body>
157
+ <h1>Queue Dashboard Unavailable</h1>
158
+ <p>Redis is not connected. Check the Redis URL in src/config/redis.ts and make sure Redis is running.</p>
159
+ <p>Redis URL: ${redisConfig.url}</p>
160
+ </body>
161
+ </html>
162
+ `;
163
+ }
164
+
165
+ export async function setupQueueDashboard() {
166
+ if (!redisClientIfReady()) {
167
+ const route = (app: any) => {
168
+ app.get(BASE_PATH, (_req: Request, res: Response) => res.status(200).send(unavailableHtml()));
169
+ app.get(new RegExp(`^${BASE_PATH.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}/.*$`), (_req: Request, res: Response) => res.redirect(BASE_PATH));
170
+ };
171
+
172
+ return { basePath: BASE_PATH, enabled: false, route };
173
+ }
174
+
175
+ boardApp = serverAdapter.getRouter();
176
+
177
+ await refreshDashboardQueues();
178
+ if (!pollTimer) {
179
+ pollTimer = setInterval(refreshDashboardQueues, POLL_INTERVAL_MS);
180
+ }
181
+
182
+ const route = (app: any) => {
183
+ app.use(BASE_PATH, dashboardAuth(), boardApp);
184
+ };
185
+
186
+ return { basePath: BASE_PATH, enabled: true, route };
187
+ }
@@ -0,0 +1,61 @@
1
+ import { queueConfig } from "@/config/index.js";
2
+ import { closeDatabase, initDatabase } from "@/framework/database/connection.js";
3
+ import { pruneStaleQueues, startQueueWorker as startQueueWorkers, stopQueueRuntime } from "@/framework/queue/queue.js";
4
+ import { closeRedis, initRedis, redisError, redisReady } from "@/framework/redis/client.js";
5
+ import { parseCsvOrFallback, registerShutdownSignals, type ShutdownSignal } from "@/framework/support/lifecycle.js";
6
+ import { logger } from "@/framework/support/logger.js";
7
+
8
+ export interface WorkerHandle {
9
+ queues: string[];
10
+ shutdown: (signal: ShutdownSignal) => Promise<void>;
11
+ }
12
+
13
+ /**
14
+ * Why: Starts the queue worker and wires DB/Redis shutdown.
15
+ * When: CLI `queue:work` command or E2E test bootstrap.
16
+ * Where: Worker entrypoint.
17
+ * How: Parses --queue= args, inits DB/Redis, starts workers, registers signals.
18
+ * When called as the main module, also prints the startup banner.
19
+ */
20
+ export async function startQueueWorkerRuntime(queues: string[] = []): Promise<WorkerHandle> {
21
+ if (!queues.length) {
22
+ const queuesArg = process.argv.find((arg) => arg.startsWith("--queue="));
23
+ const queueNames = queuesArg?.split("=")[1];
24
+ queues = parseCsvOrFallback(queueNames, queueConfig.queues);
25
+ }
26
+
27
+ await initDatabase();
28
+ await initRedis();
29
+ if (!redisReady()) {
30
+ logger.error("Queue worker cannot start because Redis is unavailable", {
31
+ error: redisError()
32
+ });
33
+ process.exit(1);
34
+ }
35
+
36
+ await startQueueWorkers(queues);
37
+ await pruneStaleQueues(queues);
38
+ console.log(`Queue worker started: ${queues.join(", ")}`);
39
+
40
+ let shuttingDown = false;
41
+
42
+ async function shutdown(signal: ShutdownSignal) {
43
+ if (shuttingDown) return;
44
+ shuttingDown = true;
45
+
46
+ logger.info("Queue worker shutdown signal received", { signal });
47
+ await Promise.allSettled([stopQueueRuntime(), closeDatabase()]);
48
+ await closeRedis();
49
+
50
+ process.exit(0);
51
+ }
52
+
53
+ registerShutdownSignals(shutdown);
54
+
55
+ return { queues, shutdown };
56
+ }
57
+
58
+ const isMain = process.argv[1]?.endsWith("worker.ts") || process.argv[1]?.endsWith("worker.js");
59
+ if (isMain) {
60
+ await startQueueWorkerRuntime();
61
+ }
@@ -0,0 +1,46 @@
1
+ import { env } from "@/env.js";
2
+ import { socketServer } from "@/framework/realtime/index.js";
3
+ import { redisClientIfReady } from "@/framework/redis/client.js";
4
+
5
+ export type BroadcastOptions = {
6
+ users?: Array<string | number>;
7
+ roles?: string[];
8
+ rooms?: string[];
9
+ auth?: boolean;
10
+ all?: boolean;
11
+ };
12
+
13
+ /**
14
+ * Why: Emits realtime events to targeted audiences.
15
+ * When: Domain events need websocket fan-out.
16
+ * Where: Event dispatcher and module jobs.
17
+ * How: Emits via Socket.IO if available; otherwise relays through Redis pub/sub
18
+ * so the main process (which has Socket.IO) can broadcast it.
19
+ */
20
+ function emitViaIo(event: string, payload: any, options: BroadcastOptions) {
21
+ const io = socketServer();
22
+ if (!io) return;
23
+
24
+ if (options.all) io.emit(event, payload);
25
+ if (options.auth) io.to("auth").emit(event, payload);
26
+
27
+ for (const user of options.users || []) io.to(`user:${user}`).emit(event, payload);
28
+ for (const role of options.roles || []) io.to(`role:${role}`).emit(event, payload);
29
+ for (const room of options.rooms || []) io.to(room).emit(event, payload);
30
+ }
31
+
32
+ export function broadcast(event: string, payload: any, options: BroadcastOptions = {}) {
33
+ const io = socketServer();
34
+
35
+ if (io) {
36
+ emitViaIo(event, payload, options);
37
+ return;
38
+ }
39
+
40
+ // No Socket.IO in this process — relay through Redis so the main process can broadcast
41
+ const redis = redisClientIfReady();
42
+ if (!redis) return;
43
+
44
+ const channel = `${env.REDIS_PREFIX}:broadcast`;
45
+ redis.publish(channel, JSON.stringify({ event, payload, options }));
46
+ }
@@ -0,0 +1,2 @@
1
+ export { broadcast } from "@/framework/realtime/broadcast.js";
2
+ export { closeRealtime, initRealtime, ioServer, socketServer, WS_PATH } from "@/framework/realtime/socket.js";