@simple-auth-kit/cli 1.4.3 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/package.json +1 -1
  2. package/registry/combos/express-drizzle/shared/src/{auth-core-error.middleware.ts → middleware/auth-core-error.middleware.ts} +17 -4
  3. package/registry/combos/express-drizzle/shared/src/{auth.middleware.ts → middleware/auth.middleware.ts} +23 -7
  4. package/registry/combos/{express-prisma/shared/src → express-drizzle/shared/src/middleware}/response-envelope.middleware.ts +15 -3
  5. package/registry/combos/express-drizzle/shared/src/{oauth.repository.ts → repositories/oauth.repository.ts} +32 -9
  6. package/registry/combos/express-drizzle/shared/src/repositories/password-reset.repository.ts +59 -0
  7. package/registry/combos/express-drizzle/shared/src/{session.repository.ts → repositories/session.repository.ts} +52 -14
  8. package/registry/combos/express-drizzle/shared/src/{two-factor.repository.ts → repositories/two-factor.repository.ts} +20 -6
  9. package/registry/combos/express-drizzle/shared/src/request-context.ts +1 -1
  10. package/registry/combos/express-drizzle/shared/src/routers/auth.router.ts +395 -0
  11. package/registry/combos/express-drizzle/variants/base/src/create-auth-app.ts +52 -19
  12. package/registry/combos/express-drizzle/variants/base/src/{authz.middleware.ts → middleware/authz.middleware.ts} +15 -7
  13. package/registry/combos/express-drizzle/variants/base/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +28 -10
  14. package/registry/combos/express-drizzle/variants/base/src/{rbac.repository.ts → repositories/rbac.repository.ts} +239 -48
  15. package/registry/combos/express-drizzle/variants/base/src/routers/admin.router.ts +497 -0
  16. package/registry/combos/express-drizzle/variants/base/src/{auth.service.ts → services/auth.service.ts} +480 -109
  17. package/registry/combos/express-drizzle/variants/workspaces/src/create-auth-app.ts +69 -23
  18. package/registry/combos/express-drizzle/variants/workspaces/src/{authz.middleware.ts → middleware/authz.middleware.ts} +32 -12
  19. package/registry/combos/express-drizzle/variants/workspaces/src/openapi-workspace.ts +147 -28
  20. package/registry/combos/express-drizzle/variants/workspaces/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +30 -11
  21. package/registry/combos/express-drizzle/variants/workspaces/src/{rbac.repository.ts → repositories/rbac.repository.ts} +319 -70
  22. package/registry/combos/express-drizzle/variants/workspaces/src/{workspace.repository.ts → repositories/workspace.repository.ts} +127 -30
  23. package/registry/combos/express-drizzle/variants/workspaces/src/routers/admin.router.ts +534 -0
  24. package/registry/combos/express-drizzle/variants/workspaces/src/routers/workspace.router.ts +194 -0
  25. package/registry/combos/express-drizzle/variants/workspaces/src/{auth.service.ts → services/auth.service.ts} +544 -122
  26. package/registry/combos/express-prisma/shared/src/{auth-core-error.middleware.ts → middleware/auth-core-error.middleware.ts} +17 -4
  27. package/registry/combos/express-prisma/shared/src/{auth.middleware.ts → middleware/auth.middleware.ts} +19 -6
  28. package/registry/combos/{express-drizzle/shared/src → express-prisma/shared/src/middleware}/response-envelope.middleware.ts +15 -3
  29. package/registry/combos/express-prisma/shared/src/{oauth.repository.ts → repositories/oauth.repository.ts} +27 -9
  30. package/registry/combos/express-prisma/shared/src/repositories/password-reset.repository.ts +55 -0
  31. package/registry/combos/express-prisma/shared/src/{session.repository.ts → repositories/session.repository.ts} +30 -9
  32. package/registry/combos/express-prisma/shared/src/{two-factor.repository.ts → repositories/two-factor.repository.ts} +11 -4
  33. package/registry/combos/express-prisma/shared/src/request-context.ts +1 -1
  34. package/registry/combos/express-prisma/shared/src/routers/auth.router.ts +398 -0
  35. package/registry/combos/express-prisma/variants/base/src/create-auth-app.ts +57 -22
  36. package/registry/combos/express-prisma/variants/base/src/{authz.middleware.ts → middleware/authz.middleware.ts} +15 -7
  37. package/registry/combos/express-prisma/variants/base/src/rbac.defaults.ts +67 -19
  38. package/registry/combos/express-prisma/variants/base/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +16 -5
  39. package/registry/combos/express-prisma/variants/base/src/{rbac.repository.ts → repositories/rbac.repository.ts} +199 -48
  40. package/registry/combos/express-prisma/variants/base/src/routers/admin.router.ts +508 -0
  41. package/registry/combos/express-prisma/variants/base/src/seed.ts +78 -20
  42. package/registry/combos/express-prisma/variants/base/src/{auth.service.ts → services/auth.service.ts} +454 -105
  43. package/registry/combos/express-prisma/variants/workspaces/src/create-auth-app.ts +81 -26
  44. package/registry/combos/express-prisma/variants/workspaces/src/{authz.middleware.ts → middleware/authz.middleware.ts} +32 -12
  45. package/registry/combos/express-prisma/variants/workspaces/src/openapi-workspace.ts +147 -28
  46. package/registry/combos/express-prisma/variants/workspaces/src/rbac.defaults.ts +73 -21
  47. package/registry/combos/express-prisma/variants/workspaces/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +16 -5
  48. package/registry/combos/express-prisma/variants/workspaces/src/{rbac.repository.ts → repositories/rbac.repository.ts} +272 -64
  49. package/registry/combos/express-prisma/variants/workspaces/src/{workspace.repository.ts → repositories/workspace.repository.ts} +86 -23
  50. package/registry/combos/express-prisma/variants/workspaces/src/routers/admin.router.ts +545 -0
  51. package/registry/combos/express-prisma/variants/workspaces/src/routers/workspace.router.ts +194 -0
  52. package/registry/combos/express-prisma/variants/workspaces/src/seed.ts +109 -29
  53. package/registry/combos/express-prisma/variants/workspaces/src/{auth.service.ts → services/auth.service.ts} +518 -119
  54. package/registry/combos/nestjs-drizzle/shared/src/{auth.controller.ts → controllers/auth.controller.ts} +6 -6
  55. package/registry/combos/nestjs-drizzle/shared/src/{auth-core-error.filter.ts → filters/auth-core-error.filter.ts} +29 -5
  56. package/registry/combos/nestjs-drizzle/shared/src/{ability.guard.ts → guards/ability.guard.ts} +20 -7
  57. package/registry/combos/nestjs-drizzle/shared/src/{auth.guard.ts → guards/auth.guard.ts} +19 -7
  58. package/registry/combos/{nestjs-prisma/shared/src → nestjs-drizzle/shared/src/interceptors}/response.interceptor.ts +10 -2
  59. package/registry/combos/nestjs-drizzle/shared/src/repositories/oauth.repository.ts +63 -0
  60. package/registry/combos/nestjs-drizzle/shared/src/repositories/password-reset.repository.ts +59 -0
  61. package/registry/combos/nestjs-drizzle/shared/src/{session.repository.ts → repositories/session.repository.ts} +52 -14
  62. package/registry/combos/nestjs-drizzle/shared/src/{two-factor.repository.ts → repositories/two-factor.repository.ts} +21 -7
  63. package/registry/combos/nestjs-drizzle/shared/src/request-context.ts +1 -1
  64. package/registry/combos/nestjs-drizzle/test/prove-cycle.ts +1097 -307
  65. package/registry/combos/nestjs-drizzle/variants/base/src/auth.module.ts +44 -20
  66. package/registry/combos/nestjs-drizzle/variants/base/src/{admin.controller.ts → controllers/admin.controller.ts} +228 -62
  67. package/registry/combos/nestjs-drizzle/variants/base/src/{authz.guard.ts → guards/authz.guard.ts} +12 -5
  68. package/registry/combos/nestjs-drizzle/variants/base/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +27 -10
  69. package/registry/combos/nestjs-drizzle/variants/base/src/{rbac.repository.ts → repositories/rbac.repository.ts} +249 -51
  70. package/registry/combos/nestjs-drizzle/variants/base/src/{auth.service.ts → services/auth.service.ts} +499 -116
  71. package/registry/combos/nestjs-drizzle/variants/workspaces/src/auth.module.ts +48 -22
  72. package/registry/combos/nestjs-drizzle/variants/workspaces/src/{admin.controller.ts → controllers/admin.controller.ts} +269 -70
  73. package/registry/combos/nestjs-drizzle/variants/workspaces/src/{workspace.controller.ts → controllers/workspace.controller.ts} +93 -26
  74. package/registry/combos/nestjs-drizzle/variants/workspaces/src/{authz.guard.ts → guards/authz.guard.ts} +26 -8
  75. package/registry/combos/nestjs-drizzle/variants/workspaces/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +29 -11
  76. package/registry/combos/nestjs-drizzle/variants/workspaces/src/{rbac.repository.ts → repositories/rbac.repository.ts} +323 -71
  77. package/registry/combos/nestjs-drizzle/variants/workspaces/src/{workspace.repository.ts → repositories/workspace.repository.ts} +131 -30
  78. package/registry/combos/nestjs-drizzle/variants/workspaces/src/services/auth.service.ts +988 -0
  79. package/registry/combos/nestjs-prisma/shared/src/{auth.controller.ts → controllers/auth.controller.ts} +6 -6
  80. package/registry/combos/nestjs-prisma/shared/src/{auth-core-error.filter.ts → filters/auth-core-error.filter.ts} +29 -5
  81. package/registry/combos/nestjs-prisma/shared/src/{ability.guard.ts → guards/ability.guard.ts} +20 -7
  82. package/registry/combos/nestjs-prisma/shared/src/{auth.guard.ts → guards/auth.guard.ts} +19 -7
  83. package/registry/combos/{nestjs-drizzle/shared/src → nestjs-prisma/shared/src/interceptors}/response.interceptor.ts +10 -2
  84. package/registry/combos/nestjs-prisma/shared/src/{oauth.repository.ts → repositories/oauth.repository.ts} +25 -8
  85. package/registry/combos/nestjs-prisma/shared/src/repositories/password-reset.repository.ts +55 -0
  86. package/registry/combos/nestjs-prisma/shared/src/{session.repository.ts → repositories/session.repository.ts} +30 -9
  87. package/registry/combos/nestjs-prisma/shared/src/{two-factor.repository.ts → repositories/two-factor.repository.ts} +11 -4
  88. package/registry/combos/nestjs-prisma/shared/src/request-context.ts +1 -1
  89. package/registry/combos/nestjs-prisma/test/prove-cycle.ts +1097 -307
  90. package/registry/combos/nestjs-prisma/variants/base/src/auth.module.ts +59 -28
  91. package/registry/combos/nestjs-prisma/variants/base/src/{admin.controller.ts → controllers/admin.controller.ts} +469 -110
  92. package/registry/combos/nestjs-prisma/variants/base/src/{audit-log.gateway.ts → gateways/audit-log.gateway.ts} +22 -6
  93. package/registry/combos/nestjs-prisma/variants/base/src/{authz.guard.ts → guards/authz.guard.ts} +12 -5
  94. package/registry/combos/nestjs-prisma/variants/base/src/rbac.defaults.ts +98 -27
  95. package/registry/combos/nestjs-prisma/variants/base/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +16 -5
  96. package/registry/combos/nestjs-prisma/variants/base/src/{country.repository.ts → repositories/country.repository.ts} +98 -25
  97. package/registry/combos/nestjs-prisma/variants/base/src/{customer.repository.ts → repositories/customer.repository.ts} +106 -27
  98. package/registry/combos/nestjs-prisma/variants/base/src/{language.repository.ts → repositories/language.repository.ts} +93 -25
  99. package/registry/combos/nestjs-prisma/variants/base/src/{rbac.repository.ts → repositories/rbac.repository.ts} +229 -55
  100. package/registry/combos/nestjs-prisma/variants/base/src/seed.ts +78 -20
  101. package/registry/combos/nestjs-prisma/variants/base/src/services/auth.service.ts +1064 -0
  102. package/registry/combos/nestjs-prisma/variants/workspaces/src/auth.module.ts +55 -27
  103. package/registry/combos/nestjs-prisma/variants/workspaces/src/{admin.controller.ts → controllers/admin.controller.ts} +544 -128
  104. package/registry/combos/nestjs-prisma/variants/workspaces/src/{workspace.controller.ts → controllers/workspace.controller.ts} +93 -26
  105. package/registry/combos/nestjs-prisma/variants/workspaces/src/{authz.guard.ts → guards/authz.guard.ts} +26 -8
  106. package/registry/combos/nestjs-prisma/variants/workspaces/src/rbac.defaults.ts +104 -29
  107. package/registry/combos/nestjs-prisma/variants/workspaces/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +16 -5
  108. package/registry/combos/nestjs-prisma/variants/workspaces/src/{country.repository.ts → repositories/country.repository.ts} +127 -26
  109. package/registry/combos/nestjs-prisma/variants/workspaces/src/{customer.repository.ts → repositories/customer.repository.ts} +137 -28
  110. package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/language.repository.ts +243 -0
  111. package/registry/combos/nestjs-prisma/variants/workspaces/src/{rbac.repository.ts → repositories/rbac.repository.ts} +314 -74
  112. package/registry/combos/nestjs-prisma/variants/workspaces/src/{workspace.repository.ts → repositories/workspace.repository.ts} +94 -24
  113. package/registry/combos/nestjs-prisma/variants/workspaces/src/seed.ts +109 -29
  114. package/registry/combos/nestjs-prisma/variants/workspaces/src/services/auth.service.ts +961 -0
  115. package/registry.json +4 -4
  116. package/simple-auth-kit.ts +29 -2
  117. package/registry/combos/express-drizzle/shared/src/auth.router.ts +0 -266
  118. package/registry/combos/express-drizzle/shared/src/password-reset.repository.ts +0 -35
  119. package/registry/combos/express-drizzle/variants/base/src/admin.router.ts +0 -307
  120. package/registry/combos/express-drizzle/variants/workspaces/src/admin.router.ts +0 -323
  121. package/registry/combos/express-drizzle/variants/workspaces/src/workspace.router.ts +0 -123
  122. package/registry/combos/express-prisma/shared/src/auth.router.ts +0 -269
  123. package/registry/combos/express-prisma/shared/src/password-reset.repository.ts +0 -29
  124. package/registry/combos/express-prisma/variants/base/src/admin.router.ts +0 -314
  125. package/registry/combos/express-prisma/variants/workspaces/src/admin.router.ts +0 -330
  126. package/registry/combos/express-prisma/variants/workspaces/src/workspace.router.ts +0 -123
  127. package/registry/combos/nestjs-drizzle/shared/src/oauth.repository.ts +0 -36
  128. package/registry/combos/nestjs-drizzle/shared/src/password-reset.repository.ts +0 -29
  129. package/registry/combos/nestjs-drizzle/variants/workspaces/src/auth.service.ts +0 -553
  130. package/registry/combos/nestjs-prisma/shared/src/password-reset.repository.ts +0 -29
  131. package/registry/combos/nestjs-prisma/variants/base/src/auth.service.ts +0 -618
  132. package/registry/combos/nestjs-prisma/variants/workspaces/src/auth.service.ts +0 -509
  133. package/registry/combos/nestjs-prisma/variants/workspaces/src/language.repository.ts +0 -143
@@ -1,123 +0,0 @@
1
- import { Router } from "express";
2
- import type { NextFunction, Request, RequestHandler, Response } from "express";
3
- import { HttpError } from "./http-error.js";
4
- import { RbacRepository } from "./rbac.repository.js";
5
- import { ability, authenticated, createTieredRouter } from "./route-tiers.js";
6
- import { WorkspaceRepository } from "./workspace.repository.js";
7
- import "./request-context.js";
8
-
9
- function requireString(value: unknown, field: string): string {
10
- if (typeof value !== "string" || value.length === 0) throw new HttpError(400, `${field} is required`);
11
- return value;
12
- }
13
-
14
- function requireStringArray(value: unknown, field: string): string[] {
15
- if (!Array.isArray(value) || value.some((v) => typeof v !== "string")) throw new HttpError(400, `${field} must be an array of strings`);
16
- return value as string[];
17
- }
18
-
19
- export interface WorkspaceRouterDeps {
20
- workspaces: WorkspaceRepository;
21
- rbac: RbacRepository;
22
- authentication: RequestHandler;
23
- /** Resolves a workspace when the request names one, and does nothing when it does not. */
24
- authorization: RequestHandler;
25
- /** The same resolution, but the `X-Workspace-Id` header is mandatory. */
26
- workspaceScope: RequestHandler;
27
- }
28
-
29
- /**
30
- * Workspace membership. Every route below that acts *inside* a workspace names it with the
31
- * `X-Workspace-Id` header rather than a path segment — the same rule the `/auth/admin/*` routes
32
- * follow, so there is exactly one way a request says which workspace it means.
33
- *
34
- * Three routes here are deliberately tier 2 — `authenticated()`, any logged-in caller — and the
35
- * permission catalog mints no slugs for them. That is a decision written on each route, not an
36
- * omission: the tier is a required argument, so a route with none is a compile error
37
- * (route-tiers.ts).
38
- * • `POST /workspaces` and `GET /workspaces` are outside every workspace — any authenticated
39
- * user may create one or list their own. There is no workspace whose permissions could be
40
- * checked, and gating them on a permission granted *inside* some other workspace would mean
41
- * your first workspace could only be created by someone who already had one.
42
- * • `GET /workspaces/members` is gated on membership itself: the workspace middleware already
43
- * proved the caller belongs to the workspace, and seeing who else is in a room you are in is
44
- * not an administrative capability.
45
- * The three that mutate membership are permission-gated — see `rbac.defaults.ts` for the catalog.
46
- *
47
- * Two tiered routers, because the tier's middleware is what enforces the tier: the unscoped
48
- * routes resolve a workspace only if one is named, while everything under a workspace uses the
49
- * mandatory resolution. Mounting them in this order matters — the scoped router is consulted
50
- * first, so `/workspaces/members` reaches it rather than falling through.
51
- *
52
- * Replaces the reference combo's `WorkspaceController`.
53
- */
54
- export function createWorkspaceRouter(deps: WorkspaceRouterDeps): RequestHandler {
55
- const { workspaces, rbac, authentication, authorization, workspaceScope } = deps;
56
-
57
- const scoped = createTieredRouter({ authentication, authorization: workspaceScope });
58
- const unscoped = createTieredRouter({ authentication, authorization });
59
-
60
- unscoped.route("post", "/", authenticated(), async (req: Request, res: Response, next: NextFunction) => {
61
- try {
62
- const body = req.body as Record<string, unknown>;
63
- res.status(201).json(await workspaces.create(req.auth!.sub, requireString(body.name, "name")));
64
- } catch (err) {
65
- next(err);
66
- }
67
- });
68
-
69
- unscoped.route("get", "/", authenticated(), async (req: Request, res: Response, next: NextFunction) => {
70
- try {
71
- res.status(200).json(await workspaces.listForUser(req.auth!.sub));
72
- } catch (err) {
73
- next(err);
74
- }
75
- });
76
-
77
- scoped.route("get", "/members", authenticated(), async (req: Request, res: Response, next: NextFunction) => {
78
- try {
79
- res.status(200).json(await workspaces.listMembers(req.authz!.workspaceId));
80
- } catch (err) {
81
- next(err);
82
- }
83
- });
84
-
85
- scoped.route("post", "/members", ability("members:manage"), async (req: Request, res: Response, next: NextFunction) => {
86
- try {
87
- const body = req.body as Record<string, unknown>;
88
- // No `roles` in the body means "whatever this workspace flags as the default role" — the
89
- // default lives in a row, not in this line.
90
- const roles = body.roles === undefined ? undefined : requireStringArray(body.roles, "roles");
91
- res.status(201).json(await workspaces.addMember(req.authz!.workspaceId, requireString(body.email, "email"), roles));
92
- } catch (err) {
93
- next(err);
94
- }
95
- });
96
-
97
- scoped.route("put", "/members/:memberId/roles", ability("roles:assign"), async (req: Request, res: Response, next: NextFunction) => {
98
- try {
99
- const memberId = requireString(req.params.memberId, "memberId");
100
- if (memberId === req.authz!.memberId) throw new HttpError(403, "cannot change your own roles");
101
- const body = req.body as Record<string, unknown>;
102
- res.status(200).json(await rbac.setMemberRoles(req.authz!.workspaceId, memberId, requireStringArray(body.roles, "roles")));
103
- } catch (err) {
104
- next(err);
105
- }
106
- });
107
-
108
- scoped.route("delete", "/members/:memberId", ability("members:manage"), async (req: Request, res: Response, next: NextFunction) => {
109
- try {
110
- const memberId = requireString(req.params.memberId, "memberId");
111
- if (memberId === req.authz!.memberId) throw new HttpError(403, "cannot remove yourself from a workspace you administer");
112
- await workspaces.removeMember(req.authz!.workspaceId, memberId);
113
- res.status(200).json({ ok: true });
114
- } catch (err) {
115
- next(err);
116
- }
117
- });
118
-
119
- const router = Router();
120
- router.use(scoped.handler);
121
- router.use(unscoped.handler);
122
- return router;
123
- }
@@ -1,36 +0,0 @@
1
- import { BadRequestException, Inject, Injectable } from "@nestjs/common";
2
- import { and, eq } from "drizzle-orm";
3
- import type { OAuthStoreDeps } from "@/lib/auth/core/oauth.js";
4
- import { DRIZZLE_DB, type Database } from "./db.js";
5
- import { oauthAccounts, users } from "./schema.js";
6
- import { toId } from "./id.helper.js";
7
-
8
- @Injectable()
9
- export class OAuthRepository implements OAuthStoreDeps {
10
- constructor(@Inject(DRIZZLE_DB) private readonly db: Database) {}
11
-
12
- async findAccountByProvider(provider: string, providerAccountId: string): Promise<{ userId: string } | null> {
13
- const [row] = await this.db
14
- .select()
15
- .from(oauthAccounts)
16
- .where(and(eq(oauthAccounts.provider, provider), eq(oauthAccounts.providerAccountId, providerAccountId)))
17
- .limit(1);
18
- return row ? { userId: row.userId.toString() } : null;
19
- }
20
-
21
- async linkAccount(input: { userId: string; provider: string; providerAccountId: string; email?: string }): Promise<void> {
22
- await this.db.insert(oauthAccounts).values({ ...input, userId: toId(input.userId) });
23
- }
24
-
25
- async findUserByVerifiedEmail(email: string): Promise<{ id: string } | null> {
26
- const [row] = await this.db.select().from(users).where(eq(users.email, email)).limit(1);
27
- return row ? { id: row.id.toString() } : null;
28
- }
29
-
30
- /** OAuth-only signup: no password is ever set, which is why `User.passwordHash` is nullable. */
31
- async createUserFromOAuth(input: { email?: string }): Promise<{ id: string }> {
32
- if (!input.email) throw new BadRequestException("OAuth provider did not return an email address");
33
- const [row] = await this.db.insert(users).values({ email: input.email, passwordHash: null }).returning();
34
- return { id: row.id.toString() };
35
- }
36
- }
@@ -1,29 +0,0 @@
1
- import { Inject, Injectable } from "@nestjs/common";
2
- import { eq } from "drizzle-orm";
3
- import type { PasswordResetStoreDeps } from "@/lib/auth/core/password-reset.js";
4
- import { DRIZZLE_DB, type Database } from "./db.js";
5
- import { passwordResetTokens, users } from "./schema.js";
6
- import { toId } from "./id.helper.js";
7
-
8
- @Injectable()
9
- export class PasswordResetRepository implements PasswordResetStoreDeps {
10
- constructor(@Inject(DRIZZLE_DB) private readonly db: Database) {}
11
-
12
- async saveResetToken(input: { userId: string; tokenHash: string; expiresAt: string }): Promise<void> {
13
- await this.db.insert(passwordResetTokens).values({ userId: toId(input.userId), tokenHash: input.tokenHash, expiresAt: new Date(input.expiresAt) });
14
- }
15
-
16
- async findValidResetToken(tokenHash: string): Promise<{ userId: string; expiresAt: string; consumedAt: string | null } | null> {
17
- const [row] = await this.db.select().from(passwordResetTokens).where(eq(passwordResetTokens.tokenHash, tokenHash)).limit(1);
18
- if (!row) return null;
19
- return { userId: row.userId.toString(), expiresAt: row.expiresAt.toISOString(), consumedAt: row.consumedAt?.toISOString() ?? null };
20
- }
21
-
22
- async consumeResetToken(tokenHash: string): Promise<void> {
23
- await this.db.update(passwordResetTokens).set({ consumedAt: new Date() }).where(eq(passwordResetTokens.tokenHash, tokenHash));
24
- }
25
-
26
- async setPasswordHash(userId: string, passwordHash: string): Promise<void> {
27
- await this.db.update(users).set({ passwordHash }).where(eq(users.id, toId(userId)));
28
- }
29
- }