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.
- package/README.md +29 -12
- package/express/.env +1 -1
- package/express/package.json +1 -1
- package/express/src/config/database.ts +48 -29
- package/express/src/config/index.ts +83 -83
- package/express/src/config/openapi.ts +6 -24
- package/express/src/config/queue.ts +30 -30
- package/express/src/config/validate.ts +50 -40
- package/express/src/database/migrations/sqlite/0000_init.sql +47 -47
- package/express/src/database/migrations/sqlite/meta/0000_snapshot.json +1 -1
- package/express/src/database/migrations/sqlite/meta/_journal.json +1 -1
- package/express/src/framework/cache/cache.ts +40 -8
- package/express/src/framework/circuit-breaker/cb.ts +88 -60
- package/express/src/framework/database/connection.ts +52 -27
- package/express/src/framework/events/dispatcher.ts +61 -49
- package/express/src/framework/facade.ts +47 -3
- package/express/src/framework/http/app.ts +28 -22
- package/express/src/framework/http/logger.ts +37 -17
- package/express/src/framework/http/openapi.ts +9 -11
- package/express/src/framework/maker-cli/runtime/core.mjs +1 -2
- package/express/src/framework/modules/routes.ts +26 -21
- package/express/src/framework/notification/channels/broadcast.ts +15 -0
- package/express/src/framework/notification/channels/mail.ts +42 -0
- package/express/src/framework/notification/contracts.ts +29 -0
- package/express/src/framework/notification/index.ts +19 -94
- package/express/src/framework/notification/registry.ts +15 -0
- package/express/src/framework/providers/adapter.ts +27 -17
- package/express/src/framework/queue/queue.ts +43 -44
- package/express/src/framework/realtime/broadcast.ts +29 -5
- package/express/src/framework/realtime/socket.ts +149 -119
- package/express/src/framework/redis/client.ts +148 -151
- package/express/src/framework/runtime/server-adapter.ts +39 -34
- package/express/src/framework/scheduler/lock.ts +128 -116
- package/express/src/framework/server.ts +33 -49
- package/express/src/framework/session/session.ts +69 -14
- package/express/src/framework/storage/storage.ts +744 -785
- package/express/src/framework/support/mail.ts +15 -7
- package/express/src/middlewares/auth-middleware.ts +1 -2
- package/express/src/middlewares/role-middleware.ts +3 -13
- package/express/src/modules/auth/routes/index.ts +1 -2
- package/express/src/providers/notification.ts +4 -0
- package/express/src/resources/src/components/Switch.vue +60 -65
- package/express/src/resources/src/layouts/Layout/Header.vue +124 -105
- package/express/src/resources/src/pages/auth/forgetPassword.vue +23 -30
- package/express/src/resources/src/pages/auth/login.vue +25 -28
- package/express/src/resources/src/pages/auth/register.vue +35 -39
- package/express/src/resources/src/pages/auth/resetPassword.vue +29 -37
- package/express/src/resources/src/pages/auth/verifyEmail.vue +15 -19
- package/express/src/resources/src/plugins/gum.ts +519 -203
- package/express/src/resources/src/plugins/pulse.ts +157 -147
- package/express/src/resources/src/stores/auth.ts +9 -6
- package/express/src/resources/vite.config.ts +54 -43
- package/express/src/storage/database/nexwire.sqlite +0 -0
- package/express/src/storage/logs/app.log +0 -0
- package/express/src/storage/logs/fatal.log +0 -0
- package/express/src/types/global.d.ts +32 -26
- package/fastify/.dockerignore +14 -0
- package/fastify/.env +44 -0
- package/fastify/.env.example +42 -0
- package/fastify/.vscode/extensions.json +6 -0
- package/fastify/.vscode/settings.json +37 -0
- package/fastify/LICENSE +21 -0
- package/fastify/README.md +232 -0
- package/fastify/biome.json +99 -0
- package/fastify/drizzle.config.ts +25 -0
- package/fastify/gitignore-stub +31 -0
- package/fastify/package.json +130 -0
- package/fastify/public/.gitkeep +0 -0
- package/fastify/src/config/app.ts +41 -0
- package/fastify/src/config/auth.ts +12 -0
- package/fastify/src/config/cache.ts +16 -0
- package/fastify/src/config/cookie.ts +15 -0
- package/fastify/src/config/cors.ts +12 -0
- package/fastify/src/config/database.ts +48 -0
- package/fastify/src/config/index.ts +83 -0
- package/fastify/src/config/jwt.ts +19 -0
- package/fastify/src/config/logging.ts +13 -0
- package/fastify/src/config/mail.ts +24 -0
- package/fastify/src/config/openapi.ts +36 -0
- package/fastify/src/config/queue.ts +30 -0
- package/fastify/src/config/rateLimit.ts +17 -0
- package/fastify/src/config/realtime.ts +15 -0
- package/fastify/src/config/redis.ts +18 -0
- package/fastify/src/config/security.ts +44 -0
- package/fastify/src/config/session.ts +17 -0
- package/fastify/src/config/storage.ts +22 -0
- package/fastify/src/config/validate.ts +50 -0
- package/fastify/src/database/migrations/sqlite/0000_init.sql +48 -0
- package/fastify/src/database/migrations/sqlite/meta/0000_snapshot.json +324 -0
- package/fastify/src/database/migrations/sqlite/meta/_journal.json +13 -0
- package/fastify/src/database/schema.ts +2 -0
- package/fastify/src/env.ts +76 -0
- package/fastify/src/framework/cache/cache.ts +114 -0
- package/fastify/src/framework/circuit-breaker/cb.ts +88 -0
- package/fastify/src/framework/database/app-schema.ts +37 -0
- package/fastify/src/framework/database/connection.ts +223 -0
- package/fastify/src/framework/database/migrate-hooks.ts +87 -0
- package/fastify/src/framework/database/paginate.ts +226 -0
- package/fastify/src/framework/database/schema.ts +20 -0
- package/fastify/src/framework/database/seed.ts +152 -0
- package/fastify/src/framework/events/dispatcher.ts +61 -0
- package/fastify/src/framework/facade.ts +96 -0
- package/fastify/src/framework/http/app.ts +176 -0
- package/fastify/src/framework/http/cors.ts +13 -0
- package/fastify/src/framework/http/fastify.d.ts +13 -0
- package/fastify/src/framework/http/logger.ts +110 -0
- package/fastify/src/framework/http/metrics.ts +22 -0
- package/fastify/src/framework/http/openapi.ts +48 -0
- package/fastify/src/framework/http/ratelimiter.ts +47 -0
- package/fastify/src/framework/http/security.ts +25 -0
- package/fastify/src/framework/http/static.ts +83 -0
- package/fastify/src/framework/http/validation.ts +49 -0
- package/fastify/src/framework/kernel.ts +43 -0
- package/fastify/src/framework/maker-cli/db/core.mjs +291 -0
- package/fastify/src/framework/maker-cli/db/index.mjs +204 -0
- package/fastify/src/framework/maker-cli/deploy/core.mjs +1046 -0
- package/fastify/src/framework/maker-cli/deploy/index.mjs +91 -0
- package/fastify/src/framework/maker-cli/index.mjs +65 -0
- package/fastify/src/framework/maker-cli/middleware/core.mjs +41 -0
- package/fastify/src/framework/maker-cli/middleware/index.mjs +17 -0
- package/fastify/src/framework/maker-cli/module/core.mjs +1066 -0
- package/fastify/src/framework/maker-cli/module/index.mjs +211 -0
- package/fastify/src/framework/maker-cli/provider/core.mjs +49 -0
- package/fastify/src/framework/maker-cli/provider/index.mjs +12 -0
- package/fastify/src/framework/maker-cli/runtime/build-ui.mjs +16 -0
- package/fastify/src/framework/maker-cli/runtime/core.mjs +560 -0
- package/fastify/src/framework/maker-cli/runtime/index.mjs +120 -0
- package/fastify/src/framework/maker-cli/runtime/redis-commander-patch.cjs +20 -0
- package/fastify/src/framework/maker-cli/stubs/controller/openapi.ts.stub +74 -0
- package/fastify/src/framework/maker-cli/stubs/controller/plain.ts.stub +77 -0
- package/fastify/src/framework/maker-cli/stubs/controller/standalone.ts.stub +51 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/Dockerfile.bun.stub +52 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/Dockerfile.pnpm.stub +55 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/Dockerfile.stub +51 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/Dockerfile.yarn.stub +55 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/README.stub +59 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/compose/mysql.server.stub +45 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/compose/postgres.server.stub +45 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/compose/sqlite.stub +45 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/env/mysql.server.stub +68 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/env/postgres.server.stub +68 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/env/sqlite.stub +67 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/scripts/auto-migrate.sh.stub +41 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/README.stub +78 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/compose/noredis.stub +120 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/compose/redis.dev.stub +133 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/compose/redis.stub +131 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/env/local.example.stub +11 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/env/noredis.stub +25 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/env/redis.stub +25 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/nginx-vhost/README.stub +16 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/nginx-vhost/app.example.com.stub +13 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/pgadmin/servers.stub +14 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/redis/redis.conf.stub +7 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/supervisor/noredis.stub +54 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/supervisor/redis.stub +70 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/workflow/local.json.stub +25 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/workflow/remote.json.stub +24 -0
- package/fastify/src/framework/maker-cli/stubs/facade/name.ts.stub +12 -0
- package/fastify/src/framework/maker-cli/stubs/helper/name.ts.stub +14 -0
- package/fastify/src/framework/maker-cli/stubs/job/name.ts.stub +25 -0
- package/fastify/src/framework/maker-cli/stubs/middleware/name.local.ts.stub +15 -0
- package/fastify/src/framework/maker-cli/stubs/middleware/name.ts.stub +15 -0
- package/fastify/src/framework/maker-cli/stubs/model/name.mysql.ts.stub +10 -0
- package/fastify/src/framework/maker-cli/stubs/model/name.postgresql.ts.stub +10 -0
- package/fastify/src/framework/maker-cli/stubs/model/name.sqlite.ts.stub +10 -0
- package/fastify/src/framework/maker-cli/stubs/notification/controller.ts.stub +79 -0
- package/fastify/src/framework/maker-cli/stubs/notification/job.ts.stub +11 -0
- package/fastify/src/framework/maker-cli/stubs/notification/model.ts.stub +29 -0
- package/fastify/src/framework/maker-cli/stubs/notification/provider.ts.stub +174 -0
- package/fastify/src/framework/maker-cli/stubs/notification/route.api.ts.stub +71 -0
- package/fastify/src/framework/maker-cli/stubs/notification/route.plain.ts.stub +15 -0
- package/fastify/src/framework/maker-cli/stubs/notification/schema.plain.ts.stub +26 -0
- package/fastify/src/framework/maker-cli/stubs/notification/schema.ts.stub +26 -0
- package/fastify/src/framework/maker-cli/stubs/provider/module-barrel.ts.stub +19 -0
- package/fastify/src/framework/maker-cli/stubs/provider/module.ts.stub +21 -0
- package/fastify/src/framework/maker-cli/stubs/provider/name.ts.stub +21 -0
- package/fastify/src/framework/maker-cli/stubs/route/api.ts.stub +101 -0
- package/fastify/src/framework/maker-cli/stubs/route/plain.ts.stub +23 -0
- package/fastify/src/framework/maker-cli/stubs/route/standalone.api.ts.stub +120 -0
- package/fastify/src/framework/maker-cli/stubs/route/standalone.plain.ts.stub +50 -0
- package/fastify/src/framework/maker-cli/stubs/schedule/name.ts.stub +36 -0
- package/fastify/src/framework/maker-cli/stubs/schema/name.plain.ts.stub +14 -0
- package/fastify/src/framework/maker-cli/stubs/schema/name.ts.stub +33 -0
- package/fastify/src/framework/maker-cli/stubs/seeder/name.ts.stub +18 -0
- package/fastify/src/framework/maker-cli/stubs/seeder/standalone.ts.stub +22 -0
- package/fastify/src/framework/maker-cli/stubs/service/name.plain.ts.stub +67 -0
- package/fastify/src/framework/maker-cli/stubs/service/name.ts.stub +65 -0
- package/fastify/src/framework/maker-cli/stubs/test/unit.ts.stub +22 -0
- package/fastify/src/framework/maker-cli/stubs/type/name.ts.stub +22 -0
- package/fastify/src/framework/maker-cli/utils/env-config.mjs +44 -0
- package/fastify/src/framework/maker-cli/utils/env-db.mjs +61 -0
- package/fastify/src/framework/maker-cli/utils/file-ops.mjs +30 -0
- package/fastify/src/framework/maker-cli/utils/flags.mjs +14 -0
- package/fastify/src/framework/maker-cli/utils/help.mjs +379 -0
- package/fastify/src/framework/maker-cli/utils/naming.mjs +119 -0
- package/fastify/src/framework/maker-cli/utils/process.mjs +52 -0
- package/fastify/src/framework/maker-cli/utils/project-commands.mjs +201 -0
- package/fastify/src/framework/modules/discover.ts +57 -0
- package/fastify/src/framework/modules/routes.ts +31 -0
- package/fastify/src/framework/notification/channels/broadcast.ts +15 -0
- package/fastify/src/framework/notification/channels/mail.ts +42 -0
- package/fastify/src/framework/notification/contracts.ts +29 -0
- package/fastify/src/framework/notification/index.ts +59 -0
- package/fastify/src/framework/notification/registry.ts +15 -0
- package/fastify/src/framework/providers/adapter.ts +149 -0
- package/fastify/src/framework/providers/contracts.ts +126 -0
- package/fastify/src/framework/providers/loader.ts +105 -0
- package/fastify/src/framework/providers/registry.ts +88 -0
- package/fastify/src/framework/queue/clear.ts +22 -0
- package/fastify/src/framework/queue/queue.ts +401 -0
- package/fastify/src/framework/queue/ui.ts +194 -0
- package/fastify/src/framework/queue/worker.ts +61 -0
- package/fastify/src/framework/realtime/broadcast.ts +70 -0
- package/fastify/src/framework/realtime/index.ts +2 -0
- package/fastify/src/framework/realtime/socket-cookie.ts +72 -0
- package/fastify/src/framework/realtime/socket.ts +149 -0
- package/fastify/src/framework/realtime/types.ts +6 -0
- package/fastify/src/framework/realtime/ui.ts +16 -0
- package/fastify/src/framework/redis/client.ts +148 -0
- package/fastify/src/framework/runtime/runtime.ts +23 -0
- package/fastify/src/framework/runtime/server-adapter.ts +33 -0
- package/fastify/src/framework/scheduler/lock.ts +128 -0
- package/fastify/src/framework/scheduler/run.ts +48 -0
- package/fastify/src/framework/scheduler/scheduler.ts +155 -0
- package/fastify/src/framework/server.ts +162 -0
- package/fastify/src/framework/session/session.ts +160 -0
- package/fastify/src/framework/storage/storage.ts +744 -0
- package/fastify/src/framework/support/cookie.ts +71 -0
- package/fastify/src/framework/support/jwt.ts +33 -0
- package/fastify/src/framework/support/lifecycle.ts +35 -0
- package/fastify/src/framework/support/logger.ts +148 -0
- package/fastify/src/framework/support/mail.ts +56 -0
- package/fastify/src/framework/support/password.ts +23 -0
- package/fastify/src/framework/support/status-codes.ts +75 -0
- package/fastify/src/framework/support/upload.ts +181 -0
- package/fastify/src/framework/support/url.ts +41 -0
- package/fastify/src/middlewares/auth-middleware.ts +134 -0
- package/fastify/src/middlewares/role-middleware.ts +19 -0
- package/fastify/src/modules/auth/__tests__/forgetpass.test.ts +200 -0
- package/fastify/src/modules/auth/__tests__/login.test.ts +171 -0
- package/fastify/src/modules/auth/__tests__/logout.test.ts +60 -0
- package/fastify/src/modules/auth/__tests__/logoutalldevices.test.ts +49 -0
- package/fastify/src/modules/auth/__tests__/me.test.ts +91 -0
- package/fastify/src/modules/auth/__tests__/refreshtoken.test.ts +305 -0
- package/fastify/src/modules/auth/__tests__/register.test.ts +291 -0
- package/fastify/src/modules/auth/__tests__/resetpass.test.ts +206 -0
- package/fastify/src/modules/auth/__tests__/verifyemail.test.ts +183 -0
- package/fastify/src/modules/auth/controllers/auth.ts +252 -0
- package/fastify/src/modules/auth/controllers/role.ts +35 -0
- package/fastify/src/modules/auth/database/models/role.ts +14 -0
- package/fastify/src/modules/auth/database/models/user.ts +46 -0
- package/fastify/src/modules/auth/database/seeders/role.ts +18 -0
- package/fastify/src/modules/auth/database/seeders/user.ts +35 -0
- package/fastify/src/modules/auth/facade.ts +12 -0
- package/fastify/src/modules/auth/helpers/auth.ts +115 -0
- package/fastify/src/modules/auth/jobs/forgetpass.ts +20 -0
- package/fastify/src/modules/auth/jobs/registeruser.ts +23 -0
- package/fastify/src/modules/auth/jobs/verifyemail.ts +18 -0
- package/fastify/src/modules/auth/routes/index.ts +185 -0
- package/fastify/src/modules/auth/routes/role.ts +56 -0
- package/fastify/src/modules/auth/schemas/auth.ts +116 -0
- package/fastify/src/modules/auth/services/forgetpassword.ts +66 -0
- package/fastify/src/modules/auth/services/login.ts +63 -0
- package/fastify/src/modules/auth/services/logout.ts +25 -0
- package/fastify/src/modules/auth/services/logoutalldevices.ts +19 -0
- package/fastify/src/modules/auth/services/me.ts +31 -0
- package/fastify/src/modules/auth/services/refreshtoken.ts +110 -0
- package/fastify/src/modules/auth/services/register.ts +141 -0
- package/fastify/src/modules/auth/services/resetpassword.ts +69 -0
- package/fastify/src/modules/auth/services/verifyemail.ts +77 -0
- package/fastify/src/modules/auth/types/auth.ts +31 -0
- package/fastify/src/providers/README.md +107 -0
- package/fastify/src/providers/notification.ts +4 -0
- package/fastify/src/resources/index.html +17 -0
- package/fastify/src/resources/src/App.vue +5 -0
- package/fastify/src/resources/src/assets/css/styles.css +14761 -0
- package/fastify/src/resources/src/assets/css/styles.css.map +1 -0
- package/fastify/src/resources/src/assets/fonts/public-sans/public-sans-latin-ext.woff2 +0 -0
- package/fastify/src/resources/src/assets/fonts/public-sans/public-sans-latin.woff2 +0 -0
- package/fastify/src/resources/src/assets/images/favicon/favicon.ico +0 -0
- package/fastify/src/resources/src/assets/images/logo.png +0 -0
- package/fastify/src/resources/src/assets/scss/custom.css +217 -0
- package/fastify/src/resources/src/assets/scss/custom.css.map +1 -0
- package/fastify/src/resources/src/assets/scss/custom.scss +1346 -0
- package/fastify/src/resources/src/components/Button.vue +32 -0
- package/fastify/src/resources/src/components/Checkbox.vue +32 -0
- package/fastify/src/resources/src/components/Datepicker.vue +106 -0
- package/fastify/src/resources/src/components/FeatureButton.vue +53 -0
- package/fastify/src/resources/src/components/FloatButton.vue +33 -0
- package/fastify/src/resources/src/components/Href.vue +32 -0
- package/fastify/src/resources/src/components/Input.vue +243 -0
- package/fastify/src/resources/src/components/InputGroup.vue +147 -0
- package/fastify/src/resources/src/components/InputPasswordToggle.vue +229 -0
- package/fastify/src/resources/src/components/Modal.vue +71 -0
- package/fastify/src/resources/src/components/Pagebar.vue +26 -0
- package/fastify/src/resources/src/components/Refresh.vue +19 -0
- package/fastify/src/resources/src/components/Select.vue +384 -0
- package/fastify/src/resources/src/components/Spinner.vue +42 -0
- package/fastify/src/resources/src/components/Switch.vue +60 -0
- package/fastify/src/resources/src/components/TextArea.vue +117 -0
- package/fastify/src/resources/src/components/Toast.vue +56 -0
- package/fastify/src/resources/src/components/datatable/DataTableSkeleton.vue +98 -0
- package/fastify/src/resources/src/components/datatable/Pagination.vue +147 -0
- package/fastify/src/resources/src/components/datatable/SelectOpption.vue +64 -0
- package/fastify/src/resources/src/components/datatable/index.vue +199 -0
- package/fastify/src/resources/src/composables/useAuth.ts +52 -0
- package/fastify/src/resources/src/helpers/assets.ts +51 -0
- package/fastify/src/resources/src/helpers/nformatter.ts +10 -0
- package/fastify/src/resources/src/helpers/utils.ts +75 -0
- package/fastify/src/resources/src/layouts/AuthLayout.vue +20 -0
- package/fastify/src/resources/src/layouts/Layout/Footer.vue +23 -0
- package/fastify/src/resources/src/layouts/Layout/Header.vue +124 -0
- package/fastify/src/resources/src/layouts/Layout/Sidebar.vue +137 -0
- package/fastify/src/resources/src/layouts/Layout/index.vue +87 -0
- package/fastify/src/resources/src/main.ts +28 -0
- package/fastify/src/resources/src/pages/auth/forgetPassword.vue +76 -0
- package/fastify/src/resources/src/pages/auth/login.vue +96 -0
- package/fastify/src/resources/src/pages/auth/register.vue +129 -0
- package/fastify/src/resources/src/pages/auth/resetPassword.vue +110 -0
- package/fastify/src/resources/src/pages/auth/verifyEmail.vue +64 -0
- package/fastify/src/resources/src/pages/dashboard/index.vue +66 -0
- package/fastify/src/resources/src/plugins/axios.ts +31 -0
- package/fastify/src/resources/src/plugins/browserDetect.ts +55 -0
- package/fastify/src/resources/src/plugins/dialog.ts +216 -0
- package/fastify/src/resources/src/plugins/gum.ts +683 -0
- package/fastify/src/resources/src/plugins/pulse.ts +157 -0
- package/fastify/src/resources/src/plugins/routeProgress.ts +87 -0
- package/fastify/src/resources/src/router/index.ts +85 -0
- package/fastify/src/resources/src/stores/admin-ui.ts +148 -0
- package/fastify/src/resources/src/stores/auth.ts +187 -0
- package/fastify/src/resources/tsconfig.json +19 -0
- package/fastify/src/resources/vite.config.ts +64 -0
- package/fastify/src/storage/app/private/.gitkeep +0 -0
- package/fastify/src/storage/app/public/.gitkeep +0 -0
- package/fastify/src/storage/app/tmp/generated/.gitkeep +0 -0
- package/fastify/src/storage/database/nexwire.sqlite +0 -0
- package/fastify/src/storage/logs/.gitkeep +0 -0
- package/fastify/src/storage/logs/app.log +0 -0
- package/fastify/src/storage/logs/fatal.log +0 -0
- package/fastify/src/storage/tmp/.gitkeep +0 -0
- package/fastify/src/storage/trash/modules/.gitkeep +0 -0
- package/fastify/src/types/global.d.ts +32 -0
- package/fastify/tsconfig.json +19 -0
- package/fastify/vitest.config.ts +38 -0
- package/hono/.env +1 -1
- package/hono/package.json +1 -1
- package/hono/src/config/database.ts +48 -29
- package/hono/src/config/index.ts +83 -83
- package/hono/src/config/openapi.ts +6 -24
- package/hono/src/config/queue.ts +30 -30
- package/hono/src/config/validate.ts +50 -40
- package/hono/src/database/migrations/sqlite/0000_init.sql +47 -47
- package/hono/src/database/migrations/sqlite/meta/0000_snapshot.json +1 -1
- package/hono/src/database/migrations/sqlite/meta/_journal.json +1 -1
- package/hono/src/framework/cache/cache.ts +40 -8
- package/hono/src/framework/circuit-breaker/cb.ts +88 -60
- package/hono/src/framework/database/connection.ts +52 -27
- package/hono/src/framework/events/dispatcher.ts +61 -49
- package/hono/src/framework/facade.ts +39 -1
- package/hono/src/framework/http/logger.ts +86 -81
- package/hono/src/framework/http/openapi.ts +7 -9
- package/hono/src/framework/maker-cli/runtime/core.mjs +1 -2
- package/hono/src/framework/notification/channels/broadcast.ts +15 -0
- package/hono/src/framework/notification/channels/mail.ts +42 -0
- package/hono/src/framework/notification/contracts.ts +29 -0
- package/hono/src/framework/notification/index.ts +19 -94
- package/hono/src/framework/notification/registry.ts +15 -0
- package/hono/src/framework/providers/adapter.ts +27 -17
- package/hono/src/framework/queue/queue.ts +43 -44
- package/hono/src/framework/realtime/broadcast.ts +29 -5
- package/hono/src/framework/realtime/socket.ts +217 -196
- package/hono/src/framework/redis/client.ts +148 -151
- package/hono/src/framework/scheduler/lock.ts +128 -116
- package/hono/src/framework/server.ts +27 -29
- package/hono/src/framework/session/session.ts +62 -14
- package/hono/src/framework/storage/storage.ts +744 -785
- package/hono/src/framework/support/mail.ts +56 -48
- package/hono/src/middlewares/auth-middleware.ts +1 -2
- package/hono/src/middlewares/role-middleware.ts +3 -13
- package/hono/src/modules/auth/routes/index.ts +1 -2
- package/hono/src/providers/notification.ts +4 -0
- package/hono/src/resources/src/components/Switch.vue +60 -65
- package/hono/src/resources/src/layouts/Layout/Header.vue +124 -105
- package/hono/src/resources/src/pages/auth/forgetPassword.vue +23 -29
- package/hono/src/resources/src/pages/auth/login.vue +26 -29
- package/hono/src/resources/src/pages/auth/register.vue +36 -40
- package/hono/src/resources/src/pages/auth/resetPassword.vue +29 -37
- package/hono/src/resources/src/pages/auth/verifyEmail.vue +15 -19
- package/hono/src/resources/src/plugins/gum.ts +519 -203
- package/hono/src/resources/src/plugins/pulse.ts +157 -147
- package/hono/src/resources/src/stores/auth.ts +9 -6
- package/hono/src/resources/vite.config.ts +54 -43
- package/hono/src/storage/database/nexwire.sqlite +0 -0
- package/hono/src/storage/logs/app.log +0 -0
- package/hono/src/storage/logs/fatal.log +0 -0
- package/hono/src/types/global.d.ts +32 -26
- package/package.json +4 -3
- package/src/index.js +143 -15
|
@@ -5,17 +5,44 @@
|
|
|
5
5
|
* How: Re-exports stable, reusable helpers while hiding setup internals.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
// ==========================================
|
|
9
|
+
// 1. External & Core Utilities
|
|
10
|
+
// ==========================================
|
|
8
11
|
export { createRoute, z } from "@hono/zod-openapi";
|
|
9
12
|
export * as lodash from "lodash-es";
|
|
10
13
|
export * as HttpStatusCodes from "stoker/http-status-codes";
|
|
11
14
|
export { jsonContent } from "stoker/openapi/helpers";
|
|
15
|
+
// ==========================================
|
|
16
|
+
// 6. Cache & Session
|
|
17
|
+
// ==========================================
|
|
12
18
|
export { cache } from "@/framework/cache/cache.js";
|
|
19
|
+
// ==========================================
|
|
20
|
+
// 2. Database
|
|
21
|
+
// ==========================================
|
|
13
22
|
export { database, db } from "@/framework/database/connection.js";
|
|
14
23
|
export { type PaginatedResult, paginate, paginateModel, paginateQuery, paginateTable } from "@/framework/database/paginate.js";
|
|
24
|
+
// ==========================================
|
|
25
|
+
// 4. Events & Queue
|
|
26
|
+
// ==========================================
|
|
15
27
|
export { command, dispatchCommand, dispatchEvent } from "@/framework/events/dispatcher.js";
|
|
28
|
+
// ==========================================
|
|
29
|
+
// 3. HTTP & Routing
|
|
30
|
+
// ==========================================
|
|
31
|
+
export { loginLimiter } from "@/framework/http/ratelimiter.js";
|
|
16
32
|
export { createRouter, group } from "@/framework/http/router.js";
|
|
17
33
|
export { validate } from "@/framework/http/validation.js";
|
|
34
|
+
export { broadcastChannel } from "@/framework/notification/channels/broadcast.js";
|
|
35
|
+
export { mailChannel } from "@/framework/notification/channels/mail.js";
|
|
36
|
+
export type { NotificationChannel, NotificationOptions } from "@/framework/notification/contracts.js";
|
|
37
|
+
// ==========================================
|
|
38
|
+
// 10. Notification Engine (Delivery Channels)
|
|
39
|
+
// ==========================================
|
|
18
40
|
export { notify } from "@/framework/notification/index.js";
|
|
41
|
+
export { registerChannel } from "@/framework/notification/registry.js";
|
|
42
|
+
// ==========================================
|
|
43
|
+
// 9. Providers (Storage/Auth)
|
|
44
|
+
// ==========================================
|
|
45
|
+
export { resolveAuthProvider, resolveNotificationProvider } from "@/framework/providers/adapter.js";
|
|
19
46
|
export type {
|
|
20
47
|
AuthProvider,
|
|
21
48
|
AuthProviderRefreshToken,
|
|
@@ -29,7 +56,6 @@ export type {
|
|
|
29
56
|
NotificationProvider,
|
|
30
57
|
NotificationRecord
|
|
31
58
|
} from "@/framework/providers/contracts.js";
|
|
32
|
-
export { resolveAuthProvider, resolveNotificationProvider } from "@/framework/providers/adapter.js";
|
|
33
59
|
export { loadProviders } from "@/framework/providers/loader.js";
|
|
34
60
|
export {
|
|
35
61
|
clearAuthProvider,
|
|
@@ -47,12 +73,24 @@ export {
|
|
|
47
73
|
registerProvider
|
|
48
74
|
} from "@/framework/providers/registry.js";
|
|
49
75
|
export { queue, queueJob, shouldQueue } from "@/framework/queue/queue.js";
|
|
76
|
+
// ==========================================
|
|
77
|
+
// 5. Realtime
|
|
78
|
+
// ==========================================
|
|
50
79
|
export { broadcast } from "@/framework/realtime/index.js";
|
|
51
80
|
export { defineSchedule } from "@/framework/scheduler/scheduler.js";
|
|
52
81
|
export { session } from "@/framework/session/session.js";
|
|
82
|
+
// ==========================================
|
|
83
|
+
// 7. Storage & Mail
|
|
84
|
+
// ==========================================
|
|
53
85
|
export { storage } from "@/framework/storage/storage.js";
|
|
86
|
+
// ==========================================
|
|
87
|
+
// 8. Security & Auth
|
|
88
|
+
// ==========================================
|
|
54
89
|
export { cookie } from "@/framework/support/cookie.js";
|
|
55
90
|
export { jwt } from "@/framework/support/jwt.js";
|
|
91
|
+
// ==========================================
|
|
92
|
+
// 11. Utilities
|
|
93
|
+
// ==========================================
|
|
56
94
|
export { logger } from "@/framework/support/logger.js";
|
|
57
95
|
export { mail } from "@/framework/support/mail.js";
|
|
58
96
|
export { password } from "@/framework/support/password.js";
|
|
@@ -1,81 +1,86 @@
|
|
|
1
|
-
import type { Context, Next } from "hono";
|
|
2
|
-
import { loggingConfig } from "@/config/index.js";
|
|
3
|
-
import { logger } from "@/framework/support/logger.js";
|
|
4
|
-
|
|
5
|
-
function ip(c: Context) {
|
|
6
|
-
return c.req.header("cf-connecting-ip") || c.req.header("x-forwarded-for") || c.req.header("x-real-ip") || null;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
1
|
+
import type { Context, Next } from "hono";
|
|
2
|
+
import { loggingConfig } from "@/config/index.js";
|
|
3
|
+
import { logger } from "@/framework/support/logger.js";
|
|
4
|
+
|
|
5
|
+
function ip(c: Context) {
|
|
6
|
+
return c.req.header("cf-connecting-ip") || c.req.header("x-forwarded-for") || c.req.header("x-real-ip") || null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Probe endpoints and the queue dashboard are infrastructure traffic, not
|
|
10
|
+
// application requests. Skip them on every engine so the access log stays
|
|
11
|
+
// useful (the dev CLI polls /health while it boots).
|
|
12
|
+
const PROBE_PATHS = new Set(["/health", "/ready", "/live"]);
|
|
13
|
+
|
|
14
|
+
function shouldSkipHttpLog(pathname: string) {
|
|
15
|
+
return PROBE_PATHS.has(pathname) || pathname.startsWith("/queues");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Why: Logs each HTTP request with latency and request metadata.
|
|
20
|
+
* When: Applied globally to app routes.
|
|
21
|
+
* Where: HTTP middleware stack.
|
|
22
|
+
* How: Captures start time, assigns request id, logs after downstream response.
|
|
23
|
+
*/
|
|
24
|
+
export async function loggerMiddleware(c: Context, next: Next) {
|
|
25
|
+
const start = Date.now();
|
|
26
|
+
const requestId = crypto.randomUUID();
|
|
27
|
+
|
|
28
|
+
c.set("requestId", requestId);
|
|
29
|
+
await next();
|
|
30
|
+
|
|
31
|
+
if (!loggingConfig.httpRequests || shouldSkipHttpLog(c.req.path)) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
logger.info("HTTP Request", {
|
|
36
|
+
requestId,
|
|
37
|
+
method: c.req.method,
|
|
38
|
+
path: c.req.path,
|
|
39
|
+
url: c.req.url,
|
|
40
|
+
status: c.res.status,
|
|
41
|
+
duration_ms: Date.now() - start,
|
|
42
|
+
ip: ip(c),
|
|
43
|
+
userAgent: c.req.header("user-agent") || null
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Why: Standard 404 handler with structured log context.
|
|
49
|
+
* When: No route matches request path.
|
|
50
|
+
* Where: App notFound handler registration.
|
|
51
|
+
* How: Emits warning log and returns JSON 404 payload.
|
|
52
|
+
*/
|
|
53
|
+
export function notFound(c: Context) {
|
|
54
|
+
logger.warn("404 Not Found", {
|
|
55
|
+
requestId: c.get("requestId") || null,
|
|
56
|
+
method: c.req.method,
|
|
57
|
+
path: c.req.path,
|
|
58
|
+
url: c.req.url,
|
|
59
|
+
ip: ip(c),
|
|
60
|
+
userAgent: c.req.header("user-agent") || null
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return c.json({ message: "Not Found" }, 404);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Why: Centralized unhandled error responder and logger.
|
|
68
|
+
* When: Route/middleware throws uncaught error.
|
|
69
|
+
* Where: App onError handler registration.
|
|
70
|
+
* How: Logs stack + request context and returns status/message JSON.
|
|
71
|
+
*/
|
|
72
|
+
export function onError(error: any, c: Context) {
|
|
73
|
+
logger.error("Unhandled Application Error", {
|
|
74
|
+
requestId: c.get("requestId") || null,
|
|
75
|
+
name: error?.name || "Error",
|
|
76
|
+
message: error?.message,
|
|
77
|
+
stack: error?.stack,
|
|
78
|
+
method: c.req.method,
|
|
79
|
+
path: c.req.path,
|
|
80
|
+
url: c.req.url,
|
|
81
|
+
ip: ip(c),
|
|
82
|
+
userAgent: c.req.header("user-agent") || null
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return c.json({ message: error?.message || "Internal Server Error" }, error?.status || 500);
|
|
86
|
+
}
|
|
@@ -11,6 +11,8 @@ import type { NexwireRouter } from "@/framework/http/router.js";
|
|
|
11
11
|
* How: Exposes `/doc` spec and `/api-docs` Scalar viewer.
|
|
12
12
|
*/
|
|
13
13
|
export function configureOpenApi(app: NexwireRouter) {
|
|
14
|
+
const { specUrl, docsPath, favicon, ...scalarOptions } = openApiConfig.scalar;
|
|
15
|
+
|
|
14
16
|
app.doc("/doc", {
|
|
15
17
|
openapi: openApiConfig.version,
|
|
16
18
|
info: {
|
|
@@ -20,23 +22,19 @@ export function configureOpenApi(app: NexwireRouter) {
|
|
|
20
22
|
}
|
|
21
23
|
});
|
|
22
24
|
|
|
23
|
-
if (
|
|
25
|
+
if (favicon) {
|
|
24
26
|
app.get("/favicon.ico", (c) => {
|
|
25
|
-
const faviconPath = path.join(process.cwd(),
|
|
27
|
+
const faviconPath = path.join(process.cwd(), favicon);
|
|
26
28
|
if (!fs.existsSync(faviconPath)) return c.notFound();
|
|
27
29
|
return c.body(fs.readFileSync(faviconPath), 200, { "content-type": "image/x-icon" });
|
|
28
30
|
});
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
app.get(
|
|
32
|
-
|
|
34
|
+
docsPath, // Use the destructured path
|
|
33
35
|
Scalar({
|
|
34
|
-
url:
|
|
35
|
-
|
|
36
|
-
theme: openApiConfig.scalar.theme,
|
|
37
|
-
pageTitle: openApiConfig.scalar.pageTitle,
|
|
38
|
-
defaultHttpClient: openApiConfig.scalar.defaultHttpClient,
|
|
39
|
-
defaultOpenAllTags: openApiConfig.scalar.defaultOpenAllTags
|
|
36
|
+
url: specUrl, // Use the destructured URL
|
|
37
|
+
...scalarOptions // Pass EVERYTHING else automatically!
|
|
40
38
|
})
|
|
41
39
|
);
|
|
42
40
|
}
|
|
@@ -120,7 +120,7 @@ function serveRedisUnavailablePage(port, endpoint) {
|
|
|
120
120
|
</html>
|
|
121
121
|
`;
|
|
122
122
|
|
|
123
|
-
return new Promise((
|
|
123
|
+
return new Promise((_resolve, reject) => {
|
|
124
124
|
const server = http.createServer((_req, res) => {
|
|
125
125
|
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
126
126
|
res.end(body);
|
|
@@ -558,4 +558,3 @@ export async function clearViteCache() {
|
|
|
558
558
|
|
|
559
559
|
console.log("Vite cache cleared.");
|
|
560
560
|
}
|
|
561
|
-
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { dispatchEvent } from "@/framework/events/dispatcher.js";
|
|
2
|
+
import type { NotificationChannel } from "@/framework/notification/contracts.js";
|
|
3
|
+
|
|
4
|
+
export const broadcastChannel: NotificationChannel = {
|
|
5
|
+
name: "broadcast",
|
|
6
|
+
async send(target, notification, options) {
|
|
7
|
+
if (!options.broadcast) return false;
|
|
8
|
+
|
|
9
|
+
await dispatchEvent("notification.created", notification, {
|
|
10
|
+
broadcast: { users: [target.id] }
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { dispatchEvent } from "@/framework/events/dispatcher.js";
|
|
2
|
+
import type { NotificationChannel } from "@/framework/notification/contracts.js";
|
|
3
|
+
import { resolveAuthProvider } from "@/framework/providers/adapter.js";
|
|
4
|
+
import { redisClientIfReady } from "@/framework/redis/client.js";
|
|
5
|
+
import { mail } from "@/framework/support/mail.js";
|
|
6
|
+
|
|
7
|
+
function escapeHtml(str: string) {
|
|
8
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const mailChannel: NotificationChannel = {
|
|
12
|
+
name: "mail",
|
|
13
|
+
async send(target, notification, options) {
|
|
14
|
+
if (!options.mail) return false;
|
|
15
|
+
|
|
16
|
+
let to = options.mail.to || null;
|
|
17
|
+
if (!to) {
|
|
18
|
+
try {
|
|
19
|
+
const provider = await resolveAuthProvider();
|
|
20
|
+
if (provider?.getUserContact) {
|
|
21
|
+
const contact = await provider.getUserContact(target.id);
|
|
22
|
+
if (contact?.email) to = contact.email as string;
|
|
23
|
+
}
|
|
24
|
+
} catch {}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!to) return false;
|
|
28
|
+
|
|
29
|
+
const subject = options.mail.subject || notification.title;
|
|
30
|
+
const html =
|
|
31
|
+
options.mail.html ||
|
|
32
|
+
`<h2>${escapeHtml(notification.title)}</h2>${notification.body ? `<p>${escapeHtml(notification.body)}</p>` : ""}`;
|
|
33
|
+
|
|
34
|
+
if (redisClientIfReady()) {
|
|
35
|
+
await dispatchEvent("notification:mail", { to, subject, html }, { queue: "mail" });
|
|
36
|
+
} else {
|
|
37
|
+
await mail.sendMail({ to, subject, html });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { NotifiableTarget, NotificationRecord } from "@/framework/providers/contracts.js";
|
|
2
|
+
|
|
3
|
+
export type NotificationTarget = number | string | NotifiableTarget;
|
|
4
|
+
export type NormalizedTarget = { type: string; id: number | string };
|
|
5
|
+
|
|
6
|
+
export type NotificationOptions = {
|
|
7
|
+
type?: string;
|
|
8
|
+
title: string;
|
|
9
|
+
body?: string;
|
|
10
|
+
link?: string;
|
|
11
|
+
data?: Record<string, unknown>;
|
|
12
|
+
|
|
13
|
+
// Built-in Channels
|
|
14
|
+
broadcast?: boolean;
|
|
15
|
+
mail?: {
|
|
16
|
+
to?: string;
|
|
17
|
+
subject?: string;
|
|
18
|
+
html?: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Extensible: Add any custom channel options here (e.g., sms, push, discord)
|
|
22
|
+
sms?: { to?: string; message?: string };
|
|
23
|
+
push?: { token?: string; payload?: any };
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export interface NotificationChannel {
|
|
27
|
+
name: string;
|
|
28
|
+
send(target: NormalizedTarget, notification: NotificationRecord, options: NotificationOptions): Promise<boolean>;
|
|
29
|
+
}
|
|
@@ -1,39 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import { mail } from "@/framework/support/mail.js";
|
|
1
|
+
import type { NormalizedTarget, NotificationOptions, NotificationTarget } from "@/framework/notification/contracts.js";
|
|
2
|
+
import { getChannels } from "@/framework/notification/registry.js";
|
|
3
|
+
import { resolveNotificationProvider } from "@/framework/providers/adapter.js";
|
|
4
|
+
import type { NotificationRecord } from "@/framework/providers/contracts.js";
|
|
6
5
|
|
|
7
6
|
let missingProviderWarned = false;
|
|
8
7
|
|
|
9
|
-
/** A numeric id (default auth), a string id (UUID/ULID), or an explicit target. */
|
|
10
|
-
export type NotificationTarget = number | string | NotifiableTarget;
|
|
11
|
-
|
|
12
|
-
/** Extra JSON metadata attached to the notification (e.g. entity IDs, links). */
|
|
13
|
-
export type NotificationOptions = {
|
|
14
|
-
/** Notification category key (default: "info") */
|
|
15
|
-
type?: string;
|
|
16
|
-
/** Notification title */
|
|
17
|
-
title: string;
|
|
18
|
-
/** Notification body text */
|
|
19
|
-
body?: string;
|
|
20
|
-
/** Optional click-through URL */
|
|
21
|
-
link?: string;
|
|
22
|
-
/** Arbitrary JSON payload stored in `data` column */
|
|
23
|
-
data?: Record<string, unknown>;
|
|
24
|
-
/** Whether to broadcast the notification to the user's socket in realtime */
|
|
25
|
-
broadcast?: boolean;
|
|
26
|
-
/** Email delivery options */
|
|
27
|
-
mail?: {
|
|
28
|
-
/** Explicit recipient. Skips the auth provider contact lookup. */
|
|
29
|
-
to?: string;
|
|
30
|
-
subject?: string;
|
|
31
|
-
html?: string;
|
|
32
|
-
};
|
|
33
|
-
};
|
|
34
|
-
|
|
35
8
|
/** Turn the public target argument into the polymorphic owner pair. */
|
|
36
|
-
function normalizeTarget(target: NotificationTarget):
|
|
9
|
+
function normalizeTarget(target: NotificationTarget): NormalizedTarget {
|
|
37
10
|
if (target !== null && typeof target === "object") {
|
|
38
11
|
return {
|
|
39
12
|
type: String(target.type || "user").trim() || "user",
|
|
@@ -44,13 +17,10 @@ function normalizeTarget(target: NotificationTarget): Required<Pick<NotifiableTa
|
|
|
44
17
|
}
|
|
45
18
|
|
|
46
19
|
/**
|
|
47
|
-
* Why: Persists a notification
|
|
48
|
-
* When: A feature wants to record something for a user
|
|
20
|
+
* Why: Persists a notification to the database and triggers all active channels.
|
|
21
|
+
* When: A feature wants to record something for a user.
|
|
49
22
|
* Where: Application code, through the framework facade.
|
|
50
|
-
* How: The provider owns storage
|
|
51
|
-
* store cannot silently disable them. A missing provider is reported once
|
|
52
|
-
* and returns `null` instead of throwing, matching the framework's
|
|
53
|
-
* "optional module" behaviour.
|
|
23
|
+
* How: The provider owns database storage. The channels handle delivery (mail, sms, ws).
|
|
54
24
|
*/
|
|
55
25
|
export async function notify(target: NotificationTarget, options: NotificationOptions): Promise<NotificationRecord | null> {
|
|
56
26
|
const provider = await resolveNotificationProvider();
|
|
@@ -65,6 +35,8 @@ export async function notify(target: NotificationTarget, options: NotificationOp
|
|
|
65
35
|
}
|
|
66
36
|
|
|
67
37
|
const normalizedTarget = normalizeTarget(target);
|
|
38
|
+
|
|
39
|
+
// 1. Save to Database via the Provider
|
|
68
40
|
const normalized = await provider.create(normalizedTarget, {
|
|
69
41
|
type: options.type,
|
|
70
42
|
title: options.title,
|
|
@@ -73,62 +45,15 @@ export async function notify(target: NotificationTarget, options: NotificationOp
|
|
|
73
45
|
data: options.data ?? null
|
|
74
46
|
});
|
|
75
47
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
48
|
+
// 2. Dispatch to all registered channels in parallel
|
|
49
|
+
const channels = getChannels();
|
|
50
|
+
await Promise.allSettled(
|
|
51
|
+
channels.map((channel) =>
|
|
52
|
+
channel.send(normalizedTarget, normalized, options).catch((err) => {
|
|
53
|
+
console.error(`[notification] Channel ${channel.name} failed:`, err);
|
|
54
|
+
})
|
|
55
|
+
)
|
|
56
|
+
);
|
|
85
57
|
|
|
86
58
|
return normalized;
|
|
87
59
|
}
|
|
88
|
-
|
|
89
|
-
/** Deliver an email notification, either queued (if Redis is available) or direct. */
|
|
90
|
-
async function sendMailChannel(
|
|
91
|
-
target: { type: string; id: number | string },
|
|
92
|
-
notification: NotificationRecord,
|
|
93
|
-
options: NotificationOptions
|
|
94
|
-
) {
|
|
95
|
-
let to: string | null = options.mail?.to || null;
|
|
96
|
-
|
|
97
|
-
if (!to) {
|
|
98
|
-
try {
|
|
99
|
-
const provider = await resolveAuthProvider();
|
|
100
|
-
if (provider?.getUserContact) {
|
|
101
|
-
const contact = await provider.getUserContact(target.id);
|
|
102
|
-
if (contact?.email) to = contact.email as string;
|
|
103
|
-
}
|
|
104
|
-
} catch {}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (!to) return;
|
|
108
|
-
|
|
109
|
-
const subject = options.mail?.subject || notification.title;
|
|
110
|
-
const html = options.mail?.html || defaultMailHtml(notification.title, notification.body);
|
|
111
|
-
|
|
112
|
-
if (redisClientIfReady()) {
|
|
113
|
-
await dispatchEvent(
|
|
114
|
-
"notification:mail",
|
|
115
|
-
{
|
|
116
|
-
to,
|
|
117
|
-
subject,
|
|
118
|
-
html
|
|
119
|
-
},
|
|
120
|
-
{ queue: "mail" }
|
|
121
|
-
);
|
|
122
|
-
} else {
|
|
123
|
-
await mail.sendMail({ to, subject, html });
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/** Generate a basic HTML email body from title and body text. */
|
|
128
|
-
function defaultMailHtml(title: string, body: string | null) {
|
|
129
|
-
return `<h2>${escapeHtml(title)}</h2>${body ? `<p>${escapeHtml(body)}</p>` : ""}`;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function escapeHtml(str: string) {
|
|
133
|
-
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
134
|
-
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { NotificationChannel } from "@/framework/notification/contracts.js";
|
|
2
|
+
|
|
3
|
+
const channels = new Map<string, NotificationChannel>();
|
|
4
|
+
|
|
5
|
+
export function registerChannel(channel: NotificationChannel) {
|
|
6
|
+
channels.set(channel.name, channel);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function getChannels(): NotificationChannel[] {
|
|
10
|
+
return Array.from(channels.values());
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function clearChannels() {
|
|
14
|
+
channels.clear();
|
|
15
|
+
}
|
|
@@ -18,6 +18,7 @@ async function loadDefaultProvider(): Promise<AuthProvider | null> {
|
|
|
18
18
|
if (defaultProvider) return defaultProvider;
|
|
19
19
|
if (defaultLoadAttempted) return null;
|
|
20
20
|
defaultLoadAttempted = true;
|
|
21
|
+
|
|
21
22
|
try {
|
|
22
23
|
const userMod = await import("@/modules/auth/database/models/user.js").catch(() => null);
|
|
23
24
|
if (!userMod?.users || !userMod?.refreshTokens) return null;
|
|
@@ -25,22 +26,28 @@ async function loadDefaultProvider(): Promise<AuthProvider | null> {
|
|
|
25
26
|
const users = userMod.users;
|
|
26
27
|
const refreshTokens = userMod.refreshTokens;
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
29
|
+
// Define the core logic as standalone functions to avoid `this` context issues
|
|
30
|
+
const getUserById = async (id: number | string) => {
|
|
31
|
+
try {
|
|
32
|
+
const user = await (db.query.users.findFirst as any)?.({
|
|
33
|
+
where: eq(users.id, id as any),
|
|
34
|
+
with: { role: true }
|
|
35
|
+
});
|
|
36
|
+
if (user) return user;
|
|
37
|
+
} catch {}
|
|
38
|
+
try {
|
|
39
|
+
const rows = await db
|
|
40
|
+
.select()
|
|
41
|
+
.from(users)
|
|
42
|
+
.where(eq(users.id, id as any));
|
|
43
|
+
return rows?.[0] ?? null;
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const provider: AuthProvider = {
|
|
50
|
+
getUserById,
|
|
44
51
|
async findRefreshToken(jti: string) {
|
|
45
52
|
try {
|
|
46
53
|
const token = await (db.query.refreshTokens.findFirst as any)?.({
|
|
@@ -89,7 +96,8 @@ async function loadDefaultProvider(): Promise<AuthProvider | null> {
|
|
|
89
96
|
},
|
|
90
97
|
async getUserContact(userId: number | string) {
|
|
91
98
|
try {
|
|
92
|
-
|
|
99
|
+
// Use the standalone function instead of `this.getUserById`
|
|
100
|
+
const user = await getUserById(userId);
|
|
93
101
|
if (!user) return null;
|
|
94
102
|
return {
|
|
95
103
|
email: user.email ?? null,
|
|
@@ -101,6 +109,8 @@ async function loadDefaultProvider(): Promise<AuthProvider | null> {
|
|
|
101
109
|
}
|
|
102
110
|
}
|
|
103
111
|
};
|
|
112
|
+
|
|
113
|
+
defaultProvider = provider;
|
|
104
114
|
return defaultProvider;
|
|
105
115
|
} catch {
|
|
106
116
|
return null;
|