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.
- package/README.md +5 -4
- package/express/README.md +13 -7
- package/express/package.json +2 -2
- package/express/src/config/session.ts +1 -1
- 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/database/schema.ts +3 -3
- package/express/src/framework/cache/cache.ts +82 -81
- package/express/src/framework/database/seed.ts +1 -1
- package/express/src/framework/facade.ts +1 -24
- package/express/src/framework/http/app.ts +5 -5
- package/express/src/framework/http/openapi.ts +1 -1
- package/express/src/framework/http/router.ts +3 -18
- package/express/src/framework/http/types.ts +19 -0
- package/express/src/framework/http/validation.ts +18 -18
- package/express/src/framework/maker-cli/middleware/core.mjs +41 -47
- package/express/src/framework/maker-cli/module/core.mjs +920 -684
- package/express/src/framework/maker-cli/module/index.mjs +202 -140
- package/express/src/framework/maker-cli/runtime/core.mjs +1 -2
- package/express/src/framework/maker-cli/stubs/controller/openapi.ts.stub +40 -14
- package/express/src/framework/maker-cli/stubs/controller/openapi.with-model.ts.stub +40 -15
- package/express/src/framework/maker-cli/stubs/controller/plain.ts.stub +42 -16
- package/express/src/framework/maker-cli/stubs/controller/standalone.ts.stub +47 -0
- package/express/src/framework/maker-cli/stubs/facade/name.ts.stub +12 -0
- package/express/src/framework/maker-cli/stubs/helper/name.ts.stub +14 -0
- package/express/src/framework/maker-cli/stubs/middleware/name.local.ts.stub +15 -0
- package/express/src/framework/maker-cli/stubs/model/name.mysql.ts.stub +9 -8
- package/express/src/framework/maker-cli/stubs/model/name.postgresql.ts.stub +9 -8
- package/express/src/framework/maker-cli/stubs/model/name.sqlite.ts.stub +9 -8
- package/express/src/framework/maker-cli/stubs/notification/route.api.ts.stub +50 -50
- package/express/src/framework/maker-cli/stubs/notification/route.plain.ts.stub +11 -11
- package/express/src/framework/maker-cli/stubs/route/api.ts.stub +93 -79
- package/express/src/framework/maker-cli/stubs/route/plain.ts.stub +23 -10
- package/express/src/framework/maker-cli/stubs/route/standalone.api.ts.stub +113 -0
- package/express/src/framework/maker-cli/stubs/route/standalone.plain.ts.stub +37 -0
- package/express/src/framework/maker-cli/stubs/{controller/schema.plain.ts.stub → schema/name.plain.ts.stub} +13 -13
- package/express/src/framework/maker-cli/stubs/{controller/schema.ts.stub → schema/name.ts.stub} +32 -32
- package/express/src/framework/maker-cli/stubs/service/name.plain.ts.stub +67 -0
- package/express/src/framework/maker-cli/stubs/service/name.ts.stub +65 -0
- package/express/src/framework/maker-cli/stubs/type/name.ts.stub +22 -0
- package/express/src/framework/maker-cli/utils/naming.mjs +56 -13
- package/express/src/framework/modules/discover.ts +57 -54
- package/express/src/framework/notification/index.ts +1 -1
- package/express/src/framework/realtime/broadcast.ts +46 -46
- package/express/src/framework/server.ts +2 -1
- package/express/src/framework/session/session.ts +102 -98
- package/express/src/framework/support/mail.ts +48 -48
- package/express/src/framework/support/status-codes.ts +75 -0
- package/express/src/middlewares/auth-middleware.ts +1 -1
- package/express/src/modules/auth/__tests__/forgetpass.test.ts +200 -0
- package/express/src/modules/auth/__tests__/login.test.ts +171 -0
- package/express/src/modules/auth/__tests__/logout.test.ts +60 -0
- package/express/src/modules/auth/__tests__/logoutalldevices.test.ts +49 -0
- package/express/src/modules/auth/__tests__/me.test.ts +91 -0
- package/express/src/modules/auth/__tests__/refreshtoken.test.ts +305 -0
- package/express/src/modules/auth/__tests__/register.test.ts +291 -0
- package/express/src/modules/auth/__tests__/resetpass.test.ts +206 -0
- package/express/src/modules/auth/__tests__/verifyemail.test.ts +183 -0
- package/express/src/modules/auth/controllers/auth.ts +254 -0
- package/express/src/modules/auth/controllers/{role.controller.ts → role.ts} +25 -15
- package/{hono/src/modules/auth/database/models/notifications.ts → express/src/modules/auth/database/models/notification.ts} +1 -1
- package/express/src/modules/auth/database/models/role.ts +1 -1
- package/express/src/modules/auth/database/models/user.ts +1 -1
- package/express/src/modules/auth/facade.ts +12 -0
- package/express/src/modules/auth/{controllers/auth.helpers.ts → helpers/auth.ts} +3 -4
- package/express/src/modules/auth/jobs/registeruser.ts +23 -23
- package/express/src/modules/auth/routes/{api.ts → index.ts} +150 -150
- package/express/src/modules/auth/routes/role.ts +33 -33
- package/{hono/src/modules/auth/controllers/auth.schema.ts → express/src/modules/auth/schemas/auth.ts} +102 -102
- package/express/src/modules/auth/services/forgetpassword.ts +66 -0
- package/express/src/modules/auth/services/login.ts +63 -0
- package/express/src/modules/auth/services/logout.ts +25 -0
- package/express/src/modules/auth/services/logoutalldevices.ts +19 -0
- package/express/src/modules/auth/services/me.ts +31 -0
- package/express/src/modules/auth/services/refreshtoken.ts +110 -0
- package/express/src/modules/auth/services/register.ts +141 -0
- package/express/src/modules/auth/services/resetpassword.ts +69 -0
- package/express/src/modules/auth/services/verifyemail.ts +77 -0
- package/express/src/modules/auth/types/auth.ts +31 -0
- package/express/src/resources/src/assets/images/logo.png +0 -0
- package/express/src/resources/src/pages/auth/forgetPassword.vue +1 -0
- package/express/src/resources/src/pages/auth/login.vue +1 -1
- package/express/src/storage/database/nexwire.sqlite +0 -0
- package/hono/README.md +13 -7
- package/hono/package.json +1 -1
- package/hono/src/config/session.ts +1 -1
- 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/database/schema.ts +3 -3
- package/hono/src/framework/cache/cache.ts +82 -81
- package/hono/src/framework/database/seed.ts +1 -1
- package/hono/src/framework/http/app.ts +5 -5
- package/hono/src/framework/http/validation.ts +21 -21
- package/hono/src/framework/maker-cli/middleware/core.mjs +40 -46
- package/hono/src/framework/maker-cli/module/core.mjs +401 -168
- package/hono/src/framework/maker-cli/module/index.mjs +73 -11
- package/hono/src/framework/maker-cli/stubs/controller/openapi.ts.stub +28 -16
- package/hono/src/framework/maker-cli/stubs/controller/openapi.with-model.ts.stub +28 -17
- package/hono/src/framework/maker-cli/stubs/controller/plain.ts.stub +28 -16
- package/hono/src/framework/maker-cli/stubs/controller/standalone.ts.stub +56 -0
- package/hono/src/framework/maker-cli/stubs/facade/name.ts.stub +12 -0
- package/hono/src/framework/maker-cli/stubs/helper/name.ts.stub +14 -0
- package/hono/src/framework/maker-cli/stubs/middleware/name.local.ts.stub +15 -0
- package/hono/src/framework/maker-cli/stubs/model/name.mysql.ts.stub +9 -8
- package/hono/src/framework/maker-cli/stubs/model/name.postgresql.ts.stub +9 -8
- package/hono/src/framework/maker-cli/stubs/model/name.sqlite.ts.stub +9 -8
- package/hono/src/framework/maker-cli/stubs/notification/route.api.ts.stub +49 -49
- package/hono/src/framework/maker-cli/stubs/notification/route.plain.ts.stub +11 -11
- package/hono/src/framework/maker-cli/stubs/route/api.ts.stub +93 -79
- package/hono/src/framework/maker-cli/stubs/route/plain.ts.stub +23 -10
- package/hono/src/framework/maker-cli/stubs/route/standalone.api.ts.stub +112 -0
- package/hono/src/framework/maker-cli/stubs/route/standalone.plain.ts.stub +37 -0
- package/hono/src/framework/maker-cli/stubs/{controller/schema.plain.ts.stub → schema/name.plain.ts.stub} +13 -13
- package/hono/src/framework/maker-cli/stubs/{controller/schema.ts.stub → schema/name.ts.stub} +32 -32
- package/hono/src/framework/maker-cli/stubs/service/name.plain.ts.stub +67 -0
- package/hono/src/framework/maker-cli/stubs/service/name.ts.stub +65 -0
- package/hono/src/framework/maker-cli/stubs/type/name.ts.stub +22 -0
- package/hono/src/framework/maker-cli/utils/naming.mjs +56 -13
- package/hono/src/framework/modules/discover.ts +57 -54
- package/hono/src/framework/notification/index.ts +1 -1
- package/hono/src/framework/realtime/broadcast.ts +46 -46
- package/hono/src/framework/server.ts +1 -1
- package/hono/src/framework/session/session.ts +121 -116
- package/hono/src/middlewares/auth-middleware.ts +1 -1
- package/hono/src/modules/auth/__tests__/forgetpass.test.ts +170 -0
- package/hono/src/modules/auth/__tests__/login.test.ts +154 -0
- package/hono/src/modules/auth/__tests__/logout.test.ts +53 -0
- package/hono/src/modules/auth/__tests__/logoutalldevices.test.ts +34 -0
- package/hono/src/modules/auth/__tests__/me.test.ts +87 -0
- package/hono/src/modules/auth/__tests__/refreshtoken.test.ts +305 -0
- package/hono/src/modules/auth/__tests__/register.test.ts +258 -0
- package/hono/src/modules/auth/__tests__/resetpass.test.ts +214 -0
- package/hono/src/modules/auth/__tests__/verifyemail.test.ts +189 -0
- package/hono/src/modules/auth/controllers/auth.ts +238 -0
- package/hono/src/modules/auth/controllers/{role.controller.ts → role.ts} +25 -25
- package/{express/src/modules/auth/database/models/notifications.ts → hono/src/modules/auth/database/models/notification.ts} +1 -1
- package/hono/src/modules/auth/database/models/role.ts +1 -1
- package/hono/src/modules/auth/database/models/user.ts +1 -1
- package/hono/src/modules/auth/facade.ts +12 -0
- package/hono/src/modules/auth/{controllers/auth.helpers.ts → helpers/auth.ts} +5 -6
- package/hono/src/modules/auth/jobs/registeruser.ts +23 -23
- package/hono/src/modules/auth/routes/{api.ts → index.ts} +150 -150
- package/hono/src/modules/auth/routes/role.ts +33 -33
- package/{express/src/modules/auth/controllers/auth.schema.ts → hono/src/modules/auth/schemas/auth.ts} +102 -102
- package/hono/src/modules/auth/services/forgetpassword.ts +58 -0
- package/hono/src/modules/auth/services/login.ts +61 -0
- package/hono/src/modules/auth/services/logout.ts +24 -0
- package/hono/src/modules/auth/services/logoutalldevices.ts +19 -0
- package/hono/src/modules/auth/services/me.ts +31 -0
- package/hono/src/modules/auth/services/refreshtoken.ts +110 -0
- package/hono/src/modules/auth/services/register.ts +132 -0
- package/hono/src/modules/auth/services/resetpassword.ts +69 -0
- package/hono/src/modules/auth/services/verifyemail.ts +77 -0
- package/hono/src/modules/auth/types/auth.ts +31 -0
- package/hono/src/resources/src/assets/images/logo.png +0 -0
- package/hono/src/storage/database/nexwire.sqlite +0 -0
- package/package.json +1 -1
- package/express/src/framework/maker-cli/stubs/example/console.ts.stub +0 -34
- package/express/src/framework/maker-cli/stubs/example/controller.ts.stub +0 -418
- package/express/src/framework/maker-cli/stubs/example/job.ts.stub +0 -75
- package/express/src/framework/maker-cli/stubs/example/route.api.ts.stub +0 -207
- package/express/src/framework/maker-cli/stubs/example/route.plain.ts.stub +0 -18
- package/express/src/framework/maker-cli/stubs/example/schema.plain.ts.stub +0 -42
- package/express/src/framework/maker-cli/stubs/example/schema.ts.stub +0 -42
- package/express/src/modules/auth/controllers/auth.controller.ts +0 -358
- package/hono/src/framework/maker-cli/stubs/example/console.ts.stub +0 -34
- package/hono/src/framework/maker-cli/stubs/example/controller.ts.stub +0 -505
- package/hono/src/framework/maker-cli/stubs/example/job.ts.stub +0 -75
- package/hono/src/framework/maker-cli/stubs/example/route.api.ts.stub +0 -207
- package/hono/src/framework/maker-cli/stubs/example/route.plain.ts.stub +0 -18
- package/hono/src/framework/maker-cli/stubs/example/schema.plain.ts.stub +0 -42
- package/hono/src/framework/maker-cli/stubs/example/schema.ts.stub +0 -42
- 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/
|
|
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 ?
|
|
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.
|
|
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
|
-
|
|
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
|
-
data
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
async
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return
|
|
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 {
|
|
4
|
-
import {
|
|
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,
|
|
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) {
|