@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,8 +1,16 @@
1
- import { ConflictException, Inject, Injectable, NotFoundException } from "@nestjs/common";
2
- import { PrismaClient } from "../generated/prisma/client.js";
3
- import { provisionDefaultRoles, WORKSPACE_CREATOR_ROLES } from "./rbac.defaults.js";
1
+ import {
2
+ ConflictException,
3
+ Inject,
4
+ Injectable,
5
+ NotFoundException,
6
+ } from "@nestjs/common";
7
+ import { PrismaClient } from "../../generated/prisma/client.js";
8
+ import {
9
+ provisionDefaultRoles,
10
+ WORKSPACE_CREATOR_ROLES,
11
+ } from "../rbac.defaults.js";
4
12
  import { RbacRepository } from "./rbac.repository.js";
5
- import { toId } from "./id.helper.js";
13
+ import { toId } from "../id.helper.js";
6
14
 
7
15
  export interface WorkspaceSummary {
8
16
  id: string;
@@ -33,11 +41,23 @@ export class WorkspaceRepository {
33
41
  async create(userId: string, name: string): Promise<WorkspaceSummary> {
34
42
  const userIdBig = toId(userId);
35
43
  const workspace = await this.prisma.$transaction(async (tx) => {
36
- const created = await tx.workspace.create({ data: { name, createdBy: userIdBig, updatedBy: userIdBig } });
44
+ const created = await tx.workspace.create({
45
+ data: { name, createdBy: userIdBig, updatedBy: userIdBig },
46
+ });
37
47
  await provisionDefaultRoles(tx, created.id);
38
- const roles = await tx.role.findMany({ where: { workspaceId: created.id, slug: { in: WORKSPACE_CREATOR_ROLES } }, select: { id: true } });
48
+ const roles = await tx.role.findMany({
49
+ where: {
50
+ workspaceId: created.id,
51
+ slug: { in: WORKSPACE_CREATOR_ROLES },
52
+ },
53
+ select: { id: true },
54
+ });
39
55
  await tx.workspaceMember.create({
40
- data: { workspaceId: created.id, userId: userIdBig, roles: { create: roles.map((role) => ({ roleId: role.id })) } },
56
+ data: {
57
+ workspaceId: created.id,
58
+ userId: userIdBig,
59
+ roles: { create: roles.map((role) => ({ roleId: role.id })) },
60
+ },
41
61
  });
42
62
  return created;
43
63
  });
@@ -51,8 +71,14 @@ export class WorkspaceRepository {
51
71
 
52
72
  async listForUser(userId: string): Promise<WorkspaceSummary[]> {
53
73
  const memberships = await this.prisma.workspaceMember.findMany({
54
- where: { userId: toId(userId), workspace: { isDeleted: false, isActive: true } },
55
- include: { workspace: true, roles: { select: { role: { select: { slug: true } } } } },
74
+ where: {
75
+ userId: toId(userId),
76
+ workspace: { isDeleted: false, isActive: true },
77
+ },
78
+ include: {
79
+ workspace: true,
80
+ roles: { select: { role: { select: { slug: true } } } },
81
+ },
56
82
  orderBy: { createdAt: "asc" },
57
83
  });
58
84
  return memberships.map((m) => ({
@@ -66,7 +92,10 @@ export class WorkspaceRepository {
66
92
  async listMembers(workspaceId: string): Promise<MembershipSummary[]> {
67
93
  const rows = await this.prisma.workspaceMember.findMany({
68
94
  where: { workspaceId: toId(workspaceId), user: { isDeleted: false } },
69
- include: { user: true, roles: { select: { role: { select: { slug: true } } } } },
95
+ include: {
96
+ user: true,
97
+ roles: { select: { role: { select: { slug: true } } } },
98
+ },
70
99
  orderBy: { createdAt: "asc" },
71
100
  });
72
101
  return rows.map((row) => ({
@@ -78,31 +107,63 @@ export class WorkspaceRepository {
78
107
  }));
79
108
  }
80
109
 
81
- private async resolveMemberRoles(workspaceIdBig: bigint, roleSlugs?: string[]) {
110
+ private async resolveMemberRoles(
111
+ workspaceIdBig: bigint,
112
+ roleSlugs?: string[],
113
+ ) {
82
114
  const roles = await this.prisma.role.findMany({
83
115
  where: roleSlugs
84
- ? { workspaceId: workspaceIdBig, slug: { in: roleSlugs }, isDeleted: false }
85
- : { workspaceId: workspaceIdBig, isDefault: true, isActive: true, isDeleted: false },
116
+ ? {
117
+ workspaceId: workspaceIdBig,
118
+ slug: { in: roleSlugs },
119
+ isDeleted: false,
120
+ }
121
+ : {
122
+ workspaceId: workspaceIdBig,
123
+ isDefault: true,
124
+ isActive: true,
125
+ isDeleted: false,
126
+ },
86
127
  select: { id: true, slug: true },
87
128
  });
88
- const unknown = (roleSlugs ?? []).filter((slug) => !roles.some((role) => role.slug === slug));
89
- if (unknown.length) throw new NotFoundException(`role(s) not defined in this workspace: ${unknown.join(", ")}`);
129
+ const unknown = (roleSlugs ?? []).filter(
130
+ (slug) => !roles.some((role) => role.slug === slug),
131
+ );
132
+ if (unknown.length)
133
+ throw new NotFoundException(
134
+ `role(s) not defined in this workspace: ${unknown.join(", ")}`,
135
+ );
90
136
  return roles;
91
137
  }
92
138
 
93
139
  // Adds an existing user by email — there is no invite/email flow in this library. With no
94
140
  // roles named, the new membership gets whichever of this workspace's roles are flagged `isDefault`.
95
- async addMember(workspaceId: string, email: string, roleSlugs?: string[]): Promise<MembershipSummary> {
141
+ async addMember(
142
+ workspaceId: string,
143
+ email: string,
144
+ roleSlugs?: string[],
145
+ ): Promise<MembershipSummary> {
96
146
  const workspaceIdBig = toId(workspaceId);
97
- const user = await this.prisma.user.findUnique({ where: { email, isDeleted: false } });
147
+ const user = await this.prisma.user.findUnique({
148
+ where: { email, isDeleted: false },
149
+ });
98
150
  if (!user) throw new NotFoundException("no user with that email");
99
151
 
100
- const existing = await this.prisma.workspaceMember.findUnique({ where: { userId_workspaceId: { userId: user.id, workspaceId: workspaceIdBig } } });
101
- if (existing) throw new ConflictException("already a member of this workspace");
152
+ const existing = await this.prisma.workspaceMember.findUnique({
153
+ where: {
154
+ userId_workspaceId: { userId: user.id, workspaceId: workspaceIdBig },
155
+ },
156
+ });
157
+ if (existing)
158
+ throw new ConflictException("already a member of this workspace");
102
159
 
103
160
  const roles = await this.resolveMemberRoles(workspaceIdBig, roleSlugs);
104
161
  const member = await this.prisma.workspaceMember.create({
105
- data: { workspaceId: workspaceIdBig, userId: user.id, roles: { create: roles.map((role) => ({ roleId: role.id })) } },
162
+ data: {
163
+ workspaceId: workspaceIdBig,
164
+ userId: user.id,
165
+ roles: { create: roles.map((role) => ({ roleId: role.id })) },
166
+ },
106
167
  });
107
168
  return {
108
169
  memberId: member.id.toString(),
@@ -134,7 +195,9 @@ export class WorkspaceRepository {
134
195
  actorUserId: string | null,
135
196
  ): Promise<MembershipSummary> {
136
197
  const workspaceIdBig = toId(workspaceId);
137
- const existing = await this.prisma.user.findUnique({ where: { email: input.email } });
198
+ const existing = await this.prisma.user.findUnique({
199
+ where: { email: input.email },
200
+ });
138
201
  if (existing) throw new ConflictException("email already registered");
139
202
 
140
203
  const roles = await this.resolveMemberRoles(workspaceIdBig, input.roles);
@@ -152,7 +215,11 @@ export class WorkspaceRepository {
152
215
  },
153
216
  });
154
217
  const member = await this.prisma.workspaceMember.create({
155
- data: { workspaceId: workspaceIdBig, userId: user.id, roles: { create: roles.map((role) => ({ roleId: role.id })) } },
218
+ data: {
219
+ workspaceId: workspaceIdBig,
220
+ userId: user.id,
221
+ roles: { create: roles.map((role) => ({ roleId: role.id })) },
222
+ },
156
223
  });
157
224
  return {
158
225
  memberId: member.id.toString(),
@@ -166,8 +233,11 @@ export class WorkspaceRepository {
166
233
  async removeMember(workspaceId: string, memberId: string): Promise<void> {
167
234
  const workspaceIdBig = toId(workspaceId);
168
235
  const memberIdBig = toId(memberId);
169
- const member = await this.prisma.workspaceMember.findUnique({ where: { id: memberIdBig } });
170
- if (!member || member.workspaceId !== workspaceIdBig) throw new NotFoundException("member not found in this workspace");
236
+ const member = await this.prisma.workspaceMember.findUnique({
237
+ where: { id: memberIdBig },
238
+ });
239
+ if (!member || member.workspaceId !== workspaceIdBig)
240
+ throw new NotFoundException("member not found in this workspace");
171
241
 
172
242
  // Role assignments and direct grants belong to the membership, so `onDelete: Cascade` on
173
243
  // both join tables takes them with it.
@@ -11,21 +11,35 @@
11
11
  // routes demand.
12
12
  import { PrismaPg } from "@prisma/adapter-pg";
13
13
  import { hashPassword } from "@/lib/auth/core/crypto.js";
14
- import { PrismaClient } from "../generated/prisma/client.js";
15
- import { DEFAULT_ROLES, PERMISSION_SLUGS, provisionDefaultRoles, SEED_SUPERADMIN_ROLES, WORKSPACE_CREATOR_ROLES } from "./rbac.defaults.js";
14
+ import { PrismaClient } from "./../generated/prisma/client.js";
15
+ import {
16
+ DEFAULT_ROLES,
17
+ PERMISSION_SLUGS,
18
+ provisionDefaultRoles,
19
+ SEED_SUPERADMIN_ROLES,
20
+ WORKSPACE_CREATOR_ROLES,
21
+ } from "./rbac.defaults.js";
16
22
 
17
23
  const DEFAULT_WORKSPACE_NAME = "Default workspace";
18
24
 
19
25
  function requireEnv(name: string): string {
20
26
  const value = process.env[name];
21
- if (!value) throw new Error(`${name} is not set — the seeder cannot reach the database without it`);
27
+ if (!value)
28
+ throw new Error(
29
+ `${name} is not set — the seeder cannot reach the database without it`,
30
+ );
22
31
  return value;
23
32
  }
24
33
 
25
34
  // Workspace names aren't unique in the schema, so re-running adopts the one it made last time.
26
- async function seedWorkspace(prisma: PrismaClient): Promise<{ id: bigint; name: string }> {
35
+ async function seedWorkspace(
36
+ prisma: PrismaClient,
37
+ ): Promise<{ id: bigint; name: string }> {
27
38
  const name = process.env["SEED_WORKSPACE_NAME"] || DEFAULT_WORKSPACE_NAME;
28
- const existing = await prisma.workspace.findFirst({ where: { name }, orderBy: { createdAt: "asc" } });
39
+ const existing = await prisma.workspace.findFirst({
40
+ where: { name },
41
+ orderBy: { createdAt: "asc" },
42
+ });
29
43
  if (existing) {
30
44
  console.log(`workspace: "${name}" already exists (${existing.id})`);
31
45
  return existing;
@@ -35,66 +49,130 @@ async function seedWorkspace(prisma: PrismaClient): Promise<{ id: bigint; name:
35
49
  return created;
36
50
  }
37
51
 
38
- async function seedRbacDefaults(prisma: PrismaClient, workspaceId: bigint): Promise<void> {
52
+ async function seedRbacDefaults(
53
+ prisma: PrismaClient,
54
+ workspaceId: bigint,
55
+ ): Promise<void> {
39
56
  await provisionDefaultRoles(prisma, workspaceId);
40
57
  console.log(`permissions: ${PERMISSION_SLUGS.length} slug(s) in the catalog`);
41
- for (const role of DEFAULT_ROLES) console.log(`role "${role.slug}": ${role.permissions.length} permission(s)${role.isDefault ? " (new-member default)" : ""}`);
58
+ for (const role of DEFAULT_ROLES)
59
+ console.log(
60
+ `role "${role.slug}": ${role.permissions.length} permission(s)${role.isDefault ? " (new-member default)" : ""}`,
61
+ );
42
62
 
43
63
  // Slugs in the database but not this build's catalog aren't an error — a deployment can
44
64
  // define its own — but a typo would otherwise silently grant nothing, so surface them.
45
- const rows = await prisma.permission.findMany({ select: { slug: true, isActive: true } });
46
- const unknown = rows.filter((row) => !(PERMISSION_SLUGS as string[]).includes(row.slug));
47
- if (unknown.length) console.log(`permissions: ${unknown.length} slug(s) outside this build's catalog (no route names them): ${unknown.map((r) => r.slug).join(", ")}`);
65
+ const rows = await prisma.permission.findMany({
66
+ select: { slug: true, isActive: true },
67
+ });
68
+ const unknown = rows.filter(
69
+ (row) => !(PERMISSION_SLUGS as string[]).includes(row.slug),
70
+ );
71
+ if (unknown.length)
72
+ console.log(
73
+ `permissions: ${unknown.length} slug(s) outside this build's catalog (no route names them): ${unknown.map((r) => r.slug).join(", ")}`,
74
+ );
48
75
  const inactive = rows.filter((row) => !row.isActive);
49
- if (inactive.length) console.log(`permissions: ${inactive.length} deactivated, granting nothing: ${inactive.map((r) => r.slug).join(", ")}`);
76
+ if (inactive.length)
77
+ console.log(
78
+ `permissions: ${inactive.length} deactivated, granting nothing: ${inactive.map((r) => r.slug).join(", ")}`,
79
+ );
50
80
  }
51
81
 
52
- async function seedAdminUser(prisma: PrismaClient, workspace: { id: bigint; name: string }): Promise<void> {
82
+ async function seedAdminUser(
83
+ prisma: PrismaClient,
84
+ workspace: { id: bigint; name: string },
85
+ ): Promise<void> {
53
86
  const email = process.env["SEED_ADMIN_EMAIL"];
54
87
  const password = process.env["SEED_ADMIN_PASSWORD"];
55
88
  if (!email || !password) {
56
- console.log(`admin: skipped — set SEED_ADMIN_EMAIL and SEED_ADMIN_PASSWORD to create one (there is no default password)`);
57
- console.log(`admin: "${workspace.name}" has no members until you re-run with them set`);
89
+ console.log(
90
+ `admin: skipped set SEED_ADMIN_EMAIL and SEED_ADMIN_PASSWORD to create one (there is no default password)`,
91
+ );
92
+ console.log(
93
+ `admin: "${workspace.name}" has no members until you re-run with them set`,
94
+ );
58
95
  return;
59
96
  }
60
97
 
61
98
  // Rewriting the password here would silently reset one the user may have already changed.
62
99
  const existing = await prisma.user.findUnique({ where: { email } });
63
- const user = existing ?? (await prisma.user.create({ data: { email, passwordHash: await hashPassword(password) } }));
64
- console.log(existing ? `admin: ${email} already exists — password left unchanged` : `admin: created ${email}`);
100
+ const user =
101
+ existing ??
102
+ (await prisma.user.create({
103
+ data: { email, passwordHash: await hashPassword(password) },
104
+ }));
105
+ console.log(
106
+ existing
107
+ ? `admin: ${email} already exists — password left unchanged`
108
+ : `admin: created ${email}`,
109
+ );
65
110
 
66
111
  const membership = await prisma.workspaceMember.upsert({
67
- where: { userId_workspaceId: { userId: user.id, workspaceId: workspace.id } },
112
+ where: {
113
+ userId_workspaceId: { userId: user.id, workspaceId: workspace.id },
114
+ },
68
115
  create: { userId: user.id, workspaceId: workspace.id },
69
116
  update: {},
70
117
  });
71
- const roles = await prisma.role.findMany({ where: { workspaceId: workspace.id, slug: { in: WORKSPACE_CREATOR_ROLES } }, select: { id: true, slug: true } });
72
- await prisma.roleMember.createMany({ data: roles.map((role) => ({ memberId: membership.id, roleId: role.id })), skipDuplicates: true });
73
- console.log(`admin: member of "${workspace.name}" with roles ${roles.map((r) => r.slug).join(", ")}`);
118
+ const roles = await prisma.role.findMany({
119
+ where: { workspaceId: workspace.id, slug: { in: WORKSPACE_CREATOR_ROLES } },
120
+ select: { id: true, slug: true },
121
+ });
122
+ await prisma.roleMember.createMany({
123
+ data: roles.map((role) => ({ memberId: membership.id, roleId: role.id })),
124
+ skipDuplicates: true,
125
+ });
126
+ console.log(
127
+ `admin: member of "${workspace.name}" with roles ${roles.map((r) => r.slug).join(", ")}`,
128
+ );
74
129
  }
75
130
 
76
- async function seedSuperAdminUser(prisma: PrismaClient, workspace: { id: bigint; name: string }): Promise<void> {
131
+ async function seedSuperAdminUser(
132
+ prisma: PrismaClient,
133
+ workspace: { id: bigint; name: string },
134
+ ): Promise<void> {
77
135
  const email = process.env["SEED_SUPERADMIN_EMAIL"];
78
136
  const password = process.env["SEED_SUPERADMIN_PASSWORD"];
79
137
  if (!email || !password) {
80
- console.log(`super admin: skipped — set SEED_SUPERADMIN_EMAIL and SEED_SUPERADMIN_PASSWORD to create one (there is no default password)`);
138
+ console.log(
139
+ `super admin: skipped — set SEED_SUPERADMIN_EMAIL and SEED_SUPERADMIN_PASSWORD to create one (there is no default password)`,
140
+ );
81
141
  return;
82
142
  }
83
143
  const username = process.env["SEED_SUPERADMIN_USERNAME"] || undefined;
84
144
 
85
145
  // Rewriting the password here would silently reset one the user may have already changed.
86
146
  const existing = await prisma.user.findUnique({ where: { email } });
87
- const user = existing ?? (await prisma.user.create({ data: { email, username, passwordHash: await hashPassword(password) } }));
88
- console.log(existing ? `super admin: ${email} already exists — password left unchanged` : `super admin: created ${email}${username ? ` (username ${username})` : ""}`);
147
+ const user =
148
+ existing ??
149
+ (await prisma.user.create({
150
+ data: { email, username, passwordHash: await hashPassword(password) },
151
+ }));
152
+ console.log(
153
+ existing
154
+ ? `super admin: ${email} already exists — password left unchanged`
155
+ : `super admin: created ${email}${username ? ` (username ${username})` : ""}`,
156
+ );
89
157
 
90
158
  const membership = await prisma.workspaceMember.upsert({
91
- where: { userId_workspaceId: { userId: user.id, workspaceId: workspace.id } },
159
+ where: {
160
+ userId_workspaceId: { userId: user.id, workspaceId: workspace.id },
161
+ },
92
162
  create: { userId: user.id, workspaceId: workspace.id },
93
163
  update: {},
94
164
  });
95
- const roles = await prisma.role.findMany({ where: { workspaceId: workspace.id, slug: { in: SEED_SUPERADMIN_ROLES } }, select: { id: true, slug: true } });
96
- await prisma.roleMember.createMany({ data: roles.map((role) => ({ memberId: membership.id, roleId: role.id })), skipDuplicates: true });
97
- console.log(`super admin: member of "${workspace.name}" with roles ${roles.map((r) => r.slug).join(", ")}`);
165
+ const roles = await prisma.role.findMany({
166
+ where: { workspaceId: workspace.id, slug: { in: SEED_SUPERADMIN_ROLES } },
167
+ select: { id: true, slug: true },
168
+ });
169
+ await prisma.roleMember.createMany({
170
+ data: roles.map((role) => ({ memberId: membership.id, roleId: role.id })),
171
+ skipDuplicates: true,
172
+ });
173
+ console.log(
174
+ `super admin: member of "${workspace.name}" with roles ${roles.map((r) => r.slug).join(", ")}`,
175
+ );
98
176
  }
99
177
 
100
178
  async function main(): Promise<void> {
@@ -104,7 +182,9 @@ async function main(): Promise<void> {
104
182
  // No .env file — the environment is expected to carry the variables itself.
105
183
  }
106
184
 
107
- const prisma = new PrismaClient({ adapter: new PrismaPg({ connectionString: requireEnv("DATABASE_URL") }) });
185
+ const prisma = new PrismaClient({
186
+ adapter: new PrismaPg({ connectionString: requireEnv("DATABASE_URL") }),
187
+ });
108
188
  try {
109
189
  const workspace = await seedWorkspace(prisma);
110
190
  await seedRbacDefaults(prisma, workspace.id);