create-nexwire 4.0.0 → 4.1.1

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 (173) hide show
  1. package/README.md +5 -4
  2. package/express/README.md +13 -7
  3. package/express/package.json +2 -2
  4. package/express/src/config/session.ts +1 -1
  5. package/express/src/database/migrations/sqlite/meta/0000_snapshot.json +1 -1
  6. package/express/src/database/migrations/sqlite/meta/_journal.json +1 -1
  7. package/express/src/database/schema.ts +3 -3
  8. package/express/src/framework/cache/cache.ts +82 -81
  9. package/express/src/framework/database/seed.ts +1 -1
  10. package/express/src/framework/facade.ts +1 -24
  11. package/express/src/framework/http/app.ts +5 -5
  12. package/express/src/framework/http/openapi.ts +1 -1
  13. package/express/src/framework/http/router.ts +3 -18
  14. package/express/src/framework/http/types.ts +19 -0
  15. package/express/src/framework/http/validation.ts +18 -18
  16. package/express/src/framework/maker-cli/middleware/core.mjs +41 -47
  17. package/express/src/framework/maker-cli/module/core.mjs +920 -684
  18. package/express/src/framework/maker-cli/module/index.mjs +202 -140
  19. package/express/src/framework/maker-cli/runtime/core.mjs +1 -2
  20. package/express/src/framework/maker-cli/stubs/controller/openapi.ts.stub +40 -14
  21. package/express/src/framework/maker-cli/stubs/controller/openapi.with-model.ts.stub +40 -15
  22. package/express/src/framework/maker-cli/stubs/controller/plain.ts.stub +42 -16
  23. package/express/src/framework/maker-cli/stubs/controller/standalone.ts.stub +47 -0
  24. package/express/src/framework/maker-cli/stubs/facade/name.ts.stub +12 -0
  25. package/express/src/framework/maker-cli/stubs/helper/name.ts.stub +14 -0
  26. package/express/src/framework/maker-cli/stubs/middleware/name.local.ts.stub +15 -0
  27. package/express/src/framework/maker-cli/stubs/model/name.mysql.ts.stub +9 -8
  28. package/express/src/framework/maker-cli/stubs/model/name.postgresql.ts.stub +9 -8
  29. package/express/src/framework/maker-cli/stubs/model/name.sqlite.ts.stub +9 -8
  30. package/express/src/framework/maker-cli/stubs/notification/route.api.ts.stub +50 -50
  31. package/express/src/framework/maker-cli/stubs/notification/route.plain.ts.stub +11 -11
  32. package/express/src/framework/maker-cli/stubs/route/api.ts.stub +93 -79
  33. package/express/src/framework/maker-cli/stubs/route/plain.ts.stub +23 -10
  34. package/express/src/framework/maker-cli/stubs/route/standalone.api.ts.stub +113 -0
  35. package/express/src/framework/maker-cli/stubs/route/standalone.plain.ts.stub +37 -0
  36. package/express/src/framework/maker-cli/stubs/{controller/schema.plain.ts.stub → schema/name.plain.ts.stub} +13 -13
  37. package/express/src/framework/maker-cli/stubs/{controller/schema.ts.stub → schema/name.ts.stub} +32 -32
  38. package/express/src/framework/maker-cli/stubs/service/name.plain.ts.stub +67 -0
  39. package/express/src/framework/maker-cli/stubs/service/name.ts.stub +65 -0
  40. package/express/src/framework/maker-cli/stubs/type/name.ts.stub +22 -0
  41. package/express/src/framework/maker-cli/utils/naming.mjs +56 -13
  42. package/express/src/framework/modules/discover.ts +57 -54
  43. package/express/src/framework/notification/index.ts +1 -1
  44. package/express/src/framework/realtime/broadcast.ts +46 -46
  45. package/express/src/framework/server.ts +2 -1
  46. package/express/src/framework/session/session.ts +102 -98
  47. package/express/src/framework/support/mail.ts +48 -48
  48. package/express/src/framework/support/status-codes.ts +75 -0
  49. package/express/src/middlewares/auth-middleware.ts +1 -1
  50. package/express/src/modules/auth/__tests__/forgetpass.test.ts +200 -0
  51. package/express/src/modules/auth/__tests__/login.test.ts +171 -0
  52. package/express/src/modules/auth/__tests__/logout.test.ts +60 -0
  53. package/express/src/modules/auth/__tests__/logoutalldevices.test.ts +49 -0
  54. package/express/src/modules/auth/__tests__/me.test.ts +91 -0
  55. package/express/src/modules/auth/__tests__/refreshtoken.test.ts +305 -0
  56. package/express/src/modules/auth/__tests__/register.test.ts +291 -0
  57. package/express/src/modules/auth/__tests__/resetpass.test.ts +206 -0
  58. package/express/src/modules/auth/__tests__/verifyemail.test.ts +183 -0
  59. package/express/src/modules/auth/controllers/auth.ts +254 -0
  60. package/express/src/modules/auth/controllers/{role.controller.ts → role.ts} +25 -15
  61. package/{hono/src/modules/auth/database/models/notifications.ts → express/src/modules/auth/database/models/notification.ts} +1 -1
  62. package/express/src/modules/auth/database/models/role.ts +1 -1
  63. package/express/src/modules/auth/database/models/user.ts +1 -1
  64. package/express/src/modules/auth/facade.ts +12 -0
  65. package/express/src/modules/auth/{controllers/auth.helpers.ts → helpers/auth.ts} +3 -4
  66. package/express/src/modules/auth/jobs/registeruser.ts +23 -23
  67. package/express/src/modules/auth/routes/{api.ts → index.ts} +150 -150
  68. package/express/src/modules/auth/routes/role.ts +33 -33
  69. package/{hono/src/modules/auth/controllers/auth.schema.ts → express/src/modules/auth/schemas/auth.ts} +102 -102
  70. package/express/src/modules/auth/services/forgetpassword.ts +66 -0
  71. package/express/src/modules/auth/services/login.ts +63 -0
  72. package/express/src/modules/auth/services/logout.ts +25 -0
  73. package/express/src/modules/auth/services/logoutalldevices.ts +19 -0
  74. package/express/src/modules/auth/services/me.ts +31 -0
  75. package/express/src/modules/auth/services/refreshtoken.ts +110 -0
  76. package/express/src/modules/auth/services/register.ts +141 -0
  77. package/express/src/modules/auth/services/resetpassword.ts +69 -0
  78. package/express/src/modules/auth/services/verifyemail.ts +77 -0
  79. package/express/src/modules/auth/types/auth.ts +31 -0
  80. package/express/src/resources/src/assets/images/logo.png +0 -0
  81. package/express/src/resources/src/pages/auth/forgetPassword.vue +1 -0
  82. package/express/src/resources/src/pages/auth/login.vue +1 -1
  83. package/express/src/storage/database/nexwire.sqlite +0 -0
  84. package/hono/README.md +13 -7
  85. package/hono/package.json +1 -1
  86. package/hono/src/config/session.ts +1 -1
  87. package/hono/src/database/migrations/sqlite/meta/0000_snapshot.json +1 -1
  88. package/hono/src/database/migrations/sqlite/meta/_journal.json +1 -1
  89. package/hono/src/database/schema.ts +3 -3
  90. package/hono/src/framework/cache/cache.ts +82 -81
  91. package/hono/src/framework/database/seed.ts +1 -1
  92. package/hono/src/framework/http/app.ts +5 -5
  93. package/hono/src/framework/http/validation.ts +21 -21
  94. package/hono/src/framework/maker-cli/middleware/core.mjs +40 -46
  95. package/hono/src/framework/maker-cli/module/core.mjs +401 -168
  96. package/hono/src/framework/maker-cli/module/index.mjs +73 -11
  97. package/hono/src/framework/maker-cli/stubs/controller/openapi.ts.stub +28 -16
  98. package/hono/src/framework/maker-cli/stubs/controller/openapi.with-model.ts.stub +28 -17
  99. package/hono/src/framework/maker-cli/stubs/controller/plain.ts.stub +28 -16
  100. package/hono/src/framework/maker-cli/stubs/controller/standalone.ts.stub +56 -0
  101. package/hono/src/framework/maker-cli/stubs/facade/name.ts.stub +12 -0
  102. package/hono/src/framework/maker-cli/stubs/helper/name.ts.stub +14 -0
  103. package/hono/src/framework/maker-cli/stubs/middleware/name.local.ts.stub +15 -0
  104. package/hono/src/framework/maker-cli/stubs/model/name.mysql.ts.stub +9 -8
  105. package/hono/src/framework/maker-cli/stubs/model/name.postgresql.ts.stub +9 -8
  106. package/hono/src/framework/maker-cli/stubs/model/name.sqlite.ts.stub +9 -8
  107. package/hono/src/framework/maker-cli/stubs/notification/route.api.ts.stub +49 -49
  108. package/hono/src/framework/maker-cli/stubs/notification/route.plain.ts.stub +11 -11
  109. package/hono/src/framework/maker-cli/stubs/route/api.ts.stub +93 -79
  110. package/hono/src/framework/maker-cli/stubs/route/plain.ts.stub +23 -10
  111. package/hono/src/framework/maker-cli/stubs/route/standalone.api.ts.stub +112 -0
  112. package/hono/src/framework/maker-cli/stubs/route/standalone.plain.ts.stub +37 -0
  113. package/hono/src/framework/maker-cli/stubs/{controller/schema.plain.ts.stub → schema/name.plain.ts.stub} +13 -13
  114. package/hono/src/framework/maker-cli/stubs/{controller/schema.ts.stub → schema/name.ts.stub} +32 -32
  115. package/hono/src/framework/maker-cli/stubs/service/name.plain.ts.stub +67 -0
  116. package/hono/src/framework/maker-cli/stubs/service/name.ts.stub +65 -0
  117. package/hono/src/framework/maker-cli/stubs/type/name.ts.stub +22 -0
  118. package/hono/src/framework/maker-cli/utils/naming.mjs +56 -13
  119. package/hono/src/framework/modules/discover.ts +57 -54
  120. package/hono/src/framework/notification/index.ts +1 -1
  121. package/hono/src/framework/realtime/broadcast.ts +46 -46
  122. package/hono/src/framework/server.ts +1 -1
  123. package/hono/src/framework/session/session.ts +121 -116
  124. package/hono/src/middlewares/auth-middleware.ts +1 -1
  125. package/hono/src/modules/auth/__tests__/forgetpass.test.ts +170 -0
  126. package/hono/src/modules/auth/__tests__/login.test.ts +154 -0
  127. package/hono/src/modules/auth/__tests__/logout.test.ts +53 -0
  128. package/hono/src/modules/auth/__tests__/logoutalldevices.test.ts +34 -0
  129. package/hono/src/modules/auth/__tests__/me.test.ts +87 -0
  130. package/hono/src/modules/auth/__tests__/refreshtoken.test.ts +305 -0
  131. package/hono/src/modules/auth/__tests__/register.test.ts +258 -0
  132. package/hono/src/modules/auth/__tests__/resetpass.test.ts +214 -0
  133. package/hono/src/modules/auth/__tests__/verifyemail.test.ts +189 -0
  134. package/hono/src/modules/auth/controllers/auth.ts +238 -0
  135. package/hono/src/modules/auth/controllers/{role.controller.ts → role.ts} +25 -25
  136. package/{express/src/modules/auth/database/models/notifications.ts → hono/src/modules/auth/database/models/notification.ts} +1 -1
  137. package/hono/src/modules/auth/database/models/role.ts +1 -1
  138. package/hono/src/modules/auth/database/models/user.ts +1 -1
  139. package/hono/src/modules/auth/facade.ts +12 -0
  140. package/hono/src/modules/auth/{controllers/auth.helpers.ts → helpers/auth.ts} +5 -6
  141. package/hono/src/modules/auth/jobs/registeruser.ts +23 -23
  142. package/hono/src/modules/auth/routes/{api.ts → index.ts} +150 -150
  143. package/hono/src/modules/auth/routes/role.ts +33 -33
  144. package/{express/src/modules/auth/controllers/auth.schema.ts → hono/src/modules/auth/schemas/auth.ts} +102 -102
  145. package/hono/src/modules/auth/services/forgetpassword.ts +58 -0
  146. package/hono/src/modules/auth/services/login.ts +61 -0
  147. package/hono/src/modules/auth/services/logout.ts +24 -0
  148. package/hono/src/modules/auth/services/logoutalldevices.ts +19 -0
  149. package/hono/src/modules/auth/services/me.ts +31 -0
  150. package/hono/src/modules/auth/services/refreshtoken.ts +110 -0
  151. package/hono/src/modules/auth/services/register.ts +132 -0
  152. package/hono/src/modules/auth/services/resetpassword.ts +69 -0
  153. package/hono/src/modules/auth/services/verifyemail.ts +77 -0
  154. package/hono/src/modules/auth/types/auth.ts +31 -0
  155. package/hono/src/resources/src/assets/images/logo.png +0 -0
  156. package/hono/src/storage/database/nexwire.sqlite +0 -0
  157. package/package.json +1 -1
  158. package/express/src/framework/maker-cli/stubs/example/console.ts.stub +0 -34
  159. package/express/src/framework/maker-cli/stubs/example/controller.ts.stub +0 -418
  160. package/express/src/framework/maker-cli/stubs/example/job.ts.stub +0 -75
  161. package/express/src/framework/maker-cli/stubs/example/route.api.ts.stub +0 -207
  162. package/express/src/framework/maker-cli/stubs/example/route.plain.ts.stub +0 -18
  163. package/express/src/framework/maker-cli/stubs/example/schema.plain.ts.stub +0 -42
  164. package/express/src/framework/maker-cli/stubs/example/schema.ts.stub +0 -42
  165. package/express/src/modules/auth/controllers/auth.controller.ts +0 -358
  166. package/hono/src/framework/maker-cli/stubs/example/console.ts.stub +0 -34
  167. package/hono/src/framework/maker-cli/stubs/example/controller.ts.stub +0 -505
  168. package/hono/src/framework/maker-cli/stubs/example/job.ts.stub +0 -75
  169. package/hono/src/framework/maker-cli/stubs/example/route.api.ts.stub +0 -207
  170. package/hono/src/framework/maker-cli/stubs/example/route.plain.ts.stub +0 -18
  171. package/hono/src/framework/maker-cli/stubs/example/schema.plain.ts.stub +0 -42
  172. package/hono/src/framework/maker-cli/stubs/example/schema.ts.stub +0 -42
  173. package/hono/src/modules/auth/controllers/auth.controller.ts +0 -413
@@ -1,46 +1,46 @@
1
- import { env } from "@/env.js";
2
- import { socketServer } from "@/framework/realtime/index.js";
3
- import { redisClientIfReady } from "@/framework/redis/client.js";
4
-
5
- export type BroadcastOptions = {
6
- users?: Array<string | number>;
7
- roles?: string[];
8
- rooms?: string[];
9
- auth?: boolean;
10
- all?: boolean;
11
- };
12
-
13
- /**
14
- * Why: Emits realtime events to targeted audiences.
15
- * When: Domain events need websocket fan-out.
16
- * Where: Event dispatcher and module jobs.
17
- * How: Emits via Socket.IO if available; otherwise relays through Redis pub/sub
18
- * so the main process (which has Socket.IO) can broadcast it.
19
- */
20
- function emitViaIo(event: string, payload: any, options: BroadcastOptions) {
21
- const io = socketServer();
22
- if (!io) return;
23
-
24
- if (options.all) io.emit(event, payload);
25
- if (options.auth) io.to("auth").emit(event, payload);
26
-
27
- for (const user of options.users || []) io.to(`user:${user}`).emit(event, payload);
28
- for (const role of options.roles || []) io.to(`role:${role}`).emit(event, payload);
29
- for (const room of options.rooms || []) io.to(room).emit(event, payload);
30
- }
31
-
32
- export function broadcast(event: string, payload: any, options: BroadcastOptions = {}) {
33
- const io = socketServer();
34
-
35
- if (io) {
36
- emitViaIo(event, payload, options);
37
- return;
38
- }
39
-
40
- // No Socket.IO in this process — relay through Redis so the main process can broadcast
41
- const redis = redisClientIfReady();
42
- if (!redis) return;
43
-
44
- const channel = `${env.REDIS_PREFIX}:broadcast`;
45
- redis.publish(channel, JSON.stringify({ event, payload, options }));
46
- }
1
+ import { env } from "@/env.js";
2
+ import { socketServer } from "@/framework/realtime/socket.js";
3
+ import { redisClientIfReady } from "@/framework/redis/client.js";
4
+
5
+ export type BroadcastOptions = {
6
+ users?: Array<string | number>;
7
+ roles?: string[];
8
+ rooms?: string[];
9
+ auth?: boolean;
10
+ all?: boolean;
11
+ };
12
+
13
+ /**
14
+ * Why: Emits realtime events to targeted audiences.
15
+ * When: Domain events need websocket fan-out.
16
+ * Where: Event dispatcher and module jobs.
17
+ * How: Emits via Socket.IO if available; otherwise relays through Redis pub/sub
18
+ * so the main process (which has Socket.IO) can broadcast it.
19
+ */
20
+ function emitViaIo(event: string, payload: any, options: BroadcastOptions) {
21
+ const io = socketServer();
22
+ if (!io) return;
23
+
24
+ if (options.all) io.emit(event, payload);
25
+ if (options.auth) io.to("auth").emit(event, payload);
26
+
27
+ for (const user of options.users || []) io.to(`user:${user}`).emit(event, payload);
28
+ for (const role of options.roles || []) io.to(`role:${role}`).emit(event, payload);
29
+ for (const room of options.rooms || []) io.to(room).emit(event, payload);
30
+ }
31
+
32
+ export function broadcast(event: string, payload: any, options: BroadcastOptions = {}) {
33
+ const io = socketServer();
34
+
35
+ if (io) {
36
+ emitViaIo(event, payload, options);
37
+ return;
38
+ }
39
+
40
+ // No Socket.IO in this process — relay through Redis so the main process can broadcast
41
+ const redis = redisClientIfReady();
42
+ if (!redis) return;
43
+
44
+ const channel = `${env.REDIS_PREFIX}:broadcast`;
45
+ redis.publish(channel, JSON.stringify({ event, payload, options }));
46
+ }
@@ -139,10 +139,11 @@ export async function startServer(): Promise<ServerHandle> {
139
139
  console.log("Drizzle Studio requested: https://local.drizzle.studio (see dev process status)");
140
140
  }
141
141
 
142
+ const wsUrl = serverUrl(server, WS_PATH).replace(/^http/, "ws");
142
143
  const socketLine = !realtimeConfig.enabled
143
144
  ? "Realtime (Socket.IO) disabled"
144
145
  : realtime
145
- ? `Realtime (Socket.IO) enabled: ${socketAdmin.enabled ? "Admin UI: https://admin.socket.io" : ""} ${" | " + serverUrl(server, WS_PATH).replace(/^http/, "ws")}`
146
+ ? `Realtime (Socket.IO) enabled: ${socketAdmin.enabled ? `Admin UI: https://admin.socket.io | ${wsUrl}` : `| ${wsUrl}`}`
146
147
  : "Realtime (Socket.IO) unavailable";
147
148
  console.log(!realtimeConfig.enabled ? chalk.gray(socketLine) : realtime ? chalk.green(socketLine) : chalk.yellow(socketLine));
148
149
 
@@ -1,98 +1,102 @@
1
- import { randomUUID } from "node:crypto";
2
- import type { NextFunction, Request, Response } from "express";
3
- import { appConfig, redisConfig, sessionConfig } from "@/config/index.js";
4
- import { redisClientIfReady } from "@/framework/redis/client.js";
5
-
6
- function originOf(url?: string) {
7
- if (!url) return "";
8
- try {
9
- return new URL(url).origin;
10
- } catch {
11
- return "";
12
- }
13
- }
14
-
15
- function needsCrossSiteCookie() {
16
- const appOrigin = originOf(appConfig.url);
17
- const frontendOrigin = originOf(appConfig.frontendUrl);
18
- return Boolean(appOrigin && frontendOrigin && appOrigin !== frontendOrigin);
19
- }
20
-
21
- function sessionKey(id: string) {
22
- return `${sessionConfig.keyPrefix}:${id}`;
23
- }
24
-
25
- export async function sessionMiddleware(req: Request, res: Response, next: NextFunction) {
26
- let sessionId = req.signedCookies?.[sessionConfig.cookieName];
27
-
28
- if (!sessionId) {
29
- sessionId = randomUUID();
30
- const crossSiteCookie = needsCrossSiteCookie();
31
- res.cookie(sessionConfig.cookieName, sessionId, {
32
- httpOnly: true,
33
- sameSite: crossSiteCookie ? "none" : "lax",
34
- secure: crossSiteCookie,
35
- path: "/",
36
- maxAge: sessionConfig.ttlSeconds * 1000,
37
- signed: true
38
- });
39
- }
40
-
41
- res.locals.sessionId = sessionId;
42
- await session.refresh(sessionId);
43
- return next();
44
- }
45
-
46
- export const session = {
47
- async start(data: Record<string, unknown> = {}) {
48
- const client = redisClientIfReady();
49
- if (!client) return "";
50
-
51
- const id = randomUUID();
52
- await client.set(sessionKey(id), JSON.stringify(data), "EX", sessionConfig.ttlSeconds);
53
- return id;
54
- },
55
-
56
- async all<T = Record<string, unknown>>(id: string): Promise<T | null> {
57
- const client = redisClientIfReady();
58
- if (!client) return null;
59
-
60
- const data = await client.get(sessionKey(id));
61
- return data ? JSON.parse(data) : null;
62
- },
63
-
64
- async get<T>(id: string, key: string): Promise<T | null> {
65
- const data = await session.all<Record<string, T>>(id);
66
- return data?.[key] ?? null;
67
- },
68
-
69
- async put(id: string, key: string, value: unknown) {
70
- const client = redisClientIfReady();
71
- if (!client) return false;
72
-
73
- const data = (await session.all<Record<string, unknown>>(id)) || {};
74
- data[key] = value;
75
- await client.set(sessionKey(id), JSON.stringify(data), "EX", sessionConfig.ttlSeconds);
76
- return true;
77
- },
78
-
79
- async refresh(id: string) {
80
- const client = redisClientIfReady();
81
- if (!client) return false;
82
-
83
- await client.expire(sessionKey(id), sessionConfig.ttlSeconds);
84
- return true;
85
- },
86
-
87
- async destroy(id: string) {
88
- const client = redisClientIfReady();
89
- if (!client) return false;
90
-
91
- await client.del(sessionKey(id));
92
- return true;
93
- },
94
-
95
- isAvailable() {
96
- return redisConfig.enabled && redisClientIfReady() !== null;
97
- }
98
- };
1
+ import { randomUUID } from "node:crypto";
2
+ import type { NextFunction, Request, Response } from "express";
3
+ import { appConfig, redisConfig, sessionConfig } from "@/config/index.js";
4
+ import { redisClientIfReady } from "@/framework/redis/client.js";
5
+
6
+ function originOf(url?: string) {
7
+ if (!url) return "";
8
+ try {
9
+ return new URL(url).origin;
10
+ } catch {
11
+ return "";
12
+ }
13
+ }
14
+
15
+ function needsCrossSiteCookie() {
16
+ const appOrigin = originOf(appConfig.url);
17
+ const frontendOrigin = originOf(appConfig.frontendUrl);
18
+ return Boolean(appOrigin && frontendOrigin && appOrigin !== frontendOrigin);
19
+ }
20
+
21
+ function sessionKey(id: string) {
22
+ return `${sessionConfig.keyPrefix}:${id}`;
23
+ }
24
+
25
+ export async function sessionMiddleware(req: Request, res: Response, next: NextFunction) {
26
+ let sessionId = req.cookies?.[sessionConfig.cookieName];
27
+
28
+ if (!sessionId) {
29
+ sessionId = randomUUID();
30
+ const crossSiteCookie = needsCrossSiteCookie();
31
+ res.cookie(sessionConfig.cookieName, sessionId, {
32
+ httpOnly: true,
33
+ sameSite: crossSiteCookie ? "none" : "lax",
34
+ secure: crossSiteCookie,
35
+ path: "/",
36
+ maxAge: sessionConfig.ttlSeconds * 1000
37
+ });
38
+ }
39
+
40
+ res.locals.sessionId = sessionId;
41
+ await session.refresh(sessionId);
42
+ next();
43
+ }
44
+
45
+ export const session = {
46
+ async start(data: Record<string, unknown> = {}) {
47
+ const client = redisClientIfReady();
48
+ if (!client) return "";
49
+
50
+ const id = randomUUID();
51
+ await client.set(sessionKey(id), JSON.stringify(data), "EX", sessionConfig.ttlSeconds);
52
+ return id;
53
+ },
54
+
55
+ async all<T = Record<string, unknown>>(id: string): Promise<T | null> {
56
+ const client = redisClientIfReady();
57
+ if (!client) return null;
58
+
59
+ const data = await client.get(sessionKey(id));
60
+ return data ? JSON.parse(data) : null;
61
+ },
62
+
63
+ async get<T>(id: string, key: string): Promise<T | null> {
64
+ const data = await session.all<Record<string, T>>(id);
65
+ return data?.[key] ?? null;
66
+ },
67
+
68
+ async set(id: string, key: string, value: unknown) {
69
+ const client = redisClientIfReady();
70
+ if (!client) return false;
71
+
72
+ const data = (await session.all<Record<string, unknown>>(id)) || {};
73
+ data[key] = value;
74
+ await client.set(sessionKey(id), JSON.stringify(data), "EX", sessionConfig.ttlSeconds);
75
+ return true;
76
+ },
77
+
78
+ /** @deprecated Use set() instead. */
79
+ async put(id: string, key: string, value: unknown) {
80
+ return this.set(id, key, value);
81
+ },
82
+
83
+ async refresh(id: string) {
84
+ const client = redisClientIfReady();
85
+ if (!client) return false;
86
+
87
+ await client.expire(sessionKey(id), sessionConfig.ttlSeconds);
88
+ return true;
89
+ },
90
+
91
+ async destroy(id: string) {
92
+ const client = redisClientIfReady();
93
+ if (!client) return false;
94
+
95
+ await client.del(sessionKey(id));
96
+ return true;
97
+ },
98
+
99
+ isAvailable() {
100
+ return redisConfig.enabled && redisClientIfReady() !== null;
101
+ }
102
+ };
@@ -1,48 +1,48 @@
1
- import nodemailer from "nodemailer";
2
- import { mailConfig } from "@/config/index.js";
3
- import { logger } from "@/framework/support/logger.js";
4
- import { CircuitBreaker } from "@/framework/circuit-breaker/cb.js";
5
-
6
- type MailPayload = {
7
- to: string;
8
- subject: string;
9
- html?: string;
10
- text?: string;
11
- };
12
-
13
- const transport = nodemailer.createTransport({
14
- host: mailConfig.host,
15
- port: mailConfig.port,
16
- secure: mailConfig.encryption === "ssl",
17
- auth: mailConfig.username ? { user: mailConfig.username, pass: mailConfig.password } : undefined
18
- });
19
-
20
- const mailBreaker = new CircuitBreaker();
21
-
22
- export const mail = {
23
- /**
24
- * Why: Sends transactional email through configured SMTP transport.
25
- * When: Features need notifications/password reset/signup email.
26
- * Where: Jobs and event handlers.
27
- * How: Uses nodemailer transport and respects the fail-silent setting.
28
- */
29
- async sendMail(payload: MailPayload) {
30
- try {
31
- return await mailBreaker.exec(() =>
32
- transport.sendMail({
33
- from: mailConfig.fromAddress,
34
- ...payload
35
- })
36
- );
37
- } catch (error) {
38
- logger.error("Mail send failed", {
39
- to: payload.to,
40
- subject: payload.subject,
41
- error: error instanceof Error ? error.message : error
42
- });
43
-
44
- if (!mailConfig.failSilent) throw error;
45
- return null;
46
- }
47
- }
48
- };
1
+ import nodemailer from "nodemailer";
2
+ import { mailConfig } from "@/config/index.js";
3
+ import { CircuitBreaker } from "@/framework/circuit-breaker/cb.js";
4
+ import { logger } from "@/framework/support/logger.js";
5
+
6
+ type MailPayload = {
7
+ to: string;
8
+ subject: string;
9
+ html?: string;
10
+ text?: string;
11
+ };
12
+
13
+ const transport = nodemailer.createTransport({
14
+ host: mailConfig.host,
15
+ port: mailConfig.port,
16
+ secure: mailConfig.encryption === "ssl",
17
+ auth: mailConfig.username ? { user: mailConfig.username, pass: mailConfig.password } : undefined
18
+ });
19
+
20
+ const mailBreaker = new CircuitBreaker();
21
+
22
+ export const mail = {
23
+ /**
24
+ * Why: Sends transactional email through configured SMTP transport.
25
+ * When: Features need notifications/password reset/signup email.
26
+ * Where: Jobs and event handlers.
27
+ * How: Uses nodemailer transport and respects the fail-silent setting.
28
+ */
29
+ async sendMail(payload: MailPayload) {
30
+ try {
31
+ return await mailBreaker.exec(() =>
32
+ transport.sendMail({
33
+ from: mailConfig.fromAddress,
34
+ ...payload
35
+ })
36
+ );
37
+ } catch (error) {
38
+ logger.error("Mail send failed", {
39
+ to: payload.to,
40
+ subject: payload.subject,
41
+ error: error instanceof Error ? error.message : error
42
+ });
43
+
44
+ if (!mailConfig.failSilent) throw error;
45
+ return null;
46
+ }
47
+ }
48
+ };
@@ -0,0 +1,75 @@
1
+ // 1xx Informational
2
+ export const CONTINUE = 100;
3
+ export const SWITCHING_PROTOCOLS = 101;
4
+ export const PROCESSING = 102;
5
+ export const EARLY_HINTS = 103;
6
+
7
+ // 2xx Success
8
+ export const OK = 200;
9
+ export const CREATED = 201;
10
+ export const ACCEPTED = 202;
11
+ export const NON_AUTHORITATIVE_INFORMATION = 203;
12
+ export const NO_CONTENT = 204;
13
+ export const RESET_CONTENT = 205;
14
+ export const PARTIAL_CONTENT = 206;
15
+ export const MULTI_STATUS = 207;
16
+ export const ALREADY_REPORTED = 208;
17
+ export const IM_USED = 226;
18
+
19
+ // 3xx Redirection
20
+ export const MULTIPLE_CHOICES = 300;
21
+ export const MOVED_PERMANENTLY = 301;
22
+ export const FOUND = 302;
23
+ export const MOVED_TEMPORARILY = 302;
24
+ export const SEE_OTHER = 303;
25
+ export const NOT_MODIFIED = 304;
26
+ export const USE_PROXY = 305;
27
+ export const TEMPORARY_REDIRECT = 307;
28
+ export const PERMANENT_REDIRECT = 308;
29
+
30
+ // 4xx Client Error
31
+ export const BAD_REQUEST = 400;
32
+ export const UNAUTHORIZED = 401;
33
+ export const PAYMENT_REQUIRED = 402;
34
+ export const FORBIDDEN = 403;
35
+ export const NOT_FOUND = 404;
36
+ export const METHOD_NOT_ALLOWED = 405;
37
+ export const NOT_ACCEPTABLE = 406;
38
+ export const PROXY_AUTHENTICATION_REQUIRED = 407;
39
+ export const REQUEST_TIMEOUT = 408;
40
+ export const CONFLICT = 409;
41
+ export const GONE = 410;
42
+ export const LENGTH_REQUIRED = 411;
43
+ export const PRECONDITION_FAILED = 412;
44
+ export const PAYLOAD_TOO_LARGE = 413;
45
+ export const REQUEST_TOO_LONG = 413;
46
+ export const URI_TOO_LONG = 414;
47
+ export const REQUEST_URI_TOO_LONG = 414;
48
+ export const UNSUPPORTED_MEDIA_TYPE = 415;
49
+ export const RANGE_NOT_SATISFIABLE = 416;
50
+ export const REQUESTED_RANGE_NOT_SATISFIABLE = 416;
51
+ export const EXPECTATION_FAILED = 417;
52
+ export const IM_A_TEAPOT = 418;
53
+ export const MISDIRECTED_REQUEST = 421;
54
+ export const UNPROCESSABLE_ENTITY = 422;
55
+ export const LOCKED = 423;
56
+ export const FAILED_DEPENDENCY = 424;
57
+ export const TOO_EARLY = 425;
58
+ export const UPGRADE_REQUIRED = 426;
59
+ export const PRECONDITION_REQUIRED = 428;
60
+ export const TOO_MANY_REQUESTS = 429;
61
+ export const REQUEST_HEADER_FIELDS_TOO_LARGE = 431;
62
+ export const UNAVAILABLE_FOR_LEGAL_REASONS = 451;
63
+
64
+ // 5xx Server Error
65
+ export const INTERNAL_SERVER_ERROR = 500;
66
+ export const NOT_IMPLEMENTED = 501;
67
+ export const BAD_GATEWAY = 502;
68
+ export const SERVICE_UNAVAILABLE = 503;
69
+ export const GATEWAY_TIMEOUT = 504;
70
+ export const HTTP_VERSION_NOT_SUPPORTED = 505;
71
+ export const VARIANT_ALSO_NEGOTIATES = 506;
72
+ export const INSUFFICIENT_STORAGE = 507;
73
+ export const LOOP_DETECTED = 508;
74
+ export const NOT_EXTENDED = 510;
75
+ export const NETWORK_AUTHENTICATION_REQUIRED = 511;
@@ -1,6 +1,6 @@
1
1
  import { eq } from "drizzle-orm";
2
2
  import { type NextFunction, type Request, type Response } from "express";
3
- import { cookie, db, jwt, HttpStatusCodes } from "@/framework/facade.js";
3
+ import { cookie, db, HttpStatusCodes, jwt } from "@/framework/facade.js";
4
4
  import { refreshTokens, users } from "@/modules/auth/database/models/user.js";
5
5
 
6
6
  export async function authMiddleware(req: Request, res: Response, next: NextFunction) {