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,413 +0,0 @@
|
|
|
1
|
-
import { and, eq, gt, lt } from "drizzle-orm";
|
|
2
|
-
import type { Handler } from "hono";
|
|
3
|
-
import { authConfig, jwtConfig } from "@/config/index.js";
|
|
4
|
-
import { cookie, db, dispatchEvent, HttpStatusCodes, jwt, password, urls } from "@/framework/facade.js";
|
|
5
|
-
import { roles } from "@/modules/auth/database/models/role.js";
|
|
6
|
-
import { emailVerificationTokens, passwordResetTokens, refreshTokens, users } from "@/modules/auth/database/models/user.js";
|
|
7
|
-
import {
|
|
8
|
-
hashEmailVerificationToken,
|
|
9
|
-
hashResetToken,
|
|
10
|
-
issueTokens,
|
|
11
|
-
makeEmailVerificationToken,
|
|
12
|
-
makeResetToken,
|
|
13
|
-
revokeCurrentRefreshToken,
|
|
14
|
-
sanitizeUser
|
|
15
|
-
} from "./auth.helpers.js";
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Why: Creates a new user, issues tokens, and triggers signup side effects.
|
|
19
|
-
* When: Used on first-time account creation.
|
|
20
|
-
* Where: POST auth register route.
|
|
21
|
-
*/
|
|
22
|
-
export const register: Handler = async (c: any) => {
|
|
23
|
-
try {
|
|
24
|
-
const body = c.req.valid("json");
|
|
25
|
-
const defaultRole = await db.query.roles.findFirst({
|
|
26
|
-
where: eq(roles.name, "user")
|
|
27
|
-
});
|
|
28
|
-
const existingUser = await db.query.users.findFirst({
|
|
29
|
-
where: eq(users.email, body.email)
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
if (existingUser) {
|
|
33
|
-
return c.json({ message: "Email already exists" }, HttpStatusCodes.UNPROCESSABLE_ENTITY);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const insertResult = await db.insert(users).values({
|
|
37
|
-
name: body.name,
|
|
38
|
-
email: body.email,
|
|
39
|
-
password: await password.hashPassword(body.password),
|
|
40
|
-
roleId: defaultRole?.id ?? null
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
const insertedId = Number(
|
|
44
|
-
(insertResult as any)[0]?.insertId ?? (insertResult as any).insertId ?? (insertResult as any).lastInsertRowid
|
|
45
|
-
);
|
|
46
|
-
if (!insertedId) {
|
|
47
|
-
throw new Error("Failed to resolve inserted user id");
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const user = await db.query.users.findFirst({
|
|
51
|
-
where: eq(users.id, insertedId),
|
|
52
|
-
with: { role: true }
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
if (!user) throw new Error("Inserted user not found");
|
|
56
|
-
|
|
57
|
-
if (authConfig.requireEmailVerification) {
|
|
58
|
-
const plainToken = makeEmailVerificationToken();
|
|
59
|
-
const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000);
|
|
60
|
-
|
|
61
|
-
await db.delete(emailVerificationTokens).where(eq(emailVerificationTokens.email, user.email));
|
|
62
|
-
await db.insert(emailVerificationTokens).values({
|
|
63
|
-
email: user.email,
|
|
64
|
-
token: hashEmailVerificationToken(plainToken),
|
|
65
|
-
expiresAt,
|
|
66
|
-
createdAt: new Date()
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
const verifyUrl = urls.url(`/verify-email?token=${plainToken}&email=${encodeURIComponent(user.email)}`);
|
|
70
|
-
await dispatchEvent("user:verify-email", { email: user.email, name: user.name, verifyUrl }, { queue: "mail" });
|
|
71
|
-
|
|
72
|
-
return c.json({ message: "User registered successfully. Please verify your email before logging in." }, HttpStatusCodes.CREATED);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
await revokeCurrentRefreshToken(c);
|
|
76
|
-
const tokens = await issueTokens(c, user, { remember: !!body.remember });
|
|
77
|
-
await dispatchEvent(
|
|
78
|
-
"user:signup",
|
|
79
|
-
{
|
|
80
|
-
userId: user.id,
|
|
81
|
-
email: user.email,
|
|
82
|
-
name: user.name,
|
|
83
|
-
password: body.password
|
|
84
|
-
},
|
|
85
|
-
{ queue: "mail" }
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
return c.json(
|
|
89
|
-
{
|
|
90
|
-
message: "User registered successfully",
|
|
91
|
-
data: {
|
|
92
|
-
user: sanitizeUser(user),
|
|
93
|
-
access_token: tokens.accessToken,
|
|
94
|
-
refresh_token: tokens.refreshToken,
|
|
95
|
-
token_type: "Bearer"
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
HttpStatusCodes.CREATED
|
|
99
|
-
);
|
|
100
|
-
} catch (error) {
|
|
101
|
-
console.error("Register error:", error);
|
|
102
|
-
return c.json({ message: "Failed to register user" }, HttpStatusCodes.INTERNAL_SERVER_ERROR);
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Why: Authenticates user credentials and rotates active login cookies/tokens.
|
|
108
|
-
* When: Used whenever a user signs in.
|
|
109
|
-
* Where: POST auth login route.
|
|
110
|
-
*/
|
|
111
|
-
export const login: Handler = async (c: any) => {
|
|
112
|
-
try {
|
|
113
|
-
const body = c.req.valid("json");
|
|
114
|
-
const user = await db.query.users.findFirst({ where: eq(users.email, body.email), with: { role: true } });
|
|
115
|
-
|
|
116
|
-
if (!user || !(await password.verifyPassword(body.password, user.password))) {
|
|
117
|
-
return c.json({ message: "Invalid credentials" }, HttpStatusCodes.UNAUTHORIZED);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (authConfig.requireEmailVerification && !user.emailVerifiedAt) {
|
|
121
|
-
return c.json({ message: "Please verify your email before logging in" }, HttpStatusCodes.FORBIDDEN);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
await revokeCurrentRefreshToken(c);
|
|
125
|
-
const tokens = await issueTokens(c, user, { remember: !!body.remember });
|
|
126
|
-
|
|
127
|
-
return c.json(
|
|
128
|
-
{
|
|
129
|
-
message: "User logged in successfully",
|
|
130
|
-
data: {
|
|
131
|
-
user: sanitizeUser(user),
|
|
132
|
-
access_token: tokens.accessToken,
|
|
133
|
-
refresh_token: tokens.refreshToken,
|
|
134
|
-
token_type: "Bearer"
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
HttpStatusCodes.OK
|
|
138
|
-
);
|
|
139
|
-
} catch (error) {
|
|
140
|
-
console.error("Login error:", error);
|
|
141
|
-
return c.json({ message: "Failed to login" }, HttpStatusCodes.INTERNAL_SERVER_ERROR);
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Why: Returns the currently authenticated user profile.
|
|
147
|
-
* When: Used by clients to bootstrap session/user state.
|
|
148
|
-
* Where: GET auth me route.
|
|
149
|
-
*/
|
|
150
|
-
export const me: Handler = async (c: any) => {
|
|
151
|
-
try {
|
|
152
|
-
const auth = c.get("auth");
|
|
153
|
-
const user = await db.query.users.findFirst({
|
|
154
|
-
where: eq(users.id, auth.id),
|
|
155
|
-
with: { role: true }
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
if (!user) return c.json({ message: "User not found" }, HttpStatusCodes.NOT_FOUND);
|
|
159
|
-
|
|
160
|
-
return c.json(
|
|
161
|
-
{
|
|
162
|
-
message: "Authenticated user fetched successfully",
|
|
163
|
-
data: sanitizeUser(user)
|
|
164
|
-
},
|
|
165
|
-
HttpStatusCodes.OK
|
|
166
|
-
);
|
|
167
|
-
} catch (error) {
|
|
168
|
-
console.error("Me error:", error);
|
|
169
|
-
return c.json({ message: "Failed to fetch user" }, HttpStatusCodes.INTERNAL_SERVER_ERROR);
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Why: Revokes current refresh token and clears auth cookies.
|
|
175
|
-
* When: Used when the active device logs out.
|
|
176
|
-
* Where: POST auth logout route.
|
|
177
|
-
*/
|
|
178
|
-
export const logout: Handler = async (c: any) => {
|
|
179
|
-
try {
|
|
180
|
-
await revokeCurrentRefreshToken(c);
|
|
181
|
-
cookie.deleteAuth(c);
|
|
182
|
-
cookie.deleteRefresh(c);
|
|
183
|
-
return c.json({ message: "Logged out successfully" }, HttpStatusCodes.OK);
|
|
184
|
-
} catch (error) {
|
|
185
|
-
console.error("Logout error:", error);
|
|
186
|
-
cookie.deleteAuth(c);
|
|
187
|
-
cookie.deleteRefresh(c);
|
|
188
|
-
return c.json({ message: "Failed to logout" }, HttpStatusCodes.INTERNAL_SERVER_ERROR);
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Why: Starts password reset flow and queues email delivery event.
|
|
194
|
-
* When: Used when user requests a forgot-password link.
|
|
195
|
-
* Where: POST auth forgot-password route.
|
|
196
|
-
*/
|
|
197
|
-
export const forgotPassword: Handler = async (c: any) => {
|
|
198
|
-
try {
|
|
199
|
-
const body = c.req.valid("json");
|
|
200
|
-
await db
|
|
201
|
-
.delete(passwordResetTokens)
|
|
202
|
-
.where(and(eq(passwordResetTokens.email, body.email), lt(passwordResetTokens.expiresAt, new Date())));
|
|
203
|
-
|
|
204
|
-
const user = await db.query.users.findFirst({
|
|
205
|
-
where: eq(users.email, body.email)
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
if (!user) return c.json({ message: "Reset link has been sent" }, HttpStatusCodes.OK);
|
|
209
|
-
|
|
210
|
-
const plainToken = makeResetToken();
|
|
211
|
-
const expiresAt = new Date(Date.now() + 15 * 60 * 1000);
|
|
212
|
-
|
|
213
|
-
await db.delete(passwordResetTokens).where(eq(passwordResetTokens.email, user.email));
|
|
214
|
-
await db.insert(passwordResetTokens).values({
|
|
215
|
-
email: user.email,
|
|
216
|
-
token: hashResetToken(plainToken),
|
|
217
|
-
expiresAt,
|
|
218
|
-
createdAt: new Date()
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
const resetUrl = urls.url(`/reset-password?token=${plainToken}&email=${encodeURIComponent(user.email)}`);
|
|
222
|
-
await dispatchEvent("user:forget-password", { email: user.email, name: user.name, resetUrl }, { queue: "mail" });
|
|
223
|
-
|
|
224
|
-
return c.json({ message: "Reset link passed" }, HttpStatusCodes.OK);
|
|
225
|
-
} catch (error) {
|
|
226
|
-
console.error("Forgot password error:", error);
|
|
227
|
-
return c.json({ message: "Failed to process forgot password request" }, HttpStatusCodes.INTERNAL_SERVER_ERROR);
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Why: Verifies reset token and persists the new password.
|
|
233
|
-
* When: Used after user submits reset token + new password.
|
|
234
|
-
* Where: POST auth reset-password route.
|
|
235
|
-
*/
|
|
236
|
-
export const resetPassword: Handler = async (c: any) => {
|
|
237
|
-
try {
|
|
238
|
-
const body = c.req.valid("json");
|
|
239
|
-
const record = await db.query.passwordResetTokens.findFirst({
|
|
240
|
-
where: and(
|
|
241
|
-
eq(passwordResetTokens.email, body.email),
|
|
242
|
-
eq(passwordResetTokens.token, hashResetToken(body.token)),
|
|
243
|
-
gt(passwordResetTokens.expiresAt, new Date())
|
|
244
|
-
)
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
if (!record) {
|
|
248
|
-
return c.json({ message: "Invalid or expired reset token" }, HttpStatusCodes.UNPROCESSABLE_ENTITY);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
await db
|
|
252
|
-
.update(users)
|
|
253
|
-
.set({
|
|
254
|
-
password: await password.hashPassword(body.password),
|
|
255
|
-
updatedAt: new Date()
|
|
256
|
-
})
|
|
257
|
-
.where(eq(users.email, body.email));
|
|
258
|
-
|
|
259
|
-
await db.delete(passwordResetTokens).where(eq(passwordResetTokens.email, body.email));
|
|
260
|
-
|
|
261
|
-
const user = await db.query.users.findFirst({
|
|
262
|
-
where: eq(users.email, body.email)
|
|
263
|
-
});
|
|
264
|
-
if (user) await db.update(refreshTokens).set({ revoked: 1 }).where(eq(refreshTokens.userId, user.id));
|
|
265
|
-
|
|
266
|
-
return c.json({ message: "Password reset successfully" }, HttpStatusCodes.OK);
|
|
267
|
-
} catch (error) {
|
|
268
|
-
console.error("Reset password error:", error);
|
|
269
|
-
return c.json({ message: "Failed to reset password" }, HttpStatusCodes.INTERNAL_SERVER_ERROR);
|
|
270
|
-
}
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Why: Validates email verification token and marks user as verified.
|
|
275
|
-
* When: Used when user opens verification link from inbox.
|
|
276
|
-
* Where: POST auth verify-email route.
|
|
277
|
-
*/
|
|
278
|
-
export const verifyEmail: Handler = async (c: any) => {
|
|
279
|
-
try {
|
|
280
|
-
const body = c.req.valid("json");
|
|
281
|
-
|
|
282
|
-
if (!authConfig.requireEmailVerification) {
|
|
283
|
-
return c.json({ message: "Email verification is not required" }, HttpStatusCodes.OK);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
const record = await db.query.emailVerificationTokens.findFirst({
|
|
287
|
-
where: and(
|
|
288
|
-
eq(emailVerificationTokens.email, body.email),
|
|
289
|
-
eq(emailVerificationTokens.token, hashEmailVerificationToken(body.token)),
|
|
290
|
-
gt(emailVerificationTokens.expiresAt, new Date())
|
|
291
|
-
)
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
if (!record) {
|
|
295
|
-
return c.json({ message: "Invalid or expired verification token" }, HttpStatusCodes.UNPROCESSABLE_ENTITY);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
await db.update(users).set({ emailVerifiedAt: new Date(), updatedAt: new Date() }).where(eq(users.email, body.email));
|
|
299
|
-
await db.delete(emailVerificationTokens).where(eq(emailVerificationTokens.email, body.email));
|
|
300
|
-
|
|
301
|
-
return c.json({ message: "Email verified successfully" }, HttpStatusCodes.OK);
|
|
302
|
-
} catch (error) {
|
|
303
|
-
console.error("Verify email error:", error);
|
|
304
|
-
return c.json({ message: "Failed to verify email" }, HttpStatusCodes.INTERNAL_SERVER_ERROR);
|
|
305
|
-
}
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Why: Rotates refresh token and reissues access credentials.
|
|
310
|
-
* When: Used when access token expires but refresh token is still valid.
|
|
311
|
-
* Where: POST auth refresh-token route.
|
|
312
|
-
*/
|
|
313
|
-
export const refreshToken: Handler = async (c: any) => {
|
|
314
|
-
try {
|
|
315
|
-
const body = c.req.valid("json");
|
|
316
|
-
const payload = await jwt.verifyToken(body.refresh_token, "refresh");
|
|
317
|
-
|
|
318
|
-
if (!payload?.jti) return c.json({ message: "Invalid refresh token" }, HttpStatusCodes.UNAUTHORIZED);
|
|
319
|
-
|
|
320
|
-
const storedToken = await db.query.refreshTokens.findFirst({
|
|
321
|
-
where: eq(refreshTokens.jti, payload.jti as string)
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
if (!storedToken || storedToken.revoked === 1) {
|
|
325
|
-
return c.json({ message: "Refresh token revoked" }, HttpStatusCodes.UNAUTHORIZED);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
if (storedToken.expiresAt.getTime() < Date.now()) {
|
|
329
|
-
await db.delete(refreshTokens).where(eq(refreshTokens.id, storedToken.id));
|
|
330
|
-
return c.json({ message: "Refresh token expired" }, HttpStatusCodes.UNAUTHORIZED);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
const user = await db.query.users.findFirst({
|
|
334
|
-
where: eq(users.id, payload.id as number),
|
|
335
|
-
with: { role: true }
|
|
336
|
-
});
|
|
337
|
-
if (!user) return c.json({ message: "User not found" }, HttpStatusCodes.UNAUTHORIZED);
|
|
338
|
-
|
|
339
|
-
const remember = !!payload.remember;
|
|
340
|
-
const refreshExpiry = remember ? jwtConfig.refreshRememberExpirySeconds : undefined;
|
|
341
|
-
const accessToken = await jwt.generateToken(
|
|
342
|
-
{
|
|
343
|
-
id: user.id,
|
|
344
|
-
email: user.email,
|
|
345
|
-
roleId: user.role?.id,
|
|
346
|
-
role: user.role?.name,
|
|
347
|
-
remember
|
|
348
|
-
},
|
|
349
|
-
"access"
|
|
350
|
-
);
|
|
351
|
-
const newRefreshToken = await jwt.generateToken(
|
|
352
|
-
{
|
|
353
|
-
id: user.id,
|
|
354
|
-
email: user.email,
|
|
355
|
-
roleId: user.role?.id,
|
|
356
|
-
role: user.role?.name,
|
|
357
|
-
remember
|
|
358
|
-
},
|
|
359
|
-
"refresh",
|
|
360
|
-
refreshExpiry
|
|
361
|
-
);
|
|
362
|
-
|
|
363
|
-
await db
|
|
364
|
-
.update(refreshTokens)
|
|
365
|
-
.set({
|
|
366
|
-
jti: newRefreshToken.jti as string,
|
|
367
|
-
expiresAt: new Date(newRefreshToken.exp * 1000),
|
|
368
|
-
revoked: 0
|
|
369
|
-
})
|
|
370
|
-
.where(eq(refreshTokens.id, storedToken.id));
|
|
371
|
-
|
|
372
|
-
await cookie.setAuth(c, accessToken.token);
|
|
373
|
-
await cookie.setRefresh(c, newRefreshToken.token, refreshExpiry);
|
|
374
|
-
|
|
375
|
-
return c.json(
|
|
376
|
-
{
|
|
377
|
-
message: "Token refreshed successfully",
|
|
378
|
-
data: {
|
|
379
|
-
user: sanitizeUser(user),
|
|
380
|
-
access_token: accessToken.token,
|
|
381
|
-
refresh_token: newRefreshToken.token,
|
|
382
|
-
token_type: "Bearer"
|
|
383
|
-
}
|
|
384
|
-
},
|
|
385
|
-
HttpStatusCodes.OK
|
|
386
|
-
);
|
|
387
|
-
} catch (error) {
|
|
388
|
-
console.error("Refresh token error:", error);
|
|
389
|
-
return c.json({ message: "Invalid or expired refresh token" }, HttpStatusCodes.UNAUTHORIZED);
|
|
390
|
-
}
|
|
391
|
-
};
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Why: Revokes all refresh tokens for account-wide logout.
|
|
395
|
-
* When: Used for "logout from all devices" security action.
|
|
396
|
-
* Where: POST auth logout-all-devices route.
|
|
397
|
-
*/
|
|
398
|
-
export const logoutAllDevices: Handler = async (c: any) => {
|
|
399
|
-
try {
|
|
400
|
-
const auth = c.get("auth");
|
|
401
|
-
|
|
402
|
-
if (!auth) return c.json({ message: "Unauthorized" }, HttpStatusCodes.UNAUTHORIZED);
|
|
403
|
-
|
|
404
|
-
await db.delete(refreshTokens).where(eq(refreshTokens.userId, auth.id));
|
|
405
|
-
cookie.deleteAuth(c);
|
|
406
|
-
cookie.deleteRefresh(c);
|
|
407
|
-
|
|
408
|
-
return c.json({ message: "Logged out from all devices successfully" }, HttpStatusCodes.OK);
|
|
409
|
-
} catch (error) {
|
|
410
|
-
console.error("Logout all devices error:", error);
|
|
411
|
-
return c.json({ message: "Failed to logout from all devices" }, HttpStatusCodes.INTERNAL_SERVER_ERROR);
|
|
412
|
-
}
|
|
413
|
-
};
|