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,62 @@
1
+ import type { Request, Response } from "express";
2
+ import { appConfig, cookieConfig, jwtConfig } from "@/config/index.js";
3
+
4
+ function originOf(url?: string) {
5
+ if (!url) return "";
6
+ try {
7
+ return new URL(url).origin;
8
+ } catch {
9
+ return "";
10
+ }
11
+ }
12
+
13
+ function needsCrossSiteCookie() {
14
+ const appOrigin = originOf(appConfig.url);
15
+ const frontendOrigin = originOf(appConfig.frontendUrl);
16
+ return Boolean(appOrigin && frontendOrigin && appOrigin !== frontendOrigin);
17
+ }
18
+
19
+ function baseOptions() {
20
+ const crossSiteCookie = needsCrossSiteCookie();
21
+ const sameSite: "none" | "lax" = crossSiteCookie ? "none" : "lax";
22
+ return {
23
+ httpOnly: true,
24
+ secure: crossSiteCookie,
25
+ sameSite,
26
+ path: "/"
27
+ };
28
+ }
29
+
30
+ export const cookie = {
31
+ async setAuth(res: Response, token: string) {
32
+ res.cookie(`${cookieConfig.name}_access`, token, {
33
+ ...baseOptions(),
34
+ maxAge: jwtConfig.accessExpirySeconds * 1000,
35
+ signed: true
36
+ });
37
+ },
38
+
39
+ async setRefresh(res: Response, token: string, maxAge?: number) {
40
+ res.cookie(`${cookieConfig.name}_refresh`, token, {
41
+ ...baseOptions(),
42
+ maxAge: (typeof maxAge === "number" ? maxAge : jwtConfig.refreshExpirySeconds) * 1000,
43
+ signed: true
44
+ });
45
+ },
46
+
47
+ async getAuth(req: Request) {
48
+ return req.signedCookies?.[`${cookieConfig.name}_access`];
49
+ },
50
+
51
+ async getRefresh(req: Request) {
52
+ return req.signedCookies?.[`${cookieConfig.name}_refresh`];
53
+ },
54
+
55
+ deleteAuth(res: Response) {
56
+ res.clearCookie(`${cookieConfig.name}_access`, { path: "/" });
57
+ },
58
+
59
+ deleteRefresh(res: Response) {
60
+ res.clearCookie(`${cookieConfig.name}_refresh`, { path: "/" });
61
+ }
62
+ };
@@ -0,0 +1,33 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import jwtLib from "jsonwebtoken";
3
+ import { jwtConfig } from "@/config/index.js";
4
+
5
+ export const jwt = {
6
+ async generateToken(payload: any, type: "access" | "refresh", expirySeconds?: number) {
7
+ const now = Math.floor(Date.now() / 1000);
8
+ const exp =
9
+ now +
10
+ (typeof expirySeconds === "number"
11
+ ? expirySeconds
12
+ : type === "refresh"
13
+ ? jwtConfig.refreshExpirySeconds
14
+ : jwtConfig.accessExpirySeconds);
15
+ const secret = type === "refresh" ? jwtConfig.refreshSecret : jwtConfig.accessSecret;
16
+ const jti = type === "refresh" ? randomUUID() : undefined;
17
+
18
+ const tokenPayload = { ...payload, type, iat: now, exp, ...(jti ? { jti } : {}) };
19
+ const token = await jwtLib.sign(tokenPayload, secret, { algorithm: jwtConfig.algorithm });
20
+
21
+ return { token, jti, exp };
22
+ },
23
+
24
+ async verifyToken(token: string, type: "access" | "refresh") {
25
+ try {
26
+ const secret = type === "refresh" ? jwtConfig.refreshSecret : jwtConfig.accessSecret;
27
+ const payload = (await jwtLib.verify(token, secret, { algorithms: [jwtConfig.algorithm] })) as Record<string, unknown>;
28
+ return payload.type === type ? payload : null;
29
+ } catch {
30
+ return null;
31
+ }
32
+ }
33
+ };
@@ -0,0 +1,35 @@
1
+ const shutdownSignals = ["SIGINT", "SIGTERM"] as const;
2
+ export type ShutdownSignal = (typeof shutdownSignals)[number];
3
+
4
+ type ShutdownHandler = (signal: ShutdownSignal) => Promise<void>;
5
+
6
+ /**
7
+ * Why: Avoid duplicated graceful-shutdown wiring across runtime entrypoints.
8
+ * When: Server/worker/scheduler need signal handling.
9
+ * Where: Runtime bootstrap files.
10
+ * How: Registers SIGINT/SIGTERM handlers and forwards signal to async callback.
11
+ */
12
+ export function registerShutdownSignals(handler: ShutdownHandler) {
13
+ for (const signal of shutdownSignals) {
14
+ process.on(signal, () => {
15
+ void handler(signal);
16
+ });
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Why: Keep comma-separated CLI/env parsing consistent.
22
+ * When: Runtime options are provided as a single CSV string.
23
+ * Where: Worker and other entrypoints.
24
+ * How: Trims tokens, removes blanks, and falls back when empty.
25
+ */
26
+ export function parseCsvOrFallback(value: string | undefined, fallback: string[]) {
27
+ if (!value) return fallback;
28
+
29
+ const parsed = value
30
+ .split(",")
31
+ .map((item) => item.trim())
32
+ .filter(Boolean);
33
+
34
+ return parsed.length ? parsed : fallback;
35
+ }
@@ -0,0 +1,148 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { Writable } from "node:stream";
4
+ import pino from "pino";
5
+ import { appConfig, loggingConfig } from "@/config/index.js";
6
+
7
+ const logDir = path.resolve(process.cwd(), "src", "storage", "logs");
8
+ const MAX_FILE_BYTES = 10 * 1024 * 1024;
9
+
10
+ if (!fs.existsSync(logDir)) {
11
+ fs.mkdirSync(logDir, { recursive: true });
12
+ }
13
+
14
+ const LEVEL_COLORS: Record<number, string> = {
15
+ 10: "\x1b[90m",
16
+ 20: "\x1b[90m",
17
+ 30: "\x1b[32m",
18
+ 40: "\x1b[33m",
19
+ 50: "\x1b[31m",
20
+ 60: "\x1b[31m"
21
+ };
22
+
23
+ const RESET = "\x1b[0m";
24
+
25
+ function formatLine(chunk: string, colorize: boolean): string {
26
+ const line = chunk.trim();
27
+ if (!line) return "";
28
+
29
+ let record: Record<string, unknown>;
30
+ try {
31
+ record = JSON.parse(line);
32
+ } catch {
33
+ return colorize ? line : `${line}\n`;
34
+ }
35
+
36
+ const time = typeof record.time === "number" ? new Date(record.time) : new Date(String(record.time));
37
+ const timestamp = Number.isNaN(time.getTime()) ? "" : `[${time.toLocaleTimeString("en-GB", { hour12: false })}] `;
38
+ const levelNumber = typeof record.level === "number" ? record.level : 30;
39
+ const label = pino.levels.labels[levelNumber] ?? "info";
40
+ const message = typeof record.msg === "string" ? record.msg : "";
41
+
42
+ const ignored = new Set(["time", "level", "msg", "pid", "hostname", "service"]);
43
+ const meta: Record<string, unknown> = {};
44
+ for (const [key, value] of Object.entries(record)) {
45
+ if (!ignored.has(key)) meta[key] = value;
46
+ }
47
+
48
+ const level = colorize ? `${LEVEL_COLORS[levelNumber] ?? ""}${label}${RESET}` : label;
49
+ const metaString = Object.keys(meta).length ? `\n${JSON.stringify(meta, null, 2)}` : "";
50
+ return `${timestamp}${level} -> ${message}${metaString}\n`;
51
+ }
52
+
53
+ function consoleWriter() {
54
+ return new Writable({
55
+ write(chunk, _encoding, callback) {
56
+ const text = formatLine(chunk.toString("utf8"), true);
57
+ if (text) process.stdout.write(text);
58
+ callback();
59
+ }
60
+ });
61
+ }
62
+
63
+ function rotate(filePath: string, maxFiles: number) {
64
+ for (let index = maxFiles - 1; index >= 1; index--) {
65
+ const from = `${filePath}.${index}`;
66
+ const to = `${filePath}.${index + 1}`;
67
+ if (fs.existsSync(from)) fs.renameSync(from, to);
68
+ }
69
+ if (fs.existsSync(filePath)) fs.renameSync(filePath, `${filePath}.1`);
70
+ }
71
+
72
+ function fileWriter(filePath: string, maxFiles: number) {
73
+ let bytes = 0;
74
+
75
+ return new Writable({
76
+ write(chunk, _encoding, callback) {
77
+ const text = formatLine(chunk.toString("utf8"), false);
78
+ bytes += Buffer.byteLength(text);
79
+ if (bytes >= MAX_FILE_BYTES) {
80
+ rotate(filePath, maxFiles);
81
+ bytes = 0;
82
+ }
83
+ try {
84
+ fs.appendFileSync(filePath, text);
85
+ } catch {
86
+ // best-effort file logging
87
+ }
88
+ callback();
89
+ }
90
+ });
91
+ }
92
+
93
+ const base = pino(
94
+ {
95
+ level: loggingConfig.level,
96
+ base: { service: appConfig.name },
97
+ timestamp: pino.stdTimeFunctions.isoTime
98
+ },
99
+ pino.multistream([
100
+ { stream: consoleWriter(), level: loggingConfig.level },
101
+ { stream: fileWriter(path.join(logDir, "app.log"), 5), level: "info" },
102
+ { stream: fileWriter(path.join(logDir, "fatal.log"), 3), level: "error" }
103
+ ])
104
+ );
105
+
106
+ type CompatLogger = {
107
+ fatal: (message: string, meta?: Record<string, unknown>) => void;
108
+ error: (message: string, meta?: Record<string, unknown>) => void;
109
+ warn: (message: string, meta?: Record<string, unknown>) => void;
110
+ info: (message: string, meta?: Record<string, unknown>) => void;
111
+ debug: (message: string, meta?: Record<string, unknown>) => void;
112
+ trace: (message: string, meta?: Record<string, unknown>) => void;
113
+ child: (bindings: Record<string, unknown>) => CompatLogger;
114
+ };
115
+
116
+ function compat(child: pino.Logger): CompatLogger {
117
+ const wrap = (log: (obj: object, msg?: string, ...args: unknown[]) => void) => (message: string, meta?: Record<string, unknown>) =>
118
+ log.call(child, meta ?? {}, message);
119
+
120
+ return {
121
+ fatal: wrap(child.fatal),
122
+ error: wrap(child.error),
123
+ warn: wrap(child.warn),
124
+ info: wrap(child.info),
125
+ debug: wrap(child.debug),
126
+ trace: wrap(child.trace),
127
+ child: (bindings) => compat(child.child(bindings))
128
+ };
129
+ }
130
+
131
+ /**
132
+ * Why: Shared structured logger for app/runtime/framework internals.
133
+ * When: Any code needs operational logs or error reporting.
134
+ * Where: Imported across framework and modules.
135
+ * How: Emits JSON via pino to a color console sink plus rotating app/fatal
136
+ * files, keeping the legacy `(message, meta)` call signature.
137
+ */
138
+ export const logger: CompatLogger = compat(base);
139
+
140
+ process.on("uncaughtException", (error) => {
141
+ logger.error("Uncaught exception", { error: { name: error.name, message: error.message, stack: error.stack } });
142
+ process.exit(1);
143
+ });
144
+
145
+ process.on("unhandledRejection", (reason) => {
146
+ const error = reason instanceof Error ? reason : new Error(String(reason));
147
+ logger.error("Unhandled rejection", { error: { name: error.name, message: error.message, stack: error.stack } });
148
+ });
@@ -0,0 +1,48 @@
1
+ import nodemailer from "nodemailer";
2
+ import { mailConfig } from "@/config/index.js";
3
+ import { logger } from "@/framework/support/logger.js";
4
+ import { CircuitBreaker } from "@/framework/circuit-breaker/cb.js";
5
+
6
+ type MailPayload = {
7
+ to: string;
8
+ subject: string;
9
+ html?: string;
10
+ text?: string;
11
+ };
12
+
13
+ const transport = nodemailer.createTransport({
14
+ host: mailConfig.host,
15
+ port: mailConfig.port,
16
+ secure: mailConfig.encryption === "ssl",
17
+ auth: mailConfig.username ? { user: mailConfig.username, pass: mailConfig.password } : undefined
18
+ });
19
+
20
+ const mailBreaker = new CircuitBreaker();
21
+
22
+ export const mail = {
23
+ /**
24
+ * Why: Sends transactional email through configured SMTP transport.
25
+ * When: Features need notifications/password reset/signup email.
26
+ * Where: Jobs and event handlers.
27
+ * How: Uses nodemailer transport and respects the fail-silent setting.
28
+ */
29
+ async sendMail(payload: MailPayload) {
30
+ try {
31
+ return await mailBreaker.exec(() =>
32
+ transport.sendMail({
33
+ from: mailConfig.fromAddress,
34
+ ...payload
35
+ })
36
+ );
37
+ } catch (error) {
38
+ logger.error("Mail send failed", {
39
+ to: payload.to,
40
+ subject: payload.subject,
41
+ error: error instanceof Error ? error.message : error
42
+ });
43
+
44
+ if (!mailConfig.failSilent) throw error;
45
+ return null;
46
+ }
47
+ }
48
+ };
@@ -0,0 +1,23 @@
1
+ import bcrypt from "bcryptjs";
2
+
3
+ export const password = {
4
+ /**
5
+ * Why: Hashes plaintext password before persistence.
6
+ * When: Registration and password reset flows.
7
+ * Where: Auth controllers/seeders.
8
+ * How: Uses bcrypt with cost factor 10.
9
+ */
10
+ async hashPassword(inputPassword: string) {
11
+ return await bcrypt.hash(inputPassword, 10);
12
+ },
13
+
14
+ /**
15
+ * Why: Compares plaintext password to stored hash.
16
+ * When: Login/authentication checks.
17
+ * Where: Auth controllers/middleware.
18
+ * How: Uses bcrypt secure compare.
19
+ */
20
+ async verifyPassword(inputPassword: string, hashedPassword: string) {
21
+ return await bcrypt.compare(inputPassword, hashedPassword);
22
+ }
23
+ };
@@ -0,0 +1,160 @@
1
+ import path from "node:path";
2
+ import type { RequestHandler } from "express";
3
+ import createError from "http-errors";
4
+ import multer from "multer";
5
+
6
+ export type UploadOptions = {
7
+ /**
8
+ * Form field name that carries the file(s). Matches the client's FormData key.
9
+ */
10
+ field: string;
11
+ /**
12
+ * When set, accepts an array of up to `multiple` files on that field.
13
+ * Omit for a single-file upload (parsed into `req.file`).
14
+ */
15
+ multiple?: number;
16
+ /**
17
+ * Maximum allowed file size in bytes. Defaults to unlimited.
18
+ */
19
+ maxSize?: number;
20
+ /**
21
+ * Allowed file extensions (lowercase, with dot), e.g. [".xlsx", ".xlsb"].
22
+ * Rejects others with a 422 message.
23
+ */
24
+ allowedExtensions?: string[];
25
+ /**
26
+ * Allowed MIME types, e.g. ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"].
27
+ * When provided alongside extensions, a file must satisfy both.
28
+ */
29
+ allowedMimeTypes?: string[];
30
+ };
31
+
32
+ export type UploadFieldsSpec = Array<{ name: string; maxCount?: number }>;
33
+
34
+ function humanSize(bytes: number): string {
35
+ const units = ["B", "KB", "MB", "GB", "TB"];
36
+ let i = 0;
37
+ let size = bytes;
38
+ while (size >= 1024 && i < units.length - 1) {
39
+ size /= 1024;
40
+ i += 1;
41
+ }
42
+ return `${Math.round(size * 100) / 100} ${units[i]}`;
43
+ }
44
+
45
+ /**
46
+ * Why: Parses multipart uploads as a reusable Express middleware factory.
47
+ * When: Any route needs to accept file uploads (single, multiple, or labeled fields).
48
+ * Where: Route files via `.api(route, [requireRole(...), upload({...})], handler)`.
49
+ * How: Wraps `multer` (memory storage) so controllers receive `req.file` /
50
+ * `req.files` buffers and enforce size/extension/MIME rules; failures are
51
+ * normalized into readable 422 errors before the controller runs.
52
+ */
53
+ export function upload(options: UploadOptions): RequestHandler {
54
+ const { field, multiple, maxSize, allowedExtensions, allowedMimeTypes } = options;
55
+ const maxCount = multiple ?? 1;
56
+
57
+ const parser = multer({
58
+ storage: multer.memoryStorage(),
59
+ limits: {
60
+ fileSize: maxSize ?? Infinity,
61
+ files: maxCount
62
+ },
63
+ fileFilter: (_req, file, cb) => {
64
+ const ext = path.extname(file.originalname).toLowerCase();
65
+ if (allowedExtensions?.length && !allowedExtensions.includes(ext)) {
66
+ cb(createError(422, `File must be ${allowedExtensions.join(" or ")}`));
67
+ return;
68
+ }
69
+ if (allowedMimeTypes?.length && !allowedMimeTypes.includes(file.mimetype)) {
70
+ cb(createError(422, `File type ${file.mimetype} is not allowed`));
71
+ return;
72
+ }
73
+ cb(null, true);
74
+ }
75
+ });
76
+
77
+ const parse = multiple ? parser.array(field, maxCount) : parser.single(field);
78
+
79
+ return (req, res, next) => {
80
+ parse(req, res, (error) => {
81
+ if (error) {
82
+ if (error instanceof multer.MulterError) {
83
+ if (error.code === "LIMIT_FILE_SIZE") {
84
+ next(createError(422, `File size must be less than ${humanSize(maxSize ?? 0)}`));
85
+ return;
86
+ }
87
+ if (error.code === "LIMIT_FILE_COUNT" || error.code === "LIMIT_UNEXPECTED_FILE") {
88
+ next(createError(422, `Too many files, max ${maxCount} allowed`));
89
+ return;
90
+ }
91
+ next(createError(422, error.message));
92
+ return;
93
+ }
94
+ next(error);
95
+ return;
96
+ }
97
+
98
+ const received = multiple ? Array.isArray(req.files) && req.files.length > 0 : Boolean(req.file);
99
+
100
+ if (!received) {
101
+ next(createError(422, "File required"));
102
+ return;
103
+ }
104
+
105
+ next();
106
+ });
107
+ };
108
+ }
109
+
110
+ /**
111
+ * Why: Parses multipart forms carrying multiple labeled file fields at once.
112
+ * When: A form uploads several different inputs (e.g. avatar + document) in one request.
113
+ * Where: Route files via `.api(route, [fields([{ name: "avatar" }, { name: "docs", maxCount: 5 }])], handler)`.
114
+ * How: Uses `multer.fields()` and applies shared size/MIME/extension limits to every file.
115
+ */
116
+ export function fields(spec: UploadFieldsSpec, options: Omit<UploadOptions, "field" | "multiple"> = {}): RequestHandler {
117
+ const { maxSize, allowedExtensions, allowedMimeTypes } = options;
118
+
119
+ const parser = multer({
120
+ storage: multer.memoryStorage(),
121
+ limits: { fileSize: maxSize ?? Infinity },
122
+ fileFilter: (_req, file, cb) => {
123
+ const ext = path.extname(file.originalname).toLowerCase();
124
+ if (allowedExtensions?.length && !allowedExtensions.includes(ext)) {
125
+ cb(createError(422, `File must be ${allowedExtensions.join(" or ")}`));
126
+ return;
127
+ }
128
+ if (allowedMimeTypes?.length && !allowedMimeTypes.includes(file.mimetype)) {
129
+ cb(createError(422, `File type ${file.mimetype} is not allowed`));
130
+ return;
131
+ }
132
+ cb(null, true);
133
+ }
134
+ }).fields(spec);
135
+
136
+ return (req, res, next) => {
137
+ parser(req, res, (error) => {
138
+ if (error) {
139
+ if (error instanceof multer.MulterError) {
140
+ if (error.code === "LIMIT_FILE_SIZE") {
141
+ next(createError(422, `File size must be less than ${humanSize(maxSize ?? 0)}`));
142
+ return;
143
+ }
144
+ next(createError(422, error.message));
145
+ return;
146
+ }
147
+ next(error);
148
+ return;
149
+ }
150
+
151
+ const files: Express.Multer.File[] = Object.values((req.files as Record<string, Express.Multer.File[]>) ?? {}).flat();
152
+ if (files.length === 0) {
153
+ next(createError(422, "File required"));
154
+ return;
155
+ }
156
+
157
+ next();
158
+ });
159
+ };
160
+ }
@@ -0,0 +1,41 @@
1
+ import { appConfig } from "@/config/index.js";
2
+ import { hasUiBuild } from "@/framework/http/static.js";
3
+
4
+ export const urls = {
5
+ /**
6
+ * Why: Returns normalized app base URL without trailing slash.
7
+ * When: Building absolute links.
8
+ * Where: Mail/reset URL generation.
9
+ * How: Trims trailing slash from the configured app URL.
10
+ */
11
+ appUrl() {
12
+ return appConfig.url.replace(/\/$/, "");
13
+ },
14
+
15
+ /**
16
+ * Why: Builds absolute URL pointing at the SPA page that handles the path.
17
+ * When: Creating links for emails / external callbacks (reset, verify).
18
+ * Where: Auth/password reset and integrations.
19
+ * How: Three independent knobs decide the origin —
20
+ * appConfig.frontendUrl: the SPA is a separate build/origin (dev Vite
21
+ * dev server, or a separately deployed SPA). Link there.
22
+ * appConfig.uiEnabled + hasUiBuild(): the framework serves the built
23
+ * SPA itself, so the page resolves to the app origin.
24
+ * appConfig.uiEnabled + no UI build: the Vite dev server is still
25
+ * running (UI not built yet) — use the origin the maker-cli
26
+ * injected (NEXWIRE_FRONTEND_URL, dev Vite dev server).
27
+ * When BOTH origin knobs are unset (API-only mode) there is no SPA to
28
+ * point at — fall back to the app URL so the link is valid.
29
+ */
30
+ url(path = "") {
31
+ const base =
32
+ appConfig.frontendUrl || // explicit SPA origin — wins
33
+ (appConfig.uiEnabled
34
+ ? hasUiBuild()
35
+ ? urls.appUrl() // built SPA, framework serves it — same origin
36
+ : process.env.NEXWIRE_FRONTEND_URL || urls.appUrl() // dev Vite dev server — injected by maker-cli
37
+ : appConfig.url); // API-only
38
+ if (!path) return base;
39
+ return `${base}${path.startsWith("/") ? path : `/${path}`}`;
40
+ }
41
+ };
@@ -0,0 +1,98 @@
1
+ import { eq } from "drizzle-orm";
2
+ import { type NextFunction, type Request, type Response } from "express";
3
+ import { cookie, db, jwt, HttpStatusCodes } from "@/framework/facade.js";
4
+ import { refreshTokens, users } from "@/modules/auth/database/models/user.js";
5
+
6
+ export async function authMiddleware(req: Request, res: Response, next: NextFunction) {
7
+ const accessToken = await cookie.getAuth(req);
8
+
9
+ if (accessToken) {
10
+ const accessPayload = await jwt.verifyToken(accessToken, "access");
11
+
12
+ if (accessPayload) {
13
+ const user = await db.query.users.findFirst({
14
+ where: eq(users.id, accessPayload.id as number),
15
+ with: { role: true }
16
+ });
17
+
18
+ if (!user) {
19
+ cookie.deleteAuth(res);
20
+ cookie.deleteRefresh(res);
21
+ return res.status(HttpStatusCodes.UNAUTHORIZED).json({ message: "Unauthorized" });
22
+ }
23
+
24
+ res.locals.auth = {
25
+ ...accessPayload,
26
+ id: user.id,
27
+ email: user.email,
28
+ roleId: user.role?.id ?? null,
29
+ role: user.role?.name ?? null
30
+ };
31
+ return next();
32
+ }
33
+ }
34
+
35
+ const refreshToken = await cookie.getRefresh(req);
36
+
37
+ if (!refreshToken) {
38
+ cookie.deleteAuth(res);
39
+ cookie.deleteRefresh(res);
40
+ return res.status(HttpStatusCodes.UNAUTHORIZED).json({ message: "Unauthorized" });
41
+ }
42
+
43
+ const refreshPayload = await jwt.verifyToken(refreshToken, "refresh");
44
+
45
+ if (!refreshPayload?.jti) {
46
+ cookie.deleteAuth(res);
47
+ cookie.deleteRefresh(res);
48
+ return res.status(HttpStatusCodes.UNAUTHORIZED).json({ message: "Invalid token" });
49
+ }
50
+
51
+ const storedToken = await db.query.refreshTokens.findFirst({
52
+ where: eq(refreshTokens.jti, refreshPayload.jti as string)
53
+ });
54
+
55
+ if (!storedToken || storedToken.revoked) {
56
+ cookie.deleteAuth(res);
57
+ cookie.deleteRefresh(res);
58
+ return res.status(HttpStatusCodes.UNAUTHORIZED).json({ message: "Invalid token" });
59
+ }
60
+
61
+ if (storedToken.expiresAt.getTime() < Date.now()) {
62
+ await db.delete(refreshTokens).where(eq(refreshTokens.id, storedToken.id));
63
+ cookie.deleteAuth(res);
64
+ cookie.deleteRefresh(res);
65
+ return res.status(HttpStatusCodes.UNAUTHORIZED).json({ message: "Invalid token" });
66
+ }
67
+
68
+ const user = await db.query.users.findFirst({
69
+ where: eq(users.id, refreshPayload.id as number),
70
+ with: { role: true }
71
+ });
72
+
73
+ if (!user) {
74
+ cookie.deleteAuth(res);
75
+ cookie.deleteRefresh(res);
76
+ return res.status(HttpStatusCodes.UNAUTHORIZED).json({ message: "Unauthorized" });
77
+ }
78
+
79
+ const newAccessToken = await jwt.generateToken(
80
+ {
81
+ id: user.id,
82
+ email: user.email,
83
+ roleId: user.role?.id ?? null,
84
+ role: user.role?.name ?? null
85
+ },
86
+ "access"
87
+ );
88
+
89
+ await cookie.setAuth(res, newAccessToken.token);
90
+ res.locals.auth = {
91
+ id: user.id,
92
+ email: user.email,
93
+ roleId: user.role?.id ?? null,
94
+ role: user.role?.name ?? null,
95
+ type: "access"
96
+ };
97
+ return next();
98
+ }
@@ -0,0 +1,25 @@
1
+ import type { NextFunction, Request, Response } from "express";
2
+ import { HttpStatusCodes } from "@/framework/facade.js";
3
+
4
+ type AuthPayload = {
5
+ role?: string | null;
6
+ };
7
+
8
+ export function requireRole(...allowedRoles: string[]) {
9
+ const roles = allowedRoles.map((role) => role.toLowerCase());
10
+
11
+ return async (_req: Request, res: Response, next: NextFunction) => {
12
+ const auth = res.locals.auth as AuthPayload | undefined;
13
+
14
+ if (!auth) {
15
+ return res.status(HttpStatusCodes.UNAUTHORIZED).json({ message: "Unauthorized" });
16
+ }
17
+
18
+ const userRole = String(auth.role ?? "").toLowerCase();
19
+ if (!roles.includes(userRole)) {
20
+ return res.status(HttpStatusCodes.FORBIDDEN).json({ message: "Forbidden: Insufficient permissions" });
21
+ }
22
+
23
+ return next();
24
+ };
25
+ }