create-nexwire 4.2.5 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (399) hide show
  1. package/README.md +29 -12
  2. package/express/.env +1 -1
  3. package/express/package.json +1 -1
  4. package/express/src/config/database.ts +48 -29
  5. package/express/src/config/index.ts +83 -83
  6. package/express/src/config/openapi.ts +6 -24
  7. package/express/src/config/queue.ts +30 -30
  8. package/express/src/config/validate.ts +50 -40
  9. package/express/src/database/migrations/sqlite/0000_init.sql +47 -47
  10. package/express/src/database/migrations/sqlite/meta/0000_snapshot.json +1 -1
  11. package/express/src/database/migrations/sqlite/meta/_journal.json +1 -1
  12. package/express/src/framework/cache/cache.ts +40 -8
  13. package/express/src/framework/circuit-breaker/cb.ts +88 -60
  14. package/express/src/framework/database/connection.ts +52 -27
  15. package/express/src/framework/events/dispatcher.ts +61 -49
  16. package/express/src/framework/facade.ts +47 -3
  17. package/express/src/framework/http/app.ts +28 -22
  18. package/express/src/framework/http/logger.ts +37 -17
  19. package/express/src/framework/http/openapi.ts +9 -11
  20. package/express/src/framework/maker-cli/runtime/core.mjs +1 -2
  21. package/express/src/framework/modules/routes.ts +26 -21
  22. package/express/src/framework/notification/channels/broadcast.ts +15 -0
  23. package/express/src/framework/notification/channels/mail.ts +42 -0
  24. package/express/src/framework/notification/contracts.ts +29 -0
  25. package/express/src/framework/notification/index.ts +19 -94
  26. package/express/src/framework/notification/registry.ts +15 -0
  27. package/express/src/framework/providers/adapter.ts +27 -17
  28. package/express/src/framework/queue/queue.ts +43 -44
  29. package/express/src/framework/realtime/broadcast.ts +29 -5
  30. package/express/src/framework/realtime/socket.ts +149 -119
  31. package/express/src/framework/redis/client.ts +148 -151
  32. package/express/src/framework/runtime/server-adapter.ts +39 -34
  33. package/express/src/framework/scheduler/lock.ts +128 -116
  34. package/express/src/framework/server.ts +33 -49
  35. package/express/src/framework/session/session.ts +69 -14
  36. package/express/src/framework/storage/storage.ts +744 -785
  37. package/express/src/framework/support/mail.ts +15 -7
  38. package/express/src/middlewares/auth-middleware.ts +1 -2
  39. package/express/src/middlewares/role-middleware.ts +3 -13
  40. package/express/src/modules/auth/routes/index.ts +1 -2
  41. package/express/src/providers/notification.ts +4 -0
  42. package/express/src/resources/src/components/Switch.vue +60 -65
  43. package/express/src/resources/src/layouts/Layout/Header.vue +124 -105
  44. package/express/src/resources/src/pages/auth/forgetPassword.vue +23 -30
  45. package/express/src/resources/src/pages/auth/login.vue +25 -28
  46. package/express/src/resources/src/pages/auth/register.vue +35 -39
  47. package/express/src/resources/src/pages/auth/resetPassword.vue +29 -37
  48. package/express/src/resources/src/pages/auth/verifyEmail.vue +15 -19
  49. package/express/src/resources/src/plugins/gum.ts +519 -203
  50. package/express/src/resources/src/plugins/pulse.ts +157 -147
  51. package/express/src/resources/src/stores/auth.ts +9 -6
  52. package/express/src/resources/vite.config.ts +54 -43
  53. package/express/src/storage/database/nexwire.sqlite +0 -0
  54. package/express/src/storage/logs/app.log +0 -0
  55. package/express/src/storage/logs/fatal.log +0 -0
  56. package/express/src/types/global.d.ts +32 -26
  57. package/fastify/.dockerignore +14 -0
  58. package/fastify/.env +44 -0
  59. package/fastify/.env.example +42 -0
  60. package/fastify/.vscode/extensions.json +6 -0
  61. package/fastify/.vscode/settings.json +37 -0
  62. package/fastify/LICENSE +21 -0
  63. package/fastify/README.md +232 -0
  64. package/fastify/biome.json +99 -0
  65. package/fastify/drizzle.config.ts +25 -0
  66. package/fastify/gitignore-stub +31 -0
  67. package/fastify/package.json +130 -0
  68. package/fastify/public/.gitkeep +0 -0
  69. package/fastify/src/config/app.ts +41 -0
  70. package/fastify/src/config/auth.ts +12 -0
  71. package/fastify/src/config/cache.ts +16 -0
  72. package/fastify/src/config/cookie.ts +15 -0
  73. package/fastify/src/config/cors.ts +12 -0
  74. package/fastify/src/config/database.ts +48 -0
  75. package/fastify/src/config/index.ts +83 -0
  76. package/fastify/src/config/jwt.ts +19 -0
  77. package/fastify/src/config/logging.ts +13 -0
  78. package/fastify/src/config/mail.ts +24 -0
  79. package/fastify/src/config/openapi.ts +36 -0
  80. package/fastify/src/config/queue.ts +30 -0
  81. package/fastify/src/config/rateLimit.ts +17 -0
  82. package/fastify/src/config/realtime.ts +15 -0
  83. package/fastify/src/config/redis.ts +18 -0
  84. package/fastify/src/config/security.ts +44 -0
  85. package/fastify/src/config/session.ts +17 -0
  86. package/fastify/src/config/storage.ts +22 -0
  87. package/fastify/src/config/validate.ts +50 -0
  88. package/fastify/src/database/migrations/sqlite/0000_init.sql +48 -0
  89. package/fastify/src/database/migrations/sqlite/meta/0000_snapshot.json +324 -0
  90. package/fastify/src/database/migrations/sqlite/meta/_journal.json +13 -0
  91. package/fastify/src/database/schema.ts +2 -0
  92. package/fastify/src/env.ts +76 -0
  93. package/fastify/src/framework/cache/cache.ts +114 -0
  94. package/fastify/src/framework/circuit-breaker/cb.ts +88 -0
  95. package/fastify/src/framework/database/app-schema.ts +37 -0
  96. package/fastify/src/framework/database/connection.ts +223 -0
  97. package/fastify/src/framework/database/migrate-hooks.ts +87 -0
  98. package/fastify/src/framework/database/paginate.ts +226 -0
  99. package/fastify/src/framework/database/schema.ts +20 -0
  100. package/fastify/src/framework/database/seed.ts +152 -0
  101. package/fastify/src/framework/events/dispatcher.ts +61 -0
  102. package/fastify/src/framework/facade.ts +96 -0
  103. package/fastify/src/framework/http/app.ts +176 -0
  104. package/fastify/src/framework/http/cors.ts +13 -0
  105. package/fastify/src/framework/http/fastify.d.ts +13 -0
  106. package/fastify/src/framework/http/logger.ts +110 -0
  107. package/fastify/src/framework/http/metrics.ts +22 -0
  108. package/fastify/src/framework/http/openapi.ts +48 -0
  109. package/fastify/src/framework/http/ratelimiter.ts +47 -0
  110. package/fastify/src/framework/http/security.ts +25 -0
  111. package/fastify/src/framework/http/static.ts +83 -0
  112. package/fastify/src/framework/http/validation.ts +49 -0
  113. package/fastify/src/framework/kernel.ts +43 -0
  114. package/fastify/src/framework/maker-cli/db/core.mjs +291 -0
  115. package/fastify/src/framework/maker-cli/db/index.mjs +204 -0
  116. package/fastify/src/framework/maker-cli/deploy/core.mjs +1046 -0
  117. package/fastify/src/framework/maker-cli/deploy/index.mjs +91 -0
  118. package/fastify/src/framework/maker-cli/index.mjs +65 -0
  119. package/fastify/src/framework/maker-cli/middleware/core.mjs +41 -0
  120. package/fastify/src/framework/maker-cli/middleware/index.mjs +17 -0
  121. package/fastify/src/framework/maker-cli/module/core.mjs +1066 -0
  122. package/fastify/src/framework/maker-cli/module/index.mjs +211 -0
  123. package/fastify/src/framework/maker-cli/provider/core.mjs +49 -0
  124. package/fastify/src/framework/maker-cli/provider/index.mjs +12 -0
  125. package/fastify/src/framework/maker-cli/runtime/build-ui.mjs +16 -0
  126. package/fastify/src/framework/maker-cli/runtime/core.mjs +560 -0
  127. package/fastify/src/framework/maker-cli/runtime/index.mjs +120 -0
  128. package/fastify/src/framework/maker-cli/runtime/redis-commander-patch.cjs +20 -0
  129. package/fastify/src/framework/maker-cli/stubs/controller/openapi.ts.stub +74 -0
  130. package/fastify/src/framework/maker-cli/stubs/controller/plain.ts.stub +77 -0
  131. package/fastify/src/framework/maker-cli/stubs/controller/standalone.ts.stub +51 -0
  132. package/fastify/src/framework/maker-cli/stubs/deploy/Dockerfile.bun.stub +52 -0
  133. package/fastify/src/framework/maker-cli/stubs/deploy/Dockerfile.pnpm.stub +55 -0
  134. package/fastify/src/framework/maker-cli/stubs/deploy/Dockerfile.stub +51 -0
  135. package/fastify/src/framework/maker-cli/stubs/deploy/Dockerfile.yarn.stub +55 -0
  136. package/fastify/src/framework/maker-cli/stubs/deploy/README.stub +59 -0
  137. package/fastify/src/framework/maker-cli/stubs/deploy/compose/mysql.server.stub +45 -0
  138. package/fastify/src/framework/maker-cli/stubs/deploy/compose/postgres.server.stub +45 -0
  139. package/fastify/src/framework/maker-cli/stubs/deploy/compose/sqlite.stub +45 -0
  140. package/fastify/src/framework/maker-cli/stubs/deploy/env/mysql.server.stub +68 -0
  141. package/fastify/src/framework/maker-cli/stubs/deploy/env/postgres.server.stub +68 -0
  142. package/fastify/src/framework/maker-cli/stubs/deploy/env/sqlite.stub +67 -0
  143. package/fastify/src/framework/maker-cli/stubs/deploy/scripts/auto-migrate.sh.stub +41 -0
  144. package/fastify/src/framework/maker-cli/stubs/deploy/server/README.stub +78 -0
  145. package/fastify/src/framework/maker-cli/stubs/deploy/server/compose/noredis.stub +120 -0
  146. package/fastify/src/framework/maker-cli/stubs/deploy/server/compose/redis.dev.stub +133 -0
  147. package/fastify/src/framework/maker-cli/stubs/deploy/server/compose/redis.stub +131 -0
  148. package/fastify/src/framework/maker-cli/stubs/deploy/server/env/local.example.stub +11 -0
  149. package/fastify/src/framework/maker-cli/stubs/deploy/server/env/noredis.stub +25 -0
  150. package/fastify/src/framework/maker-cli/stubs/deploy/server/env/redis.stub +25 -0
  151. package/fastify/src/framework/maker-cli/stubs/deploy/server/nginx-vhost/README.stub +16 -0
  152. package/fastify/src/framework/maker-cli/stubs/deploy/server/nginx-vhost/app.example.com.stub +13 -0
  153. package/fastify/src/framework/maker-cli/stubs/deploy/server/pgadmin/servers.stub +14 -0
  154. package/fastify/src/framework/maker-cli/stubs/deploy/server/redis/redis.conf.stub +7 -0
  155. package/fastify/src/framework/maker-cli/stubs/deploy/supervisor/noredis.stub +54 -0
  156. package/fastify/src/framework/maker-cli/stubs/deploy/supervisor/redis.stub +70 -0
  157. package/fastify/src/framework/maker-cli/stubs/deploy/workflow/local.json.stub +25 -0
  158. package/fastify/src/framework/maker-cli/stubs/deploy/workflow/remote.json.stub +24 -0
  159. package/fastify/src/framework/maker-cli/stubs/facade/name.ts.stub +12 -0
  160. package/fastify/src/framework/maker-cli/stubs/helper/name.ts.stub +14 -0
  161. package/fastify/src/framework/maker-cli/stubs/job/name.ts.stub +25 -0
  162. package/fastify/src/framework/maker-cli/stubs/middleware/name.local.ts.stub +15 -0
  163. package/fastify/src/framework/maker-cli/stubs/middleware/name.ts.stub +15 -0
  164. package/fastify/src/framework/maker-cli/stubs/model/name.mysql.ts.stub +10 -0
  165. package/fastify/src/framework/maker-cli/stubs/model/name.postgresql.ts.stub +10 -0
  166. package/fastify/src/framework/maker-cli/stubs/model/name.sqlite.ts.stub +10 -0
  167. package/fastify/src/framework/maker-cli/stubs/notification/controller.ts.stub +79 -0
  168. package/fastify/src/framework/maker-cli/stubs/notification/job.ts.stub +11 -0
  169. package/fastify/src/framework/maker-cli/stubs/notification/model.ts.stub +29 -0
  170. package/fastify/src/framework/maker-cli/stubs/notification/provider.ts.stub +174 -0
  171. package/fastify/src/framework/maker-cli/stubs/notification/route.api.ts.stub +71 -0
  172. package/fastify/src/framework/maker-cli/stubs/notification/route.plain.ts.stub +15 -0
  173. package/fastify/src/framework/maker-cli/stubs/notification/schema.plain.ts.stub +26 -0
  174. package/fastify/src/framework/maker-cli/stubs/notification/schema.ts.stub +26 -0
  175. package/fastify/src/framework/maker-cli/stubs/provider/module-barrel.ts.stub +19 -0
  176. package/fastify/src/framework/maker-cli/stubs/provider/module.ts.stub +21 -0
  177. package/fastify/src/framework/maker-cli/stubs/provider/name.ts.stub +21 -0
  178. package/fastify/src/framework/maker-cli/stubs/route/api.ts.stub +101 -0
  179. package/fastify/src/framework/maker-cli/stubs/route/plain.ts.stub +23 -0
  180. package/fastify/src/framework/maker-cli/stubs/route/standalone.api.ts.stub +120 -0
  181. package/fastify/src/framework/maker-cli/stubs/route/standalone.plain.ts.stub +50 -0
  182. package/fastify/src/framework/maker-cli/stubs/schedule/name.ts.stub +36 -0
  183. package/fastify/src/framework/maker-cli/stubs/schema/name.plain.ts.stub +14 -0
  184. package/fastify/src/framework/maker-cli/stubs/schema/name.ts.stub +33 -0
  185. package/fastify/src/framework/maker-cli/stubs/seeder/name.ts.stub +18 -0
  186. package/fastify/src/framework/maker-cli/stubs/seeder/standalone.ts.stub +22 -0
  187. package/fastify/src/framework/maker-cli/stubs/service/name.plain.ts.stub +67 -0
  188. package/fastify/src/framework/maker-cli/stubs/service/name.ts.stub +65 -0
  189. package/fastify/src/framework/maker-cli/stubs/test/unit.ts.stub +22 -0
  190. package/fastify/src/framework/maker-cli/stubs/type/name.ts.stub +22 -0
  191. package/fastify/src/framework/maker-cli/utils/env-config.mjs +44 -0
  192. package/fastify/src/framework/maker-cli/utils/env-db.mjs +61 -0
  193. package/fastify/src/framework/maker-cli/utils/file-ops.mjs +30 -0
  194. package/fastify/src/framework/maker-cli/utils/flags.mjs +14 -0
  195. package/fastify/src/framework/maker-cli/utils/help.mjs +379 -0
  196. package/fastify/src/framework/maker-cli/utils/naming.mjs +119 -0
  197. package/fastify/src/framework/maker-cli/utils/process.mjs +52 -0
  198. package/fastify/src/framework/maker-cli/utils/project-commands.mjs +201 -0
  199. package/fastify/src/framework/modules/discover.ts +57 -0
  200. package/fastify/src/framework/modules/routes.ts +31 -0
  201. package/fastify/src/framework/notification/channels/broadcast.ts +15 -0
  202. package/fastify/src/framework/notification/channels/mail.ts +42 -0
  203. package/fastify/src/framework/notification/contracts.ts +29 -0
  204. package/fastify/src/framework/notification/index.ts +59 -0
  205. package/fastify/src/framework/notification/registry.ts +15 -0
  206. package/fastify/src/framework/providers/adapter.ts +149 -0
  207. package/fastify/src/framework/providers/contracts.ts +126 -0
  208. package/fastify/src/framework/providers/loader.ts +105 -0
  209. package/fastify/src/framework/providers/registry.ts +88 -0
  210. package/fastify/src/framework/queue/clear.ts +22 -0
  211. package/fastify/src/framework/queue/queue.ts +401 -0
  212. package/fastify/src/framework/queue/ui.ts +194 -0
  213. package/fastify/src/framework/queue/worker.ts +61 -0
  214. package/fastify/src/framework/realtime/broadcast.ts +70 -0
  215. package/fastify/src/framework/realtime/index.ts +2 -0
  216. package/fastify/src/framework/realtime/socket-cookie.ts +72 -0
  217. package/fastify/src/framework/realtime/socket.ts +149 -0
  218. package/fastify/src/framework/realtime/types.ts +6 -0
  219. package/fastify/src/framework/realtime/ui.ts +16 -0
  220. package/fastify/src/framework/redis/client.ts +148 -0
  221. package/fastify/src/framework/runtime/runtime.ts +23 -0
  222. package/fastify/src/framework/runtime/server-adapter.ts +33 -0
  223. package/fastify/src/framework/scheduler/lock.ts +128 -0
  224. package/fastify/src/framework/scheduler/run.ts +48 -0
  225. package/fastify/src/framework/scheduler/scheduler.ts +155 -0
  226. package/fastify/src/framework/server.ts +162 -0
  227. package/fastify/src/framework/session/session.ts +160 -0
  228. package/fastify/src/framework/storage/storage.ts +744 -0
  229. package/fastify/src/framework/support/cookie.ts +71 -0
  230. package/fastify/src/framework/support/jwt.ts +33 -0
  231. package/fastify/src/framework/support/lifecycle.ts +35 -0
  232. package/fastify/src/framework/support/logger.ts +148 -0
  233. package/fastify/src/framework/support/mail.ts +56 -0
  234. package/fastify/src/framework/support/password.ts +23 -0
  235. package/fastify/src/framework/support/status-codes.ts +75 -0
  236. package/fastify/src/framework/support/upload.ts +181 -0
  237. package/fastify/src/framework/support/url.ts +41 -0
  238. package/fastify/src/middlewares/auth-middleware.ts +134 -0
  239. package/fastify/src/middlewares/role-middleware.ts +19 -0
  240. package/fastify/src/modules/auth/__tests__/forgetpass.test.ts +200 -0
  241. package/fastify/src/modules/auth/__tests__/login.test.ts +171 -0
  242. package/fastify/src/modules/auth/__tests__/logout.test.ts +60 -0
  243. package/fastify/src/modules/auth/__tests__/logoutalldevices.test.ts +49 -0
  244. package/fastify/src/modules/auth/__tests__/me.test.ts +91 -0
  245. package/fastify/src/modules/auth/__tests__/refreshtoken.test.ts +305 -0
  246. package/fastify/src/modules/auth/__tests__/register.test.ts +291 -0
  247. package/fastify/src/modules/auth/__tests__/resetpass.test.ts +206 -0
  248. package/fastify/src/modules/auth/__tests__/verifyemail.test.ts +183 -0
  249. package/fastify/src/modules/auth/controllers/auth.ts +252 -0
  250. package/fastify/src/modules/auth/controllers/role.ts +35 -0
  251. package/fastify/src/modules/auth/database/models/role.ts +14 -0
  252. package/fastify/src/modules/auth/database/models/user.ts +46 -0
  253. package/fastify/src/modules/auth/database/seeders/role.ts +18 -0
  254. package/fastify/src/modules/auth/database/seeders/user.ts +35 -0
  255. package/fastify/src/modules/auth/facade.ts +12 -0
  256. package/fastify/src/modules/auth/helpers/auth.ts +115 -0
  257. package/fastify/src/modules/auth/jobs/forgetpass.ts +20 -0
  258. package/fastify/src/modules/auth/jobs/registeruser.ts +23 -0
  259. package/fastify/src/modules/auth/jobs/verifyemail.ts +18 -0
  260. package/fastify/src/modules/auth/routes/index.ts +185 -0
  261. package/fastify/src/modules/auth/routes/role.ts +56 -0
  262. package/fastify/src/modules/auth/schemas/auth.ts +116 -0
  263. package/fastify/src/modules/auth/services/forgetpassword.ts +66 -0
  264. package/fastify/src/modules/auth/services/login.ts +63 -0
  265. package/fastify/src/modules/auth/services/logout.ts +25 -0
  266. package/fastify/src/modules/auth/services/logoutalldevices.ts +19 -0
  267. package/fastify/src/modules/auth/services/me.ts +31 -0
  268. package/fastify/src/modules/auth/services/refreshtoken.ts +110 -0
  269. package/fastify/src/modules/auth/services/register.ts +141 -0
  270. package/fastify/src/modules/auth/services/resetpassword.ts +69 -0
  271. package/fastify/src/modules/auth/services/verifyemail.ts +77 -0
  272. package/fastify/src/modules/auth/types/auth.ts +31 -0
  273. package/fastify/src/providers/README.md +107 -0
  274. package/fastify/src/providers/notification.ts +4 -0
  275. package/fastify/src/resources/index.html +17 -0
  276. package/fastify/src/resources/src/App.vue +5 -0
  277. package/fastify/src/resources/src/assets/css/styles.css +14761 -0
  278. package/fastify/src/resources/src/assets/css/styles.css.map +1 -0
  279. package/fastify/src/resources/src/assets/fonts/public-sans/public-sans-latin-ext.woff2 +0 -0
  280. package/fastify/src/resources/src/assets/fonts/public-sans/public-sans-latin.woff2 +0 -0
  281. package/fastify/src/resources/src/assets/images/favicon/favicon.ico +0 -0
  282. package/fastify/src/resources/src/assets/images/logo.png +0 -0
  283. package/fastify/src/resources/src/assets/scss/custom.css +217 -0
  284. package/fastify/src/resources/src/assets/scss/custom.css.map +1 -0
  285. package/fastify/src/resources/src/assets/scss/custom.scss +1346 -0
  286. package/fastify/src/resources/src/components/Button.vue +32 -0
  287. package/fastify/src/resources/src/components/Checkbox.vue +32 -0
  288. package/fastify/src/resources/src/components/Datepicker.vue +106 -0
  289. package/fastify/src/resources/src/components/FeatureButton.vue +53 -0
  290. package/fastify/src/resources/src/components/FloatButton.vue +33 -0
  291. package/fastify/src/resources/src/components/Href.vue +32 -0
  292. package/fastify/src/resources/src/components/Input.vue +243 -0
  293. package/fastify/src/resources/src/components/InputGroup.vue +147 -0
  294. package/fastify/src/resources/src/components/InputPasswordToggle.vue +229 -0
  295. package/fastify/src/resources/src/components/Modal.vue +71 -0
  296. package/fastify/src/resources/src/components/Pagebar.vue +26 -0
  297. package/fastify/src/resources/src/components/Refresh.vue +19 -0
  298. package/fastify/src/resources/src/components/Select.vue +384 -0
  299. package/fastify/src/resources/src/components/Spinner.vue +42 -0
  300. package/fastify/src/resources/src/components/Switch.vue +60 -0
  301. package/fastify/src/resources/src/components/TextArea.vue +117 -0
  302. package/fastify/src/resources/src/components/Toast.vue +56 -0
  303. package/fastify/src/resources/src/components/datatable/DataTableSkeleton.vue +98 -0
  304. package/fastify/src/resources/src/components/datatable/Pagination.vue +147 -0
  305. package/fastify/src/resources/src/components/datatable/SelectOpption.vue +64 -0
  306. package/fastify/src/resources/src/components/datatable/index.vue +199 -0
  307. package/fastify/src/resources/src/composables/useAuth.ts +52 -0
  308. package/fastify/src/resources/src/helpers/assets.ts +51 -0
  309. package/fastify/src/resources/src/helpers/nformatter.ts +10 -0
  310. package/fastify/src/resources/src/helpers/utils.ts +75 -0
  311. package/fastify/src/resources/src/layouts/AuthLayout.vue +20 -0
  312. package/fastify/src/resources/src/layouts/Layout/Footer.vue +23 -0
  313. package/fastify/src/resources/src/layouts/Layout/Header.vue +124 -0
  314. package/fastify/src/resources/src/layouts/Layout/Sidebar.vue +137 -0
  315. package/fastify/src/resources/src/layouts/Layout/index.vue +87 -0
  316. package/fastify/src/resources/src/main.ts +28 -0
  317. package/fastify/src/resources/src/pages/auth/forgetPassword.vue +76 -0
  318. package/fastify/src/resources/src/pages/auth/login.vue +96 -0
  319. package/fastify/src/resources/src/pages/auth/register.vue +129 -0
  320. package/fastify/src/resources/src/pages/auth/resetPassword.vue +110 -0
  321. package/fastify/src/resources/src/pages/auth/verifyEmail.vue +64 -0
  322. package/fastify/src/resources/src/pages/dashboard/index.vue +66 -0
  323. package/fastify/src/resources/src/plugins/axios.ts +31 -0
  324. package/fastify/src/resources/src/plugins/browserDetect.ts +55 -0
  325. package/fastify/src/resources/src/plugins/dialog.ts +216 -0
  326. package/fastify/src/resources/src/plugins/gum.ts +683 -0
  327. package/fastify/src/resources/src/plugins/pulse.ts +157 -0
  328. package/fastify/src/resources/src/plugins/routeProgress.ts +87 -0
  329. package/fastify/src/resources/src/router/index.ts +85 -0
  330. package/fastify/src/resources/src/stores/admin-ui.ts +148 -0
  331. package/fastify/src/resources/src/stores/auth.ts +187 -0
  332. package/fastify/src/resources/tsconfig.json +19 -0
  333. package/fastify/src/resources/vite.config.ts +64 -0
  334. package/fastify/src/storage/app/private/.gitkeep +0 -0
  335. package/fastify/src/storage/app/public/.gitkeep +0 -0
  336. package/fastify/src/storage/app/tmp/generated/.gitkeep +0 -0
  337. package/fastify/src/storage/database/nexwire.sqlite +0 -0
  338. package/fastify/src/storage/logs/.gitkeep +0 -0
  339. package/fastify/src/storage/logs/app.log +0 -0
  340. package/fastify/src/storage/logs/fatal.log +0 -0
  341. package/fastify/src/storage/tmp/.gitkeep +0 -0
  342. package/fastify/src/storage/trash/modules/.gitkeep +0 -0
  343. package/fastify/src/types/global.d.ts +32 -0
  344. package/fastify/tsconfig.json +19 -0
  345. package/fastify/vitest.config.ts +38 -0
  346. package/hono/.env +1 -1
  347. package/hono/package.json +1 -1
  348. package/hono/src/config/database.ts +48 -29
  349. package/hono/src/config/index.ts +83 -83
  350. package/hono/src/config/openapi.ts +6 -24
  351. package/hono/src/config/queue.ts +30 -30
  352. package/hono/src/config/validate.ts +50 -40
  353. package/hono/src/database/migrations/sqlite/0000_init.sql +47 -47
  354. package/hono/src/database/migrations/sqlite/meta/0000_snapshot.json +1 -1
  355. package/hono/src/database/migrations/sqlite/meta/_journal.json +1 -1
  356. package/hono/src/framework/cache/cache.ts +40 -8
  357. package/hono/src/framework/circuit-breaker/cb.ts +88 -60
  358. package/hono/src/framework/database/connection.ts +52 -27
  359. package/hono/src/framework/events/dispatcher.ts +61 -49
  360. package/hono/src/framework/facade.ts +39 -1
  361. package/hono/src/framework/http/logger.ts +86 -81
  362. package/hono/src/framework/http/openapi.ts +7 -9
  363. package/hono/src/framework/maker-cli/runtime/core.mjs +1 -2
  364. package/hono/src/framework/notification/channels/broadcast.ts +15 -0
  365. package/hono/src/framework/notification/channels/mail.ts +42 -0
  366. package/hono/src/framework/notification/contracts.ts +29 -0
  367. package/hono/src/framework/notification/index.ts +19 -94
  368. package/hono/src/framework/notification/registry.ts +15 -0
  369. package/hono/src/framework/providers/adapter.ts +27 -17
  370. package/hono/src/framework/queue/queue.ts +43 -44
  371. package/hono/src/framework/realtime/broadcast.ts +29 -5
  372. package/hono/src/framework/realtime/socket.ts +217 -196
  373. package/hono/src/framework/redis/client.ts +148 -151
  374. package/hono/src/framework/scheduler/lock.ts +128 -116
  375. package/hono/src/framework/server.ts +27 -29
  376. package/hono/src/framework/session/session.ts +62 -14
  377. package/hono/src/framework/storage/storage.ts +744 -785
  378. package/hono/src/framework/support/mail.ts +56 -48
  379. package/hono/src/middlewares/auth-middleware.ts +1 -2
  380. package/hono/src/middlewares/role-middleware.ts +3 -13
  381. package/hono/src/modules/auth/routes/index.ts +1 -2
  382. package/hono/src/providers/notification.ts +4 -0
  383. package/hono/src/resources/src/components/Switch.vue +60 -65
  384. package/hono/src/resources/src/layouts/Layout/Header.vue +124 -105
  385. package/hono/src/resources/src/pages/auth/forgetPassword.vue +23 -29
  386. package/hono/src/resources/src/pages/auth/login.vue +26 -29
  387. package/hono/src/resources/src/pages/auth/register.vue +36 -40
  388. package/hono/src/resources/src/pages/auth/resetPassword.vue +29 -37
  389. package/hono/src/resources/src/pages/auth/verifyEmail.vue +15 -19
  390. package/hono/src/resources/src/plugins/gum.ts +519 -203
  391. package/hono/src/resources/src/plugins/pulse.ts +157 -147
  392. package/hono/src/resources/src/stores/auth.ts +9 -6
  393. package/hono/src/resources/vite.config.ts +54 -43
  394. package/hono/src/storage/database/nexwire.sqlite +0 -0
  395. package/hono/src/storage/logs/app.log +0 -0
  396. package/hono/src/storage/logs/fatal.log +0 -0
  397. package/hono/src/types/global.d.ts +32 -26
  398. package/package.json +4 -3
  399. package/src/index.js +143 -15
@@ -0,0 +1,384 @@
1
+ <script setup lang="ts">
2
+ import axios from "axios";
3
+ import { debounce } from "lodash-es";
4
+ import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, useAttrs, watch } from "vue";
5
+ import "vue-select/dist/vue-select.css";
6
+ import vSelect from "vue-select";
7
+ import { empty } from "../helpers/utils";
8
+
9
+ defineOptions({ name: "Select", inheritAttrs: false });
10
+
11
+ type AnyRecord = Record<string, unknown>;
12
+ type SelectValue = string | number | boolean | AnyRecord | null;
13
+ type OptionHook = ((value: unknown) => void | Promise<void>) | null;
14
+
15
+ interface FetchPack {
16
+ url?: string | null;
17
+ data?: string | null;
18
+ params?: AnyRecord;
19
+ mapFn?: (x: AnyRecord) => AnyRecord;
20
+ option?: ((value: unknown) => void | Promise<void>) | Array<(value: unknown) => void | Promise<void>>;
21
+ reset?: boolean;
22
+ reload?: boolean;
23
+ }
24
+
25
+ interface SelectProps {
26
+ fetched?: (payload: Record<string, any>) => any;
27
+ must?: boolean;
28
+ err?: string | boolean;
29
+ hood?: string | boolean;
30
+ defaultValue?: SelectValue;
31
+ resetKey?: string | number | boolean | AnyRecord | null;
32
+ }
33
+
34
+ const $attrs = useAttrs();
35
+ const props = withDefaults(defineProps<SelectProps>(), {
36
+ defaultValue: null,
37
+ resetKey: null
38
+ });
39
+
40
+ // vue3.4 way
41
+ const emit = defineEmits<{
42
+ (event: "fetched"): void;
43
+ (event: "clear"): void;
44
+ }>();
45
+ const model = defineModel<SelectValue | SelectValue[]>();
46
+
47
+ const skipNextSearch = ref(false); // suppress search right after clear
48
+
49
+ let observer = ref<IntersectionObserver | null>(null);
50
+ let load = ref<HTMLElement | null>(null);
51
+
52
+ // store callbacks coming from fetchedDropdown(pack)
53
+ const hooks = ref<{ option: OptionHook | OptionHook[] }>({ option: null }); // function | function[] | null
54
+
55
+ // remember last fetch config and search so the child can refetch/append by itself
56
+ const lastPack = ref<FetchPack | null>(null); // { url, data, params, mapFn }
57
+ const lastSearch = ref(""); // current search term
58
+ const lastSearchRows = ref<AnyRecord[]>([]); // results of the last search, to pin on top after clearing
59
+
60
+ const hasNextPage = computed(() => {
61
+ const total = Number((fieldData.value as { total?: number }).total ?? 0);
62
+ return field.all.length < total;
63
+ });
64
+
65
+ // ask parent for a pack and run our internal fetcher
66
+ const callParentFetched = async (search: string, opts: Partial<FetchPack> = {}) => {
67
+ const maybePack = await props.fetched?.({ search, ...opts });
68
+ if (maybePack && typeof maybePack === "object") {
69
+ await fetchedDropdown(search, { ...maybePack, ...opts });
70
+ }
71
+ };
72
+
73
+ const onClear = () => {
74
+ resetMe(); // clear options + selected value
75
+ skipNextSearch.value = true; // suppress the immediate empty search
76
+ emit("clear"); // notify parent that clear button was clicked
77
+ };
78
+
79
+ const onOpen = async () => {
80
+ // if empty, load page 1 via parent fetcher (no boolean flags)
81
+ if (!field.all.length) {
82
+ if (lastPack.value) {
83
+ await fetchedDropdown("", { ...lastPack.value, reset: true });
84
+ } else {
85
+ // await props.fetched?.('');
86
+ await callParentFetched("", { reset: true });
87
+ }
88
+ }
89
+
90
+ await nextTick();
91
+ if (load.value && hasNextPage.value) {
92
+ observer.value?.observe(load.value);
93
+ }
94
+ };
95
+
96
+ const onClose = async () => {
97
+ observer.value?.disconnect();
98
+ };
99
+
100
+ const onModelUpdate = async (val: unknown) => {
101
+ const optHooks = Array.isArray(hooks.value.option) ? hooks.value.option : hooks.value.option ? [hooks.value.option] : [];
102
+
103
+ // When user clicks × or backspaces everything, v-select sets model to null.
104
+ if (val == null || val === "") {
105
+ for (const f of optHooks) {
106
+ if (typeof f === "function") {
107
+ await Promise.resolve(f(null));
108
+ }
109
+ }
110
+ emit("clear");
111
+ return;
112
+ }
113
+
114
+ field.val = val as SelectValue;
115
+
116
+ for (const f of optHooks) {
117
+ if (typeof f === "function") {
118
+ await Promise.resolve(f(val));
119
+ }
120
+ }
121
+ };
122
+
123
+ const inputSearch = debounce(async (search: string, loading: (value: boolean) => void) => {
124
+ if (skipNextSearch.value && !search?.trim().length) {
125
+ skipNextSearch.value = false; // consume the flag
126
+ return;
127
+ }
128
+
129
+ if (search?.trim().length) {
130
+ loading(true);
131
+ if (lastPack.value) {
132
+ await fetchedDropdown(search, { ...lastPack.value, reset: true }).finally(() => loading(false));
133
+ } else {
134
+ await callParentFetched(search, { reset: true }).finally(() => loading(false));
135
+ }
136
+ } else {
137
+ // user erased the query manually: refetch the full list, then pin the last search results on top
138
+ field.page = 0;
139
+ if (lastPack.value) {
140
+ await fetchedDropdown("", { ...lastPack.value, reset: true });
141
+ } else {
142
+ await callParentFetched("");
143
+ }
144
+ pinLastSearchRows();
145
+ await nextTick();
146
+ if (load.value && hasNextPage.value) observer.value?.observe(load.value);
147
+ }
148
+ }, 400);
149
+
150
+ const infiniteScroll = async ([{ isIntersecting, target }]: IntersectionObserverEntry[]) => {
151
+ if (!isIntersecting) return;
152
+
153
+ const ul = target instanceof HTMLElement && target.offsetParent instanceof HTMLElement ? target.offsetParent : null;
154
+ const scrollTop = ul?.scrollTop ?? 0;
155
+
156
+ if (lastPack.value) {
157
+ await fetchedDropdown(lastSearch.value, { ...lastPack.value }); // next page of current mode
158
+ } else {
159
+ await callParentFetched(lastSearch.value || "");
160
+ }
161
+
162
+ await nextTick();
163
+
164
+ if (ul) ul.scrollTop = scrollTop;
165
+ };
166
+
167
+ onMounted(async () => {
168
+ observer.value = new IntersectionObserver(infiniteScroll);
169
+
170
+ // set default value if provided
171
+ if (props.defaultValue) {
172
+ await nextTick();
173
+ field.val = props.defaultValue;
174
+ model.value = props.defaultValue;
175
+ }
176
+ });
177
+
178
+ onBeforeUnmount(() => {
179
+ observer.value?.disconnect();
180
+ observer.value = null;
181
+ });
182
+
183
+ // prototype and it will call from parent
184
+ const field = reactive<{ all: AnyRecord[]; val: SelectValue; page: number; size: number }>({
185
+ all: [],
186
+ val: "",
187
+ page: 0,
188
+ size: 10
189
+ });
190
+ const fieldData = ref<AnyRecord>({});
191
+
192
+ const resetMe = () => {
193
+ field.all = [];
194
+ field.page = 0;
195
+ field.val = "";
196
+ };
197
+
198
+ const reloadMe = () => {
199
+ field.all = [];
200
+ field.page = 0;
201
+ };
202
+
203
+ const fetchedDropdown = async (search: string, pack: FetchPack | null = null) => {
204
+ const url = pack?.url ?? null;
205
+ const data = pack?.data ?? null;
206
+ const params = pack?.params ?? {};
207
+ const mapFn = pack?.mapFn ?? ((x: AnyRecord) => x);
208
+ const reset = !!pack?.reset;
209
+ const reload = !!pack?.reload;
210
+
211
+ // make option sticky: only overwrite if caller provided it
212
+ if ("option" in (pack ?? {})) {
213
+ const pOpt = pack?.option;
214
+ hooks.value.option = Array.isArray(pOpt) ? pOpt.filter((fn) => typeof fn === "function") : typeof pOpt === "function" ? [pOpt] : [];
215
+ }
216
+
217
+ // remember essentials so the child can refetch/append on its own
218
+ lastPack.value = { url, data, params, mapFn };
219
+ lastSearch.value = search ?? "";
220
+
221
+ // if pass and true -> reset, reload , search
222
+ (reset && resetMe()) || (reload && reloadMe()) || (!empty(search) && !reset && !reload && reloadMe());
223
+
224
+ // after reset/reload/search
225
+ field.page++;
226
+ const param = { page: field.page, size: field.size, search: search };
227
+
228
+ // log once (don't call axios twice)
229
+ // console.log('GET', pack.url, { params: pack.params ? { ...pack.params, ...param } : param });
230
+
231
+ if (!url || !data) {
232
+ return;
233
+ }
234
+
235
+ let key: any;
236
+ try {
237
+ key = await axios.get(url, { params: params ? { ...params, ...param } : param });
238
+ } catch {
239
+ return; // failed to fetch (e.g. cascade with missing parent)
240
+ }
241
+ const payload = key.data[data as string] as AnyRecord;
242
+
243
+ fieldData.value = payload;
244
+ const rows = Array.isArray((payload as { data?: unknown }).data) ? (payload as { data: AnyRecord[] }).data : [];
245
+
246
+ const options = rows.map((dt) => (dt == null ? null : mapFn(dt))).filter((opt): opt is AnyRecord => opt != null);
247
+
248
+ // remember the last fresh search results so they can be pinned on top after clearing the search
249
+ if (search?.trim().length && reset) {
250
+ lastSearchRows.value = options.map((opt) => ({ ...opt }));
251
+ }
252
+
253
+ // console.log('Fetched', payload?.data);
254
+ options.forEach((opt) => {
255
+ const key = opt?.id ?? opt?.title ?? JSON.stringify(opt); // build a comparison key: prefer id, then title, then full object
256
+
257
+ if (
258
+ !field.all.some(
259
+ (o) =>
260
+ o != null &&
261
+ ((o as { id?: unknown; title?: unknown }).id ?? (o as { id?: unknown; title?: unknown }).title ?? JSON.stringify(o)) === key
262
+ )
263
+ ) {
264
+ field.all = [...field.all, opt];
265
+ }
266
+ });
267
+ };
268
+
269
+ const optionKey = (o: AnyRecord) =>
270
+ (o as { id?: unknown; title?: unknown }).id ?? (o as { id?: unknown; title?: unknown }).title ?? JSON.stringify(o);
271
+
272
+ // after a refetch, move the last search results to the top of the list
273
+ const pinLastSearchRows = () => {
274
+ if (!lastSearchRows.value.length) return;
275
+ const pinKeys = new Set(lastSearchRows.value.map(optionKey));
276
+ const rest = field.all.filter((o) => !pinKeys.has(optionKey(o)));
277
+ field.all = [...lastSearchRows.value, ...rest];
278
+ };
279
+
280
+ /**
281
+ * This function created for
282
+ * functionally reset or reload
283
+ * from without component
284
+ * like fn.value.reload({reset:true/reload:true/both});
285
+ * it is exposed, so we can call from with
286
+ * template ref
287
+ */
288
+ const reload = async () => {
289
+ resetMe(); // clear field.all, page, val
290
+ await callParentFetched("", { reset: true }); // ask parent to fetch first page again
291
+ };
292
+
293
+ /**
294
+ * if backend send proper object or null
295
+ * then watch function not get any axios
296
+ * error else axios can send error
297
+ */
298
+ watch(
299
+ () => props.resetKey,
300
+ async (newVal, oldVal) => {
301
+ if (oldVal === undefined) return;
302
+
303
+ const optHooks = Array.isArray(hooks.value.option) ? hooks.value.option : hooks.value.option ? [hooks.value.option] : [];
304
+
305
+ model.value = "";
306
+
307
+ // Notify hooks that selection is gone
308
+ for (const fn of optHooks) {
309
+ if (typeof fn === "function") {
310
+ await Promise.resolve(fn(null));
311
+ }
312
+ }
313
+
314
+ skipNextSearch.value = true; // avoid instant empty-search call
315
+ if (newVal == null) {
316
+ lastPack.value = null;
317
+ return;
318
+ } // parent cleared, don't fetch with null key
319
+ await callParentFetched("", { reload: true });
320
+ pinLastSearchRows();
321
+ skipNextSearch.value = false; // allow subsequent empty-searches (backspace-to-empty)
322
+ }
323
+ );
324
+
325
+ defineExpose({ field, fieldData, fetchedDropdown, reload });
326
+
327
+ const parentClass = computed(() => ($attrs.parentclass as string | undefined) || "mb-2");
328
+
329
+ const inputTitle = computed(() => ($attrs.title as string | undefined) || "");
330
+ const hoodHtml = computed(() => (props.hood === false || props.hood == null ? "" : String(props.hood)));
331
+ </script>
332
+
333
+ <template>
334
+ <div :class="parentClass">
335
+ <label
336
+ class="text-capitalize d-flex align-items-center mb-1"
337
+ :class="{ 'd-none': !inputTitle }">
338
+ <div class="position-relative">
339
+ <span class="text-capitalize" v-html="inputTitle"></span>
340
+ <span
341
+ class="position-absolute text-danger rounded-circle bg-danger must"
342
+ :class="{ 'd-none': !props.must }"></span>
343
+ </div>
344
+ <div
345
+ class="text-uppercase w-100 text-end text-primary fw-semibold"
346
+ :class="{ 'd-none': !props.hood }"
347
+ style="font-size: 12px; margin-top: 0.15rem"
348
+ v-html="hoodHtml"></div>
349
+ </label>
350
+ <v-select
351
+ v-model="model"
352
+ label="title"
353
+ :filterable="false"
354
+ :options="field.all"
355
+ v-bind="$attrs"
356
+ @open="onOpen"
357
+ @update:model-value="onModelUpdate"
358
+ @close="onClose"
359
+ @search="inputSearch"
360
+ @clear="onClear">
361
+ <template #list-footer>
362
+ <li v-show="hasNextPage" ref="load" class="loader">More...</li>
363
+ </template>
364
+ </v-select>
365
+ <div class="form-text text-danger" :class="{ 'd-none': !props.err }" style="font-size: 0.8rem">
366
+ {{ props.err }}
367
+ </div>
368
+ </div>
369
+ </template>
370
+
371
+ <style lang="scss" scoped>
372
+ .loader {
373
+ text-align: center;
374
+ color: #bbbbbb;
375
+ }
376
+
377
+ .must {
378
+ width: 4px;
379
+ height: 4px;
380
+ top: 0;
381
+ margin-top: 5px;
382
+ margin-left: 2px;
383
+ }
384
+ </style>
@@ -0,0 +1,42 @@
1
+ <template>
2
+ <div class="h-100" v-bind="$attrs">
3
+ <div class="h-100 d-flex align-items-center justify-content-center">
4
+ <div class="spinner-grow" :style="spinnerStyle" role="status">
5
+ <span class="visually-hidden">Loading...</span>
6
+ </div>
7
+ <div class="fw-bold ms-2">
8
+ <span v-if="props.text" :class="props.textsize" :style="textStyle">
9
+ {{ props.text }}
10
+ </span>
11
+ <span v-else>Loading...</span>
12
+ </div>
13
+ </div>
14
+ </div>
15
+ </template>
16
+
17
+ <script setup lang="ts">
18
+ import { computed } from "vue";
19
+
20
+ defineOptions({ name: "Spinner", inheritAttrs: false });
21
+
22
+ interface SpinnerProps {
23
+ size?: string;
24
+ text?: string;
25
+ textsize?: string;
26
+ }
27
+
28
+ const props = withDefaults(defineProps<SpinnerProps>(), {
29
+ size: "3rem",
30
+ text: "",
31
+ textsize: ""
32
+ });
33
+
34
+ const spinnerStyle = computed(() => ({
35
+ width: props.size,
36
+ height: props.size
37
+ }));
38
+
39
+ const textStyle = computed(() => ({
40
+ fontSize: props.textsize || undefined
41
+ }));
42
+ </script>
@@ -0,0 +1,60 @@
1
+ <template>
2
+ <div :class="wrapperClass">
3
+ <label :class="props.vertical ? 'form-check-label d-block' : 'd-none'">
4
+ <span v-html="statusHtml"></span>
5
+ </label>
6
+ <div class="form-check form-switch d-inline-block">
7
+ <input class="form-check-input shadow-none border-primary" type="checkbox" :checked="isChecked" v-bind="$attrs">
8
+ <label :class="props.vertical ? 'd-none' : 'form-check-label'">
9
+ <span v-html="statusHtml"></span>
10
+ </label>
11
+ </div>
12
+ </div>
13
+ </template>
14
+
15
+ <script setup lang="ts">
16
+ import { computed, useAttrs } from "vue";
17
+
18
+ defineOptions({ name: "Switch", inheritAttrs: false });
19
+
20
+ interface SwitchProps {
21
+ modelValue?: string | number | boolean;
22
+ value?: unknown[] | Record<string, unknown> | string | number | boolean;
23
+ vertical?: boolean;
24
+ checked?: boolean | number;
25
+ }
26
+
27
+ const $attrs = useAttrs();
28
+ const props = defineProps<SwitchProps>();
29
+
30
+ const textLabel = computed(() => ($attrs.text as string | undefined) || "");
31
+ const blankLabel = computed(() => ($attrs.blank as string | undefined) || "");
32
+ const topclass = computed(() => ($attrs.topclass as string | undefined) || "");
33
+
34
+ const isChecked = computed(() => Boolean(props.checked));
35
+
36
+ const wrapperClass = computed(() => (props.vertical ? `text-center ${topclass.value}`.trim() : ""));
37
+
38
+ const statusHtml = computed(() => {
39
+ if (isChecked.value) {
40
+ return `<div class='d-flex justify-content-center'><span>${textLabel.value}</span><div class='rounded-circle bg-success indicator'></div></div>`;
41
+ }
42
+
43
+ return `<div class='d-flex justify-content-center'><span>${blankLabel.value}</span><div class='rounded-circle bg-danger indicator'></div></div>`;
44
+ });
45
+ </script>
46
+
47
+ <style lang="scss" scoped>
48
+ .indicator {
49
+ width: 4px;
50
+ height: 4px;
51
+ margin-left: 2px;
52
+ margin-top: 5px;
53
+ }
54
+ </style>
55
+ <style lang="scss" scoped>
56
+ label,
57
+ input {
58
+ cursor: pointer;
59
+ }
60
+ </style>
@@ -0,0 +1,117 @@
1
+ <template>
2
+ <div :class="wrapperClass">
3
+ <div class="position-relative">
4
+ <div class="position-relative mb-1">
5
+ <span class="text-capitalize" v-html="inputLabel"></span>
6
+ <span
7
+ class="position-absolute text-danger rounded-circle bg-danger must"
8
+ :class="{ 'd-none': !props.must }"></span>
9
+ </div>
10
+ <div class="form-floating h-100">
11
+ <textarea
12
+ :value="props.modelValue"
13
+ class="form-control mb-0 h-100"
14
+ :maxlength="maxLengthAttr"
15
+ :aria-describedby="`${inputId}Help`"
16
+ v-bind="$attrs"
17
+ rows="3"
18
+ @input="updateModel">
19
+ </textarea>
20
+ <label
21
+ :for="inputId"
22
+ class="d-block placeholder-label border-0"
23
+ :class="{ 'd-none': !placeholderText }">
24
+ {{ placeholderText }}
25
+ </label>
26
+ </div>
27
+ <label
28
+ class="position-absolute end-0 pe-1 text-secondary max"
29
+ :class="{ 'd-none': !max }"
30
+ style="top: 28px">
31
+ {{ maxCounter }}
32
+ </label>
33
+ </div>
34
+ <div :id="`${inputId}Help`" class="form-text text-danger" :class="{ 'd-none': !props.err }">
35
+ {{ props.err }}
36
+ </div>
37
+ </div>
38
+ </template>
39
+
40
+ <script setup lang="ts">
41
+ import { computed, nextTick, onMounted, useAttrs } from "vue";
42
+
43
+ defineOptions({ name: "TextArea", inheritAttrs: false });
44
+
45
+ const $attrs = useAttrs();
46
+ interface TextAreaProps {
47
+ focus?: boolean;
48
+ must?: boolean;
49
+ err?: string | boolean;
50
+ modelValue?: string | number;
51
+ }
52
+
53
+ const props = withDefaults(defineProps<TextAreaProps>(), {
54
+ modelValue: ""
55
+ });
56
+
57
+ const inputId = computed(() => ($attrs.id as string | undefined) || "");
58
+ const inputLabel = computed(() => ($attrs.label as string | undefined) || "");
59
+ const placeholderText = computed(() => ($attrs.placeholder as string | undefined) || "");
60
+ const topclass = computed(() => ($attrs.topclass as string | undefined) || "");
61
+
62
+ const wrapperClass = computed(() => (topclass.value ? `${topclass.value} position-relative` : "mb-2"));
63
+
64
+ const max = computed<number | null>(() => {
65
+ const raw = $attrs.maxlength as string | number | undefined;
66
+ return raw != null ? Number(raw) : null;
67
+ });
68
+
69
+ const maxLengthAttr = computed<number | undefined>(() => max.value ?? undefined);
70
+ const maxCounter = computed(() => (max.value == null ? "" : max.value - String(props.modelValue).length));
71
+
72
+ const emit = defineEmits<(event: "update:modelValue", value: string) => void>();
73
+ const updateModel = (e: Event) => {
74
+ const target = e.target as HTMLTextAreaElement;
75
+ emit("update:modelValue", target.value);
76
+ };
77
+
78
+ onMounted(() => {
79
+ if (!props.focus || !inputId.value) {
80
+ return;
81
+ }
82
+
83
+ nextTick(() => {
84
+ const textarea = document.querySelector(`#${inputId.value}`) as HTMLTextAreaElement | null;
85
+ textarea?.focus();
86
+ });
87
+ });
88
+ </script>
89
+
90
+ <style lang="scss" scoped>
91
+ .form-floating {
92
+ label {
93
+ top: -1px;
94
+ left: 1px;
95
+ }
96
+
97
+ textarea:focus+label,
98
+ textarea:not(:placeholder-shown)+label {
99
+ top: -6px;
100
+ }
101
+ }
102
+
103
+ .placeholder-label {
104
+ color: #cccccc !important;
105
+ }
106
+
107
+ .must {
108
+ width: 4px;
109
+ height: 4px;
110
+ margin-left: 2px;
111
+ margin-top: 5px;
112
+ }
113
+
114
+ .max {
115
+ font-size: 0.6rem;
116
+ }
117
+ </style>
@@ -0,0 +1,56 @@
1
+ <template>
2
+ <Teleport to="#toast-show">
3
+ <div
4
+ ref="notify"
5
+ class="toast position-fixed"
6
+ role="alert"
7
+ aria-live="assertive"
8
+ aria-atomic="true"
9
+ style="z-index: 1000000; top: 10px; right: 23px"
10
+ data-bs-delay="2000">
11
+ <div :class="`text-bg-${bgcolor} align-items-center rounded-1 shadow-sm`">
12
+ <div class="d-flex">
13
+ <div class="toast-body">
14
+ {{ message }}
15
+ </div>
16
+ <button
17
+ type="button"
18
+ class="btn-close btn-close-white me-2 m-auto"
19
+ data-bs-dismiss="toast"
20
+ aria-label="Close"></button>
21
+ </div>
22
+ </div>
23
+ </div>
24
+ </Teleport>
25
+ </template>
26
+
27
+ <script setup lang="ts">
28
+ import Toast from "bootstrap/js/dist/toast.js";
29
+ import { ref } from "vue";
30
+
31
+ defineOptions({ name: "Toast", inheritAttrs: false });
32
+
33
+ interface ToastProps {
34
+ icon?: string;
35
+ }
36
+
37
+ defineProps<ToastProps>();
38
+
39
+ const notify = ref<HTMLElement | null>(null);
40
+ const message = ref("");
41
+ const bgcolor = ref("primary");
42
+
43
+ const toastme = (msg: string, bg: string) => {
44
+ message.value = msg;
45
+ bgcolor.value = bg;
46
+ if (!notify.value) {
47
+ return;
48
+ }
49
+ const toast = Toast.getOrCreateInstance(notify.value);
50
+ toast.show();
51
+ };
52
+
53
+ defineExpose({ toastme });
54
+ </script>
55
+
56
+ <style lang="scss" scoped></style>