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,68 @@
1
+ import type { Socket } from "socket.io";
2
+ import { cookieConfig } from "@/config/index.js";
3
+ import type { RealtimeAuthContext } from "@/framework/realtime/types.js";
4
+ import { jwt } from "@/framework/support/jwt.js";
5
+
6
+ export function unauthenticatedRealtimeAuth(): RealtimeAuthContext {
7
+ return { isAuthenticated: false, userId: null, roles: [], payload: null };
8
+ }
9
+
10
+ function parseCookieHeader(header: string | undefined) {
11
+ if (!header) return {} as Record<string, string>;
12
+
13
+ const cookies: Record<string, string> = {};
14
+ for (const part of header.split(";")) {
15
+ const [rawName, ...rawValue] = part.trim().split("=");
16
+ if (!rawName || rawValue.length === 0) continue;
17
+ cookies[rawName] = decodeURIComponent(rawValue.join("="));
18
+ }
19
+
20
+ return cookies;
21
+ }
22
+
23
+ function userIdFromPayload(payload: Record<string, unknown>) {
24
+ const candidate = payload.userId ?? payload.id ?? payload.sub;
25
+ if (candidate === null || candidate === undefined) return null;
26
+ return String(candidate);
27
+ }
28
+
29
+ function rolesFromPayload(payload: Record<string, unknown>) {
30
+ const direct = payload.roles;
31
+ if (Array.isArray(direct)) return direct.map((role) => String(role));
32
+
33
+ const single = payload.role;
34
+ if (single === null || single === undefined) return [];
35
+ return [String(single)];
36
+ }
37
+
38
+ /**
39
+ * Why: Resolves auth context for websocket connections from cookies.
40
+ * When: Socket.IO handshake middleware executes.
41
+ * Where: Realtime server auth pipeline.
42
+ * How: Parses access cookie, verifies JWT, then normalizes user/role claims.
43
+ */
44
+ export async function authFromSocketHandshake(socket: Socket): Promise<RealtimeAuthContext> {
45
+ const cookies = parseCookieHeader(socket.handshake.headers.cookie);
46
+ const rawToken = cookies[`${cookieConfig.name}_access`];
47
+
48
+ if (!rawToken) {
49
+ return unauthenticatedRealtimeAuth();
50
+ }
51
+
52
+ // Strip Hono's signed-cookie HMAC signature (last .<base64> part appended by setSignedCookie)
53
+ const token = rawToken.lastIndexOf(".") > 0 ? rawToken.substring(0, rawToken.lastIndexOf(".")) : rawToken;
54
+
55
+ const payload = await jwt.verifyToken(token, "access");
56
+ if (!payload) {
57
+ return unauthenticatedRealtimeAuth();
58
+ }
59
+
60
+ const normalizedPayload = payload as Record<string, unknown>;
61
+
62
+ return {
63
+ isAuthenticated: true,
64
+ userId: userIdFromPayload(normalizedPayload),
65
+ roles: rolesFromPayload(normalizedPayload),
66
+ payload: normalizedPayload
67
+ };
68
+ }
@@ -0,0 +1,196 @@
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 { isBun } from "@/framework/runtime/runtime.js";
8
+ import { logger } from "@/framework/support/logger.js";
9
+
10
+ export const WS_PATH = "/socket.io";
11
+
12
+ /**
13
+ * Why: Describes runtime-specific WebSocket wiring for the HTTP server adapter.
14
+ * When: startHttpServer needs Bun websocket handlers from the bun engine.
15
+ * Where: Cross-runtime server bootstrap.
16
+ * How: `bun` carries the bun-engine handler's websocket options; `node` attaches
17
+ * Socket.IO directly to the http server so no node field is needed.
18
+ */
19
+ export type RealtimeServeWebSocket = {
20
+ node?: any;
21
+ bun?: {
22
+ websocket: any;
23
+ idleTimeout?: number;
24
+ maxRequestBodySize?: number;
25
+ };
26
+ };
27
+
28
+ export type RealtimeInit = {
29
+ io: SocketIOServer;
30
+ websocketServe: RealtimeServeWebSocket;
31
+ };
32
+
33
+ let io: SocketIOServer | null = null;
34
+ let adapterSubClient: ReturnType<typeof redisClientIfReady> | null = null;
35
+ let serveWebSocket: RealtimeServeWebSocket = {};
36
+
37
+ function socketAllowedOrigins() {
38
+ const origins = [appConfig.url, appConfig.frontendUrl].filter(Boolean) as string[];
39
+ if (realtimeConfig.enabled) origins.push("https://admin.socket.io");
40
+ return [...new Set(origins)];
41
+ }
42
+
43
+ function socketCors() {
44
+ return { origin: socketAllowedOrigins(), credentials: true };
45
+ }
46
+
47
+ /**
48
+ * Why: Attaches the Redis pub/sub adapter so broadcasts span processes.
49
+ * When: Redis is available at realtime boot.
50
+ * Where: Realtime bootstrap.
51
+ * How: Duplicates the shared client for the adapter's subscription leg.
52
+ */
53
+ async function attachRedisAdapter() {
54
+ if (!redisConfig.enabled) return;
55
+
56
+ const pubClient = redisClientIfReady();
57
+ if (!pubClient) return;
58
+
59
+ adapterSubClient = pubClient.duplicate();
60
+ await adapterSubClient.connect();
61
+ io?.adapter(createAdapter(pubClient, adapterSubClient));
62
+ }
63
+
64
+ /**
65
+ * Why: Registers auth middleware and connection handling for the singleton.
66
+ * When: Realtime server is created.
67
+ * Where: Realtime bootstrap.
68
+ * How: Resolves auth from handshake cookies and joins user/role/auth rooms.
69
+ */
70
+ function registerAuthAndRooms() {
71
+ io?.use(async (socket, next) => {
72
+ const auth = await authFromSocketHandshake(socket);
73
+ socket.data.auth = auth as RealtimeAuthContext;
74
+
75
+ if (auth.isAuthenticated) {
76
+ socket.join("auth");
77
+ if (auth.userId) socket.join(`user:${auth.userId}`);
78
+ for (const role of auth.roles) socket.join(`role:${role}`);
79
+ } else {
80
+ socket.join("guest");
81
+ }
82
+
83
+ next();
84
+ });
85
+
86
+ io?.on("connection", (socket) => {
87
+ const auth = (socket.data.auth || unauthenticatedRealtimeAuth()) as RealtimeAuthContext;
88
+
89
+ logger.debug("Socket connected", {
90
+ socketId: socket.id,
91
+ authenticated: auth.isAuthenticated,
92
+ userId: auth.userId
93
+ });
94
+
95
+ socket.on("join", (room) => socket.join(String(room)));
96
+ socket.on("disconnect", (reason) => {
97
+ logger.info("Socket disconnected", {
98
+ socketId: socket.id,
99
+ reason,
100
+ authenticated: auth.isAuthenticated,
101
+ userId: auth.userId
102
+ });
103
+ });
104
+ });
105
+ }
106
+
107
+ /**
108
+ * Why: Boots the singleton Socket.IO server with auth-aware room assignment.
109
+ * When: HTTP server starts and realtime is enabled.
110
+ * Where: Framework server lifecycle.
111
+ * How: On node attaches to the created http server; on bun binds the bun-engine
112
+ * and registers its `/socket.io` route on the Hono app, returning the
113
+ * websocket options the Bun server needs.
114
+ */
115
+ export async function initRealtime(target: { app?: any; httpServer?: any }): Promise<RealtimeInit | null> {
116
+ if (!realtimeConfig.enabled) {
117
+ logger.info("Socket.IO disabled (disabled in config)");
118
+ return null;
119
+ }
120
+
121
+ if (io) return { io, websocketServe: serveWebSocket };
122
+
123
+ if (isBun()) {
124
+ const { Server: Engine } = await import("@socket.io/bun-engine");
125
+ const engine = new Engine({ path: WS_PATH, cors: socketCors() });
126
+ io = new SocketIOServer();
127
+ io.bind(engine);
128
+
129
+ const handler = engine.handler();
130
+
131
+ serveWebSocket.bun = {
132
+ websocket: handler.websocket,
133
+ idleTimeout: handler.idleTimeout || 120,
134
+ maxRequestBodySize: handler.maxRequestBodySize || 1024 * 1024
135
+ };
136
+
137
+ // Store engine in app for later use
138
+ if (target.app) {
139
+ (target.app as any)._engine = engine;
140
+ }
141
+ } else {
142
+ io = new SocketIOServer(target.httpServer, { cors: socketCors() });
143
+ }
144
+
145
+ await attachRedisAdapter();
146
+ registerAuthAndRooms();
147
+
148
+ logger.info(`Socket.IO enabled at ${WS_PATH}`);
149
+ return { io, websocketServe: serveWebSocket };
150
+ }
151
+
152
+ /**
153
+ * Why: Returns the active Socket.IO server instance.
154
+ * When: Broadcast helpers need emitter access.
155
+ * Where: Realtime broadcast/event internals.
156
+ * How: Returns nullable singleton created by initRealtime.
157
+ */
158
+ export function socketServer() {
159
+ return io;
160
+ }
161
+
162
+ /**
163
+ * Why: Alias for socketServer for facade ergonomics.
164
+ * When: Existing consumers use `io` naming.
165
+ * Where: Framework facade export compatibility.
166
+ * How: Returns same singleton Socket.IO instance.
167
+ */
168
+ export function ioServer() {
169
+ return io;
170
+ }
171
+
172
+ /**
173
+ * Why: Closes the Socket.IO server gracefully on shutdown.
174
+ * When: Server stop hooks.
175
+ * Where: Framework runtime teardown.
176
+ * How: Awaits close callback and clears the singleton reference.
177
+ */
178
+ export async function closeRealtime() {
179
+ if (!io) return;
180
+
181
+ await new Promise<void>((resolve) => {
182
+ io?.close(() => resolve());
183
+ });
184
+
185
+ if (adapterSubClient) {
186
+ try {
187
+ await adapterSubClient.quit();
188
+ } catch {
189
+ adapterSubClient.disconnect();
190
+ }
191
+ adapterSubClient = null;
192
+ }
193
+
194
+ io = null;
195
+ serveWebSocket = {};
196
+ }
@@ -0,0 +1,6 @@
1
+ export type RealtimeAuthContext = {
2
+ isAuthenticated: boolean;
3
+ userId: string | null;
4
+ roles: string[];
5
+ payload: Record<string, unknown> | null;
6
+ };
@@ -0,0 +1,16 @@
1
+ import { instrument } from "@socket.io/admin-ui";
2
+ import type { Server as SocketIOServer } from "socket.io";
3
+ import { appConfig, realtimeConfig } from "@/config/index.js";
4
+
5
+ export function setupSocketAdminUI(io: SocketIOServer | null) {
6
+ if (!io || !realtimeConfig.enabled || appConfig.environment === "production") {
7
+ return { enabled: false };
8
+ }
9
+
10
+ instrument(io, {
11
+ auth: false,
12
+ mode: "development"
13
+ });
14
+
15
+ return { enabled: true };
16
+ }
@@ -0,0 +1,151 @@
1
+ import { Redis } from "ioredis";
2
+ import { redisConfig } from "@/config/index.js";
3
+ import { CircuitBreaker } from "@/framework/circuit-breaker/cb.js";
4
+ import { logger } from "@/framework/support/logger.js";
5
+
6
+ export type redis = Redis;
7
+
8
+ let client: redis | null = null;
9
+ let ready = false;
10
+ let lastError: string | null = null;
11
+ let redisBreaker: CircuitBreaker | null = null;
12
+
13
+ if (redisConfig.enabled) {
14
+ redisBreaker = new CircuitBreaker();
15
+ }
16
+
17
+ function safeRedisUrl() {
18
+ try {
19
+ const url = new URL(redisConfig.url);
20
+ if (url.password) url.password = "***";
21
+ return url.toString();
22
+ } catch {
23
+ return redisConfig.url;
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Why: Initializes shared Redis connection for cache/queue/events/session.
29
+ * When: Runtime bootstrap before Redis-backed features are used.
30
+ * Where: Server, worker, scheduler startup paths.
31
+ * How: Connects once, tracks readiness, and degrades gracefully if unavailable.
32
+ */
33
+ export async function initRedis() {
34
+ if (client) return client;
35
+
36
+ if (!redisConfig.enabled) {
37
+ ready = false;
38
+ lastError = "redis disabled in config";
39
+ logger.info("Redis disabled; cache, session storage, queue, events, and cockpit unavailable");
40
+ return null;
41
+ }
42
+
43
+ if (redisBreaker?.getState() === "open") {
44
+ lastError = "redis circuit breaker is open";
45
+ logger.warn("Redis circuit breaker is open; skipping connection");
46
+ return null;
47
+ }
48
+
49
+ try {
50
+ client = new Redis(redisConfig.url, {
51
+ lazyConnect: true,
52
+ enableReadyCheck: true,
53
+ maxRetriesPerRequest: 0,
54
+ retryStrategy: () => null
55
+ });
56
+
57
+ client.on("error", (error) => {
58
+ lastError = error.message;
59
+ if (ready) {
60
+ logger.warn("Redis connection error", {
61
+ redisUrl: safeRedisUrl(),
62
+ error: error.message
63
+ });
64
+ }
65
+ });
66
+
67
+ await client.connect();
68
+ await client.ping();
69
+ ready = true;
70
+ redisBreaker?.onSuccess();
71
+ return client;
72
+ } catch (error) {
73
+ lastError ||= error instanceof Error ? error.message : String(error);
74
+ client?.disconnect();
75
+ client = null;
76
+ ready = false;
77
+ redisBreaker?.onFailure();
78
+ logger.warn(`Redis unavailable; continuing without Redis features (${lastError})`);
79
+ return null;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Why: Returns the current Redis client instance.
85
+ * When: Internal helpers need direct Redis operations.
86
+ * Where: Cache/session/queue/events modules.
87
+ * How: Returns nullable singleton without creating a new connection.
88
+ */
89
+ export function redis() {
90
+ return client;
91
+ }
92
+
93
+ /**
94
+ * Why: Indicates whether Redis is connected and usable.
95
+ * When: Features need to branch on Redis availability.
96
+ * Where: Cache/session/queue/event internals.
97
+ * How: Combines readiness flag with client presence.
98
+ */
99
+ export function redisReady() {
100
+ return ready && client !== null;
101
+ }
102
+
103
+ /**
104
+ * Why: Provides a single ready-checked Redis client accessor.
105
+ * When: Redis-backed modules need quick availability guard.
106
+ * Where: Cache/session/queue/events/BullBoard internals.
107
+ * How: Returns connected client or null when Redis is unavailable.
108
+ */
109
+ export function redisClientIfReady() {
110
+ if (redisBreaker?.getState() === "open") return null;
111
+ return redisReady() ? client : null;
112
+ }
113
+
114
+ /**
115
+ * Why: Exposes last Redis error for diagnostics.
116
+ * When: Startup or health logs need failure detail.
117
+ * Where: Runtime status output.
118
+ * How: Returns cached error message string.
119
+ */
120
+ export function redisError() {
121
+ return lastError;
122
+ }
123
+
124
+ /**
125
+ * Why: Returns the Redis circuit breaker instance or null when disabled.
126
+ * When: Health checks and diagnostics need breaker state.
127
+ * Where: Runtime status and monitoring paths.
128
+ * How: Returns the breaker if Redis is enabled, null otherwise.
129
+ */
130
+ export function getRedisBreaker() {
131
+ return redisBreaker;
132
+ }
133
+
134
+ /**
135
+ * Why: Closes Redis connection during graceful shutdown.
136
+ * When: Process stop hooks for server/worker/scheduler.
137
+ * Where: Runtime lifecycle files.
138
+ * How: Attempts quit, falls back to disconnect, then resets state.
139
+ */
140
+ export async function closeRedis() {
141
+ if (!client) return;
142
+
143
+ try {
144
+ await client.quit();
145
+ } catch {
146
+ client.disconnect();
147
+ } finally {
148
+ client = null;
149
+ ready = false;
150
+ }
151
+ }
@@ -0,0 +1,23 @@
1
+ export type Runtime = "node" | "bun" | "unknown";
2
+
3
+ const runtimeGlobals = globalThis as Record<string, unknown>;
4
+
5
+ /**
6
+ * Why: Select the correct driver/flavor when a runtime has specialized packages.
7
+ * When: Loading DB clients, web servers, and queues that vary per runtime.
8
+ * Where: Framework bootstrap and driver resolution layers.
9
+ * How: Inspects global runtime markers (Bun) and falls back to Node/unknown.
10
+ */
11
+ export function runtime(): Runtime {
12
+ if (runtimeGlobals.Bun !== undefined) return "bun";
13
+ if (typeof process !== "undefined" && process.versions?.node) return "node";
14
+ return "unknown";
15
+ }
16
+
17
+ export function isBun(): boolean {
18
+ return runtime() === "bun";
19
+ }
20
+
21
+ export function isNode(): boolean {
22
+ return runtime() === "node";
23
+ }
@@ -0,0 +1,94 @@
1
+ import type { AddressInfo } from "node:net";
2
+ import type { RealtimeServeWebSocket } from "@/framework/realtime/socket.js";
3
+ import { WS_PATH } from "@/framework/realtime/socket.js";
4
+ import { runtime } from "@/framework/runtime/runtime.js";
5
+
6
+ export type HttpServerHandle = {
7
+ address(): string | AddressInfo | null;
8
+ close(): Promise<void>;
9
+ native: unknown;
10
+ };
11
+
12
+ /**
13
+ * Why: Starts the Hono app on the active runtime's native HTTP server.
14
+ * When: Server bootstrap after the kernel is created.
15
+ * Where: Framework server entrypoint.
16
+ * How: Picks the runtime-appropriate serve (node-server/Bun.serve),
17
+ * wires runtime-specific WebSocket primitives when provided, and wraps
18
+ * the server in a uniform handle for address/close access.
19
+ */
20
+ async function startNode(
21
+ app: { fetch: (request: Request) => Response | Promise<Response> },
22
+ port: number,
23
+ websocket?: RealtimeServeWebSocket
24
+ ): Promise<HttpServerHandle> {
25
+ const [{ serve }, { createServer }] = await Promise.all([import("@hono/node-server"), import("node:http")]);
26
+
27
+ const server = websocket?.node
28
+ ? serve({ fetch: app.fetch, port, createServer, websocket: { server: websocket.node } })
29
+ : serve({ fetch: app.fetch, port, createServer });
30
+ return {
31
+ native: server,
32
+ address: () => server.address(),
33
+ close: () => new Promise<void>((resolve) => server.close(() => resolve()))
34
+ };
35
+ }
36
+
37
+ async function startBun(
38
+ app: { fetch: (request: Request) => Response | Promise<Response> },
39
+ port: number,
40
+ websocket?: RealtimeServeWebSocket
41
+ ): Promise<HttpServerHandle> {
42
+ const globals = globalThis as Record<string, any>;
43
+
44
+ let serverInstance: any;
45
+
46
+ const serverConfig: any = {
47
+ fetch: async (req: Request) => {
48
+ // Check if this is a socket.io request
49
+ const url = new URL(req.url);
50
+ if (url.pathname === WS_PATH || url.pathname.startsWith(`${WS_PATH}/`)) {
51
+ const engine = (app as any)._engine;
52
+ if (engine) {
53
+ // Pass the Bun server instance
54
+ return engine.handleRequest(req, serverInstance);
55
+ }
56
+ }
57
+ return app.fetch(req);
58
+ },
59
+ port
60
+ };
61
+
62
+ if (websocket?.bun) {
63
+ serverConfig.websocket = websocket.bun.websocket;
64
+ if (websocket.bun.idleTimeout) {
65
+ serverConfig.idleTimeout = websocket.bun.idleTimeout;
66
+ }
67
+ if (websocket.bun.maxRequestBodySize) {
68
+ serverConfig.maxRequestBodySize = websocket.bun.maxRequestBodySize;
69
+ }
70
+ }
71
+
72
+ serverInstance = globals.Bun.serve(serverConfig);
73
+
74
+ return {
75
+ native: serverInstance,
76
+ address: () =>
77
+ ({
78
+ address: serverInstance.hostname || "localhost",
79
+ family: "IPv4",
80
+ port: serverInstance.port
81
+ }) as AddressInfo,
82
+ close: () => Promise.resolve(serverInstance.stop())
83
+ };
84
+ }
85
+
86
+ export async function startHttpServer(
87
+ app: { fetch: (request: Request) => Response | Promise<Response> },
88
+ port: number,
89
+ websocket?: RealtimeServeWebSocket
90
+ ): Promise<HttpServerHandle> {
91
+ const activeRuntime = runtime();
92
+ if (activeRuntime === "bun") return startBun(app, port, websocket);
93
+ return startNode(app, port, websocket);
94
+ }
@@ -0,0 +1,116 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { redisConfig } from "@/config/index.js";
3
+ import { databaseDialect, databasePool } from "@/framework/database/connection.js";
4
+ import { redis, redisReady } from "@/framework/redis/client.js";
5
+
6
+ type LockOptions = { ttlMs?: number; tableName?: string };
7
+
8
+ function table(name?: string) {
9
+ return name || "scheduler_locks";
10
+ }
11
+
12
+ async function ensureTable(tableName?: string) {
13
+ const pool = databasePool();
14
+ const dialect = databaseDialect();
15
+ const lockTable = table(tableName);
16
+
17
+ if (dialect === "mysql") {
18
+ await pool.query(
19
+ `CREATE TABLE IF NOT EXISTS \`${lockTable}\` (name VARCHAR(191) PRIMARY KEY, owner VARCHAR(191) NOT NULL, expires_at BIGINT NOT NULL)`
20
+ );
21
+ return;
22
+ }
23
+
24
+ if (dialect === "postgresql") {
25
+ await pool.unsafe(
26
+ `CREATE TABLE IF NOT EXISTS "${lockTable}" (name VARCHAR(191) PRIMARY KEY, owner VARCHAR(191) NOT NULL, expires_at BIGINT NOT NULL)`
27
+ );
28
+ return;
29
+ }
30
+
31
+ await pool.execute(`CREATE TABLE IF NOT EXISTS "${lockTable}" (name TEXT PRIMARY KEY, owner TEXT NOT NULL, expires_at INTEGER NOT NULL)`);
32
+ }
33
+
34
+ async function acquireDbLock(name: string, owner: string, ttlMs: number, tableName?: string) {
35
+ await ensureTable(tableName);
36
+ const pool = databasePool();
37
+ const dialect = databaseDialect();
38
+ const lockTable = table(tableName);
39
+ const now = Date.now();
40
+ const expiresAt = now + ttlMs;
41
+
42
+ if (dialect === "postgresql") {
43
+ const rows = await pool.unsafe(
44
+ `INSERT INTO "${lockTable}" (name, owner, expires_at) VALUES ($1, $2, $3) ON CONFLICT (name) DO UPDATE SET owner = EXCLUDED.owner, expires_at = EXCLUDED.expires_at WHERE "${lockTable}".expires_at < $4 RETURNING owner`,
45
+ [name, owner, expiresAt, now]
46
+ );
47
+ return rows?.[0]?.owner === owner;
48
+ }
49
+
50
+ if (dialect === "mysql") {
51
+ await pool.query(
52
+ `INSERT INTO \`${lockTable}\` (name, owner, expires_at) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE owner = IF(expires_at < ?, VALUES(owner), owner), expires_at = IF(expires_at < ?, VALUES(expires_at), expires_at)`,
53
+ [name, owner, expiresAt, now, now]
54
+ );
55
+ const [rows] = await pool.query(`SELECT owner FROM \`${lockTable}\` WHERE name = ?`, [name]);
56
+ return rows?.[0]?.owner === owner;
57
+ }
58
+
59
+ await pool.execute({
60
+ sql: `INSERT INTO "${lockTable}" (name, owner, expires_at) VALUES (?, ?, ?) ON CONFLICT(name) DO UPDATE SET owner = excluded.owner, expires_at = excluded.expires_at WHERE "${lockTable}".expires_at < ?`,
61
+ args: [name, owner, expiresAt, now]
62
+ });
63
+ const result = await pool.execute({
64
+ sql: `SELECT owner FROM "${lockTable}" WHERE name = ?`,
65
+ args: [name]
66
+ });
67
+ return result.rows?.[0]?.owner === owner;
68
+ }
69
+
70
+ async function releaseDbLock(name: string, owner: string, tableName?: string) {
71
+ const pool = databasePool();
72
+ const dialect = databaseDialect();
73
+ const lockTable = table(tableName);
74
+
75
+ if (dialect === "postgresql") return await pool.unsafe(`DELETE FROM "${lockTable}" WHERE name = $1 AND owner = $2`, [name, owner]);
76
+ if (dialect === "mysql") return await pool.query(`DELETE FROM \`${lockTable}\` WHERE name = ? AND owner = ?`, [name, owner]);
77
+ await pool.execute({
78
+ sql: `DELETE FROM "${lockTable}" WHERE name = ? AND owner = ?`,
79
+ args: [name, owner]
80
+ });
81
+ }
82
+
83
+ /**
84
+ * Why: Runs a handler with distributed lock to prevent duplicate execution.
85
+ * When: Scheduled tasks may run across multiple processes/instances.
86
+ * Where: Scheduler runtime around each cron job.
87
+ * How: Uses Redis NX lock when available, otherwise falls back to DB lock table.
88
+ */
89
+ export async function runWithLock<T>(name: string, handler: () => Promise<T>, options: LockOptions = {}) {
90
+ const owner = randomUUID();
91
+ const ttlMs = options.ttlMs || 60_000;
92
+ const client = redis();
93
+
94
+ if (redisReady() && client) {
95
+ const key = `${redisConfig.prefix}:lock:${name}`;
96
+ const ok = await client.set(key, owner, "PX", ttlMs, "NX");
97
+ if (!ok) return { ran: false, backend: "redis" as const };
98
+
99
+ try {
100
+ const result = await handler();
101
+ return { ran: true, backend: "redis" as const, result };
102
+ } finally {
103
+ await client.del(key);
104
+ }
105
+ }
106
+
107
+ const ok = await acquireDbLock(name, owner, ttlMs, options.tableName);
108
+ if (!ok) return { ran: false, backend: "db" as const };
109
+
110
+ try {
111
+ const result = await handler();
112
+ return { ran: true, backend: "db" as const, result };
113
+ } finally {
114
+ await releaseDbLock(name, owner, options.tableName);
115
+ }
116
+ }