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,418 @@
1
+ import crypto from "node:crypto";
2
+ import type { Request, Response } from "express";
3
+ import { cache, dispatchCommand, dispatchEvent, session, storage } from "@/framework/facade.js";
4
+
5
+ /**
6
+ * Example controller guide:
7
+ * - req.body / req.params: validated payloads from the route schema (validation middleware runs first).
8
+ * - dispatchEvent(name, payload, options): emits domain events.
9
+ * - dispatchCommand(name, payload, { async, queue }): queues command-style work.
10
+ * - storage.generateForDownload(...): creates one-time temp download token.
11
+ * - storage.consumeGenerated(token): reads temp file and deletes it.
12
+ * - cache.put/get/remember: lightweight state for async flows (PDF status).
13
+ * - securityMiddleware: sets CSP, HSTS, X-Frame headers (toggle via SECURITY_HEADERS env).
14
+ */
15
+
16
+ type PdfRequest = {
17
+ title: string;
18
+ rows: number;
19
+ chunkSize: number;
20
+ };
21
+
22
+ function pdfStatusKey(requestId: string) {
23
+ return "{{module}}:pdf:status:" + requestId;
24
+ }
25
+
26
+ function sessionId(res: Response) {
27
+ return res.locals.sessionId as string;
28
+ }
29
+
30
+ /**
31
+ * Why: Demonstrates event dispatch with both queue and websocket broadcast.
32
+ * When: Use as the baseline event example for async + realtime flow.
33
+ * Where: POST /{{module}}/event.
34
+ */
35
+ export const fire = async (req: Request, res: Response) => {
36
+ // Route validates body using ExampleEventSchema in route stub.
37
+ const body = req.body;
38
+
39
+ // Session example: keeps last payload for this visitor/session.
40
+ await session.put(sessionId(res), "lastExampleEvent", body);
41
+
42
+ // Event options shown here:
43
+ // - queue: true -> send to default async event queue
44
+ // - broadcast: { all: true } -> fanout to realtime listeners
45
+ await dispatchEvent("{{module}}.created", body, {
46
+ queue: true,
47
+ broadcast: { all: true }
48
+ });
49
+
50
+ res.status(202).json({ message: "Event dispatched", data: body });
51
+ };
52
+
53
+ /**
54
+ * Why: Demonstrates command-style background processing via queue.
55
+ * When: Use when work should run asynchronously and return 202 immediately.
56
+ * Where: POST /{{module}}/queue/command.
57
+ */
58
+ export const queueCommand = async (req: Request, res: Response) => {
59
+ const body = req.body;
60
+
61
+ // Command options:
62
+ // - async: true -> run in worker, do not block request
63
+ // - queue: "default" -> explicit queue channel
64
+ await dispatchCommand("{{module}}.process", body, {
65
+ async: true,
66
+ queue: "default"
67
+ });
68
+
69
+ res.status(202).json({ message: "Queue command dispatched", data: body });
70
+ };
71
+
72
+ /**
73
+ * Why: Demonstrates queued events without realtime fanout.
74
+ * When: Use when consumers should process event in workers only.
75
+ * Where: POST /{{module}}/event/queued.
76
+ */
77
+ export const fireQueued = async (req: Request, res: Response) => {
78
+ const body = req.body;
79
+
80
+ await dispatchEvent("{{module}}.created", body, { queue: "default" });
81
+
82
+ res.status(202).json({ message: "Queued event dispatched", data: body });
83
+ };
84
+
85
+ /**
86
+ * Why: Demonstrates one event feeding both queue workers and broadcast listeners.
87
+ * When: Use when background processing and client updates are both needed.
88
+ * Where: POST /{{module}}/event/queued-broadcast.
89
+ */
90
+ export const fireQueuedBroadcast = async (req: Request, res: Response) => {
91
+ const body = req.body;
92
+
93
+ await dispatchEvent("{{module}}.created", body, {
94
+ queue: "default",
95
+ broadcast: { all: true }
96
+ });
97
+
98
+ res.status(202).json({ message: "Queued broadcast event dispatched", data: body });
99
+ };
100
+
101
+ /**
102
+ * Why: Demonstrates broadcast-only realtime events.
103
+ * When: Use for transient notifications with no worker processing.
104
+ * Where: POST /{{module}}/event/broadcast.
105
+ */
106
+ export const fireBroadcast = async (_req: Request, res: Response) => {
107
+ const payload = {
108
+ message: "Examples broadcast fired",
109
+ firedAt: new Date().toISOString()
110
+ };
111
+
112
+ await dispatchEvent("{{module}}.realtime", payload, { broadcast: { all: true } });
113
+
114
+ res.json({ message: "Broadcast event dispatched", data: payload });
115
+ };
116
+
117
+ /**
118
+ * Why: Demonstrates multipart upload with storage facade.
119
+ * When: Use when API accepts binary files from forms/clients.
120
+ * Where: POST /{{module}}/upload.
121
+ */
122
+ export const upload = async (req: Request, res: Response) => {
123
+ const file = (req.body as any)?.file;
124
+
125
+ if (!(file instanceof File)) {
126
+ return res.status(422).json({ message: "File is required" });
127
+ }
128
+
129
+ // Writes to public disk. Name is auto-generated when omitted.
130
+ const path = await storage.disk("public").putFile("uploads", file);
131
+
132
+ res.json({
133
+ message: "File uploaded successfully",
134
+ path,
135
+ url: storage.disk("public").url(path)
136
+ });
137
+ };
138
+
139
+ /**
140
+ * Why: Demonstrates Excel import by reading uploaded workbook rows.
141
+ * When: Use when clients submit .xlsx files for server-side processing.
142
+ * Where: POST /{{module}}/import/excel.
143
+ */
144
+ export const importExcel = async (req: Request, res: Response) => {
145
+ let ExcelJS: any;
146
+ try {
147
+ ({ default: ExcelJS } = await import("exceljs"));
148
+ } catch {
149
+ return res.status(500).json({ message: "Missing optional dependency 'exceljs'. Install with: bun add exceljs" });
150
+ }
151
+
152
+ const file = (req.body as any)?.file;
153
+ if (!(file instanceof File)) {
154
+ return res.status(422).json({ message: "File is required" });
155
+ }
156
+
157
+ const buffer = Buffer.from(await file.arrayBuffer());
158
+ const workbook = new ExcelJS.Workbook();
159
+ await workbook.xlsx.load(buffer);
160
+ const sheet = workbook.worksheets[0];
161
+ if (!sheet) return res.status(422).json({ message: "Workbook has no sheets" });
162
+
163
+ const headerRow = sheet.getRow(1);
164
+ const headers = headerRow.values.slice(1).map((value: unknown) => String(value || "").trim());
165
+ const rows: Record<string, string | number | null>[] = [];
166
+ sheet.eachRow((row: any, rowNumber: number) => {
167
+ if (rowNumber === 1) return;
168
+ const values = row.values.slice(1);
169
+ const record: Record<string, string | number | null> = {};
170
+ for (let i = 0; i < headers.length; i++) {
171
+ const value = values[i];
172
+ record[headers[i] || `column_${i + 1}`] = value == null ? null : (typeof value === "object" && "text" in value ? String(value.text) : value);
173
+ }
174
+ rows.push(record);
175
+ });
176
+
177
+ res.json({
178
+ message: "Excel imported",
179
+ data: {
180
+ sheetName: sheet.name,
181
+ headers,
182
+ totalRows: rows.length,
183
+ preview: rows.slice(0, 20)
184
+ }
185
+ });
186
+ };
187
+
188
+ /**
189
+ * Why: Demonstrates short-lived generated downloads with one-time token consume.
190
+ * When: Use for temporary export artifacts that should not persist.
191
+ * Where: POST /{{module}}/download/generate.
192
+ */
193
+ export const generateDownload = async (_req: Request, res: Response) => {
194
+ const csv = ["id,title", "1,nexwire report", "2,generated and temporary"].join("\n");
195
+ const token = await storage.generateForDownload({
196
+ prefix: "{{module}}-report",
197
+ extension: "csv",
198
+ data: csv
199
+ });
200
+ res.json({
201
+ message: "Temporary file generated",
202
+ token,
203
+ downloadUrl: `/{{module}}/download/${encodeURIComponent(token)}`
204
+ });
205
+ };
206
+
207
+ /**
208
+ * Why: Consumes a temporary token and returns the generated CSV file.
209
+ * When: Called after generateDownload to perform one-time download.
210
+ * Where: GET /{{module}}/download/{*token}.
211
+ */
212
+ export const downloadGenerated = async (req: Request, res: Response) => {
213
+ const token = decodeURIComponent(req.params.token);
214
+ try {
215
+ const file = await storage.consumeGenerated(token);
216
+ res.type("text/csv").attachment("{{module}}-report.csv").send(file);
217
+ } catch {
218
+ res.status(404).json({ message: "Download token expired or invalid" });
219
+ }
220
+ };
221
+
222
+ /**
223
+ * Why: Demonstrates styled Excel export generation and temporary delivery.
224
+ * When: Use when spreadsheet output with formatting/totals is needed.
225
+ * Where: POST /{{module}}/download/excel/generate.
226
+ */
227
+ export const generateExcelDownload = async (req: Request, res: Response) => {
228
+ let ExcelJS: any;
229
+ try {
230
+ ({ default: ExcelJS } = await import("exceljs"));
231
+ } catch {
232
+ return res.status(500).json({ message: "Missing optional dependency 'exceljs'. Install with: bun add exceljs" });
233
+ }
234
+
235
+ const title = (req.body as { title?: string })?.title ?? "";
236
+ // remember(key, ttl, factory) caches expensive work for repeated requests.
237
+ const rows = await cache.remember("{{module}}:index", 60, async () => [
238
+ { id: 1, title: "nexwire framework ready" },
239
+ { id: 2, title: "modules are auto-discovered" }
240
+ ]);
241
+
242
+ const workbook = new ExcelJS.Workbook();
243
+ const sheet = workbook.addWorksheet("Examples");
244
+ sheet.mergeCells("A1:C1");
245
+ sheet.getCell("A1").value = title;
246
+ sheet.getCell("A1").font = { bold: true, size: 14, color: { argb: "FFFFFFFF" } };
247
+ sheet.getCell("A1").alignment = { horizontal: "center", vertical: "middle" };
248
+ sheet.getCell("A1").fill = { type: "pattern", pattern: "solid", fgColor: { argb: "FF1F4E78" } };
249
+ sheet.addRow(["ID", "Title", "Length"]);
250
+ sheet.getRow(2).font = { bold: true, color: { argb: "FFFFFFFF" } };
251
+ sheet.getRow(2).alignment = { horizontal: "center" };
252
+ sheet.getRow(2).fill = { type: "pattern", pattern: "solid", fgColor: { argb: "FF2E75B6" } };
253
+ for (const row of rows) sheet.addRow([row.id, row.title, row.title.length]);
254
+
255
+ const dataStart = 3;
256
+ const dataEnd = dataStart + rows.length - 1;
257
+ const subtotalRow = dataEnd + 1;
258
+ const grandTotalRow = subtotalRow + 1;
259
+ sheet.getCell(`A${subtotalRow}`).value = "Subtotal";
260
+ sheet.getCell(`B${subtotalRow}`).value = rows.length;
261
+ sheet.getCell(`C${subtotalRow}`).value = { formula: `SUM(C${dataStart}:C${dataEnd})` };
262
+ sheet.getCell(`A${grandTotalRow}`).value = "Grand Total";
263
+ sheet.getCell(`B${grandTotalRow}`).value = rows.length;
264
+ sheet.getCell(`C${grandTotalRow}`).value = { formula: `SUM(C${dataStart}:C${dataEnd})` };
265
+ for (const rowNumber of [subtotalRow, grandTotalRow]) {
266
+ sheet.getRow(rowNumber).font = { bold: true };
267
+ sheet.getRow(rowNumber).fill = { type: "pattern", pattern: "solid", fgColor: { argb: rowNumber === grandTotalRow ? "FFFCE4D6" : "FFE2EFDA" } };
268
+ }
269
+ sheet.columns = [{ width: 10 }, { width: 48 }, { width: 14 }];
270
+ sheet.views = [{ state: "frozen", ySplit: 2 }];
271
+
272
+ const buffer = Buffer.from(await workbook.xlsx.writeBuffer());
273
+ const token = await storage.generateForDownload({
274
+ prefix: "{{module}}-report",
275
+ extension: "xlsx",
276
+ data: buffer
277
+ });
278
+ res.json({
279
+ message: "Excel generated",
280
+ token,
281
+ downloadUrl: `/{{module}}/download/excel/${encodeURIComponent(token)}`
282
+ });
283
+ };
284
+
285
+ /**
286
+ * Why: Consumes a temporary token and returns the generated XLSX file.
287
+ * When: Called after generateExcelDownload for one-time Excel download.
288
+ * Where: GET /{{module}}/download/excel/{*token}.
289
+ */
290
+ export const downloadGeneratedExcel = async (req: Request, res: Response) => {
291
+ const token = decodeURIComponent(req.params.token);
292
+ try {
293
+ const file = await storage.consumeGenerated(token);
294
+ res.type("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet").attachment("{{module}}-report.xlsx").send(file);
295
+ } catch {
296
+ res.status(404).json({ message: "Download token expired or invalid" });
297
+ }
298
+ };
299
+
300
+ /**
301
+ * Why: Starts hybrid PDF generation for heavy design + large data.
302
+ * When: Use when reports should be rendered with HTML/CSS and processed in background.
303
+ * Where: POST /{{module}}/download/pdf/generate.
304
+ */
305
+ export const generatePdfDownload = async (req: Request, res: Response) => {
306
+ const body = req.body as PdfRequest;
307
+ const requestId = crypto.randomUUID();
308
+
309
+ // Queue mode path: store status in cache, then worker updates it.
310
+ if (cache.isAvailable()) {
311
+ await cache.put(pdfStatusKey(requestId), {
312
+ state: "pending",
313
+ createdAt: new Date().toISOString(),
314
+ mode: "queued"
315
+ }, 1800);
316
+ await dispatchCommand("{{module}}.pdf.generate", { requestId, ...body }, {
317
+ async: true,
318
+ queue: "default"
319
+ });
320
+ return res.status(202).json({
321
+ message: "PDF generation queued",
322
+ requestId,
323
+ mode: "queued",
324
+ statusUrl: `/{{module}}/download/pdf/${requestId}`,
325
+ downloadUrl: `/{{module}}/download/pdf/${requestId}`
326
+ });
327
+ }
328
+
329
+ const token = await generatePdfToken(body);
330
+ res.status(202).json({
331
+ message: "PDF generated",
332
+ requestId,
333
+ mode: "inline",
334
+ statusUrl: `/{{module}}/download/pdf/${requestId}`,
335
+ downloadUrl: `/{{module}}/download/pdf/${encodeURIComponent(token)}`
336
+ });
337
+ };
338
+
339
+ /**
340
+ * Why: Polls queued PDF status, then serves one-time PDF download when ready.
341
+ * When: Use after generatePdfDownload returns queued request id.
342
+ * Where: GET /{{module}}/download/pdf/{requestId}.
343
+ */
344
+ export const downloadGeneratedPdf = async (req: Request, res: Response) => {
345
+ const requestId = decodeURIComponent(req.params.requestId);
346
+ if (cache.isAvailable()) {
347
+ // Status API contract:
348
+ // - pending -> return 202
349
+ // - ready + token -> return file
350
+ // - failed -> return 500 with message
351
+ const status = await cache.get<{ state: string; token?: string; message?: string }>(pdfStatusKey(requestId));
352
+
353
+ if (!status) return res.status(404).json({ message: "Unknown PDF request id" });
354
+ if (status.state === "pending") return res.status(202).json({ message: "PDF is still generating", state: "pending" });
355
+ if (status.state === "failed") return res.status(500).json({ message: status.message || "PDF generation failed" });
356
+
357
+ if (status.token) {
358
+ try {
359
+ const file = await storage.consumeGenerated(status.token);
360
+ await cache.forget(pdfStatusKey(requestId));
361
+ res.type("application/pdf").attachment("{{module}}-report.pdf").send(file);
362
+ return;
363
+ } catch {
364
+ return res.status(404).json({ message: "PDF token expired or invalid" });
365
+ }
366
+ }
367
+ }
368
+
369
+ try {
370
+ const file = await storage.consumeGenerated(requestId);
371
+ res.type("application/pdf").attachment("{{module}}-report.pdf").send(file);
372
+ } catch {
373
+ res.status(404).json({ message: "PDF token expired or invalid" });
374
+ }
375
+ };
376
+
377
+ export async function generatePdfToken(payload: PdfRequest) {
378
+ let chromium: any;
379
+ try {
380
+ ({ chromium } = await import("playwright"));
381
+ } catch {
382
+ throw new Error("Missing optional dependency 'playwright'. Install with: bun add playwright and run: bunx playwright install chromium");
383
+ }
384
+ const browser = await chromium.launch({ headless: true });
385
+ try {
386
+ const page = await browser.newPage();
387
+ await page.setContent(buildPdfHtml(payload), { waitUntil: "networkidle" });
388
+ const pdf = await page.pdf({
389
+ format: "A4",
390
+ printBackground: true,
391
+ margin: { top: "16mm", right: "12mm", bottom: "16mm", left: "12mm" }
392
+ });
393
+ return storage.generateForDownload({
394
+ prefix: "{{module}}-report",
395
+ extension: "pdf",
396
+ data: pdf
397
+ });
398
+ } finally {
399
+ await browser.close();
400
+ }
401
+ }
402
+
403
+ function buildPdfHtml(payload: PdfRequest) {
404
+ const rows = Array.from({ length: payload.rows }, (_, i) => ({
405
+ id: i + 1,
406
+ label: `Record ${i + 1}`,
407
+ value: ((i + 1) * 17) % 1000
408
+ }));
409
+ const chunks: typeof rows[] = [];
410
+ for (let i = 0; i < rows.length; i += payload.chunkSize) {
411
+ chunks.push(rows.slice(i, i + payload.chunkSize));
412
+ }
413
+ const sections = chunks.map((chunk, index) => {
414
+ const tableRows = chunk.map(item => `<tr><td>${item.id}</td><td>${item.label}</td><td>${item.value}</td></tr>`).join("");
415
+ return `<section class="page"><h2>Batch ${index + 1}</h2><table><thead><tr><th>ID</th><th>Label</th><th>Value</th></tr></thead><tbody>${tableRows}</tbody></table></section>`;
416
+ }).join("");
417
+ return `<!doctype html><html><head><meta charset="utf-8" /><style>body{font-family:Arial,sans-serif;color:#1f2937;margin:0}.hero{padding:24px;background:linear-gradient(120deg,#0f766e,#1d4ed8);color:#fff}.hero h1{margin:0 0 4px 0;font-size:22px}.hero p{margin:0;font-size:12px;opacity:.9}.page{padding:16px 20px;page-break-after:always}.page:last-child{page-break-after:auto}h2{margin:0 0 10px 0;font-size:14px;color:#0f172a}table{width:100%;border-collapse:collapse;font-size:11px}th,td{border:1px solid #cbd5e1;padding:6px}th{background:#e2e8f0;text-align:left}tr:nth-child(even){background:#f8fafc}</style></head><body><div class="hero"><h1>${payload.title}</h1><p>Rows: ${payload.rows} | Chunk size: ${payload.chunkSize}</p></div>${sections}</body></html>`;
418
+ }
@@ -0,0 +1,75 @@
1
+ import { cache, dispatchEvent, shouldQueue } from "@/framework/facade.js";
2
+ import { generatePdfToken } from "@/modules/{{module}}/controllers/{{module}}.controller.js";
3
+
4
+ /**
5
+ * event: Event name this worker listens to.
6
+ * queue: Queue name where jobs are consumed from.
7
+ * handler: Async processor that receives `job.data`.
8
+ */
9
+ shouldQueue("{{module}}.process", "default", async (job) => {
10
+ const waitSeconds = Number(job.data.waitSeconds || 1);
11
+ await new Promise(resolve => setTimeout(resolve, waitSeconds * 1000));
12
+
13
+ await cache.put("{{module}}:last-command-job", {
14
+ payload: job.data,
15
+ handledAt: new Date().toISOString()
16
+ }, 120);
17
+
18
+ /**
19
+ * dispatchEvent name: Event id to publish to listeners.
20
+ * payload: Data sent to queue/broadcast consumers.
21
+ * queue: Optional queue target for async event consumers.
22
+ * broadcast: Optional realtime fanout (all/users/roles/rooms).
23
+ */
24
+ await dispatchEvent("{{module}}.process.finished", {
25
+ ...job.data,
26
+ handledAt: new Date().toISOString()
27
+ }, {
28
+ broadcast: { all: true }
29
+ });
30
+
31
+ return { ok: true, mode: "queue" };
32
+ });
33
+
34
+ shouldQueue("{{module}}.created", "default", async (job) => {
35
+ await cache.put("{{module}}:last-created-event", job.data, 120);
36
+ return { ok: true, mode: "event-queue" };
37
+ });
38
+
39
+ shouldQueue("{{module}}.scheduler.tick", "default", async (job) => {
40
+ await cache.put("{{module}}:last-scheduler-job", job.data, 120);
41
+ return { ok: true, mode: "scheduler-queue" };
42
+ });
43
+
44
+ shouldQueue("{{module}}.pdf.generate", "default", async (job) => {
45
+ const requestId = String(job.data.requestId || "");
46
+
47
+ if (!requestId) {
48
+ return { ok: false, mode: "pdf-queue", message: "Missing requestId" };
49
+ }
50
+
51
+ const key = `{{module}}:pdf:status:${requestId}`;
52
+
53
+ try {
54
+ const token = await generatePdfToken({
55
+ title: String(job.data.title || "Example PDF Report"),
56
+ rows: Number(job.data.rows || 1000),
57
+ chunkSize: Number(job.data.chunkSize || 250)
58
+ });
59
+
60
+ await cache.put(key, {
61
+ state: "ready",
62
+ token,
63
+ readyAt: new Date().toISOString()
64
+ }, 1800);
65
+
66
+ return { ok: true, mode: "pdf-queue" };
67
+ } catch (error) {
68
+ await cache.put(key, {
69
+ state: "failed",
70
+ message: error instanceof Error ? error.message : "Unknown error"
71
+ }, 1800);
72
+ return { ok: false, mode: "pdf-queue" };
73
+ }
74
+ });
75
+
@@ -0,0 +1,207 @@
1
+ import { createRoute, createRouter, HttpStatusCodes, jsonContent, z } from "@/framework/facade.js";
2
+ import { env } from "@/env.js";
3
+ import { downloadGenerated, downloadGeneratedExcel, downloadGeneratedPdf, fire, fireBroadcast, fireQueued, fireQueuedBroadcast, generateDownload, generateExcelDownload, generatePdfDownload, importExcel, queueCommand, upload } from "@/modules/{{module}}/controllers/{{module}}.controller.js";
4
+ import { DownloadTokenSchema, ExcelDownloadRequestSchema, ExampleEventSchema, PdfGenerateAcceptedSchema, PdfGenerateRequestSchema, QueueCommandRequestSchema, UploadResponseSchema } from "@/modules/{{module}}/controllers/{{module}}.schema.js";
5
+
6
+ const eventRoute = createRoute({
7
+ path: "/event",
8
+ method: "post",
9
+ tags: ["Examples"],
10
+ request: {
11
+ body: jsonContent(ExampleEventSchema, "Examples event payload")
12
+ },
13
+ responses: {
14
+ [HttpStatusCodes.ACCEPTED]: jsonContent(z.object({ message: z.string(), data: ExampleEventSchema }), "Event accepted")
15
+ }
16
+ });
17
+
18
+ const queueCommandRoute = createRoute({
19
+ path: "/queue/command",
20
+ method: "post",
21
+ tags: ["Examples"],
22
+ request: {
23
+ body: jsonContent(QueueCommandRequestSchema, "Queue command payload")
24
+ },
25
+ responses: {
26
+ [HttpStatusCodes.ACCEPTED]: jsonContent(z.object({ message: z.string(), data: QueueCommandRequestSchema }), "Queue command dispatched")
27
+ }
28
+ });
29
+
30
+ const queuedEventRoute = createRoute({
31
+ path: "/event/queued",
32
+ method: "post",
33
+ tags: ["Examples"],
34
+ request: {
35
+ body: jsonContent(ExampleEventSchema, "Queued event payload")
36
+ },
37
+ responses: {
38
+ [HttpStatusCodes.ACCEPTED]: jsonContent(z.object({ message: z.string(), data: ExampleEventSchema }), "Queued event accepted")
39
+ }
40
+ });
41
+
42
+ const queuedBroadcastEventRoute = createRoute({
43
+ path: "/event/queued-broadcast",
44
+ method: "post",
45
+ tags: ["Examples"],
46
+ request: {
47
+ body: jsonContent(ExampleEventSchema, "Queued broadcast event payload")
48
+ },
49
+ responses: {
50
+ [HttpStatusCodes.ACCEPTED]: jsonContent(z.object({ message: z.string(), data: ExampleEventSchema }), "Queued broadcast event accepted")
51
+ }
52
+ });
53
+
54
+ const broadcastRoute = createRoute({
55
+ path: "/event/broadcast",
56
+ method: "post",
57
+ tags: ["Examples"],
58
+ responses: {
59
+ [HttpStatusCodes.OK]: jsonContent(z.object({
60
+ message: z.string(),
61
+ data: z.object({ message: z.string(), firedAt: z.string() })
62
+ }), "Broadcast event dispatched")
63
+ }
64
+ });
65
+
66
+ const uploadRoute = createRoute({
67
+ path: "/upload",
68
+ method: "post",
69
+ tags: ["Examples"],
70
+ request: {
71
+ body: {
72
+ content: {
73
+ "multipart/form-data": {
74
+ schema: z.object({ file: z.any().openapi({ type: "string", format: "binary" }) })
75
+ }
76
+ }
77
+ }
78
+ },
79
+ responses: {
80
+ [HttpStatusCodes.OK]: {
81
+ description: "File uploaded",
82
+ content: { "application/json": { schema: UploadResponseSchema } }
83
+ }
84
+ }
85
+ });
86
+
87
+ const importExcelRoute = createRoute({
88
+ path: "/import/excel",
89
+ method: "post",
90
+ tags: ["Examples"],
91
+ request: {
92
+ body: {
93
+ content: {
94
+ "multipart/form-data": {
95
+ schema: z.object({ file: z.any().openapi({ type: "string", format: "binary" }) })
96
+ }
97
+ }
98
+ }
99
+ },
100
+ responses: {
101
+ [HttpStatusCodes.OK]: jsonContent(z.object({
102
+ message: z.string(),
103
+ data: z.object({
104
+ sheetName: z.string(),
105
+ headers: z.array(z.string()),
106
+ totalRows: z.number(),
107
+ preview: z.array(z.record(z.string(), z.union([z.string(), z.number(), z.null()])))
108
+ })
109
+ }), "Excel import preview")
110
+ }
111
+ });
112
+
113
+ const generateDownloadRoute = createRoute({
114
+ path: "/download/generate",
115
+ method: "post",
116
+ tags: ["Examples"],
117
+ responses: {
118
+ [HttpStatusCodes.OK]: jsonContent(DownloadTokenSchema, "Temporary download token")
119
+ }
120
+ });
121
+
122
+ const downloadRoute = createRoute({
123
+ path: "/download/{*token}",
124
+ method: "get",
125
+ tags: ["Examples"],
126
+ request: {
127
+ params: z.object({ token: z.string() })
128
+ },
129
+ responses: {
130
+ [HttpStatusCodes.OK]: { description: "Downloads generated file and deletes it" },
131
+ [HttpStatusCodes.NOT_FOUND]: jsonContent(z.object({ message: z.string() }), "Invalid token")
132
+ }
133
+ });
134
+
135
+ const generateExcelRoute = createRoute({
136
+ path: "/download/excel/generate",
137
+ method: "post",
138
+ tags: ["Examples"],
139
+ request: {
140
+ body: jsonContent(ExcelDownloadRequestSchema, "Excel generation payload")
141
+ },
142
+ responses: {
143
+ [HttpStatusCodes.OK]: jsonContent(DownloadTokenSchema, "Excel temporary download token")
144
+ }
145
+ });
146
+
147
+ const downloadExcelRoute = createRoute({
148
+ path: "/download/excel/{*token}",
149
+ method: "get",
150
+ tags: ["Examples"],
151
+ request: {
152
+ params: z.object({ token: z.string() })
153
+ },
154
+ responses: {
155
+ [HttpStatusCodes.OK]: { description: "Downloads generated Excel file and deletes it" },
156
+ [HttpStatusCodes.NOT_FOUND]: jsonContent(z.object({ message: z.string() }), "Invalid token")
157
+ }
158
+ });
159
+
160
+ const generatePdfRoute = createRoute({
161
+ path: "/download/pdf/generate",
162
+ method: "post",
163
+ tags: ["Examples"],
164
+ request: {
165
+ body: jsonContent(PdfGenerateRequestSchema, "Hybrid PDF generation payload")
166
+ },
167
+ responses: {
168
+ [HttpStatusCodes.ACCEPTED]: jsonContent(PdfGenerateAcceptedSchema, "Queued or inline PDF response")
169
+ }
170
+ });
171
+
172
+ const downloadPdfRoute = createRoute({
173
+ path: "/download/pdf/{requestId}",
174
+ method: "get",
175
+ tags: ["Examples"],
176
+ request: {
177
+ params: z.object({ requestId: z.string() })
178
+ },
179
+ responses: {
180
+ [HttpStatusCodes.OK]: { description: "Downloads generated PDF" },
181
+ [HttpStatusCodes.ACCEPTED]: jsonContent(z.object({ message: z.string(), state: z.string() }), "Still generating"),
182
+ [HttpStatusCodes.NOT_FOUND]: jsonContent(z.object({ message: z.string() }), "Unknown request")
183
+ }
184
+ });
185
+
186
+ const enabled = env.ENABLE_EXAMPLES;
187
+ const router = createRouter();
188
+
189
+ if (enabled) {
190
+ router
191
+ .api(eventRoute, fire)
192
+ .api(queueCommandRoute, queueCommand)
193
+ .api(queuedEventRoute, fireQueued)
194
+ .api(queuedBroadcastEventRoute, fireQueuedBroadcast)
195
+ .api(broadcastRoute, fireBroadcast)
196
+ .api(uploadRoute, upload)
197
+ .api(importExcelRoute, importExcel)
198
+ .api(generateDownloadRoute, generateDownload)
199
+ .api(downloadRoute, downloadGenerated)
200
+ .api(generateExcelRoute, generateExcelDownload)
201
+ .api(downloadExcelRoute, downloadGeneratedExcel)
202
+ .api(generatePdfRoute, generatePdfDownload)
203
+ .api(downloadPdfRoute, downloadGeneratedPdf);
204
+ }
205
+
206
+ export default router;
207
+