@simple-auth-kit/cli 1.4.3 → 1.5.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/lib/copy.ts +1 -1
- package/package.json +1 -1
- package/registry/combos/express-drizzle/shared/src/{auth-core-error.middleware.ts → middleware/auth-core-error.middleware.ts} +17 -4
- package/registry/combos/express-drizzle/shared/src/{auth.middleware.ts → middleware/auth.middleware.ts} +23 -7
- package/registry/combos/{express-prisma/shared/src → express-drizzle/shared/src/middleware}/response-envelope.middleware.ts +15 -3
- package/registry/combos/express-drizzle/shared/src/{oauth.repository.ts → repositories/oauth.repository.ts} +32 -9
- package/registry/combos/express-drizzle/shared/src/repositories/password-reset.repository.ts +59 -0
- package/registry/combos/express-drizzle/shared/src/{session.repository.ts → repositories/session.repository.ts} +52 -14
- package/registry/combos/express-drizzle/shared/src/{two-factor.repository.ts → repositories/two-factor.repository.ts} +20 -6
- package/registry/combos/express-drizzle/shared/src/request-context.ts +1 -1
- package/registry/combos/express-drizzle/shared/src/routers/auth.router.ts +395 -0
- package/registry/combos/express-drizzle/variants/base/src/create-auth-app.ts +52 -19
- package/registry/combos/express-drizzle/variants/base/src/{authz.middleware.ts → middleware/authz.middleware.ts} +15 -7
- package/registry/combos/express-drizzle/variants/base/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +28 -10
- package/registry/combos/express-drizzle/variants/base/src/{rbac.repository.ts → repositories/rbac.repository.ts} +239 -48
- package/registry/combos/express-drizzle/variants/base/src/routers/admin.router.ts +497 -0
- package/registry/combos/express-drizzle/variants/base/src/{auth.service.ts → services/auth.service.ts} +480 -109
- package/registry/combos/express-drizzle/variants/workspaces/src/create-auth-app.ts +69 -23
- package/registry/combos/express-drizzle/variants/workspaces/src/{authz.middleware.ts → middleware/authz.middleware.ts} +32 -12
- package/registry/combos/express-drizzle/variants/workspaces/src/openapi-workspace.ts +147 -28
- package/registry/combos/express-drizzle/variants/workspaces/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +30 -11
- package/registry/combos/express-drizzle/variants/workspaces/src/{rbac.repository.ts → repositories/rbac.repository.ts} +319 -70
- package/registry/combos/express-drizzle/variants/workspaces/src/{workspace.repository.ts → repositories/workspace.repository.ts} +127 -30
- package/registry/combos/express-drizzle/variants/workspaces/src/routers/admin.router.ts +534 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/routers/workspace.router.ts +194 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/{auth.service.ts → services/auth.service.ts} +544 -122
- package/registry/combos/express-prisma/shared/src/{auth-core-error.middleware.ts → middleware/auth-core-error.middleware.ts} +17 -4
- package/registry/combos/express-prisma/shared/src/{auth.middleware.ts → middleware/auth.middleware.ts} +19 -6
- package/registry/combos/{express-drizzle/shared/src → express-prisma/shared/src/middleware}/response-envelope.middleware.ts +15 -3
- package/registry/combos/express-prisma/shared/src/{oauth.repository.ts → repositories/oauth.repository.ts} +27 -9
- package/registry/combos/express-prisma/shared/src/repositories/password-reset.repository.ts +55 -0
- package/registry/combos/express-prisma/shared/src/{session.repository.ts → repositories/session.repository.ts} +30 -9
- package/registry/combos/express-prisma/shared/src/{two-factor.repository.ts → repositories/two-factor.repository.ts} +11 -4
- package/registry/combos/express-prisma/shared/src/request-context.ts +1 -1
- package/registry/combos/express-prisma/shared/src/routers/auth.router.ts +398 -0
- package/registry/combos/express-prisma/variants/base/src/create-auth-app.ts +56 -21
- package/registry/combos/express-prisma/variants/base/src/{authz.middleware.ts → middleware/authz.middleware.ts} +15 -7
- package/registry/combos/express-prisma/variants/base/src/rbac.defaults.ts +66 -18
- package/registry/combos/express-prisma/variants/base/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +16 -5
- package/registry/combos/express-prisma/variants/base/src/{rbac.repository.ts → repositories/rbac.repository.ts} +199 -48
- package/registry/combos/express-prisma/variants/base/src/routers/admin.router.ts +508 -0
- package/registry/combos/express-prisma/variants/base/src/seed.ts +77 -19
- package/registry/combos/express-prisma/variants/base/src/{auth.service.ts → services/auth.service.ts} +454 -105
- package/registry/combos/express-prisma/variants/workspaces/src/create-auth-app.ts +80 -25
- package/registry/combos/express-prisma/variants/workspaces/src/{authz.middleware.ts → middleware/authz.middleware.ts} +32 -12
- package/registry/combos/express-prisma/variants/workspaces/src/openapi-workspace.ts +147 -28
- package/registry/combos/express-prisma/variants/workspaces/src/rbac.defaults.ts +72 -20
- package/registry/combos/express-prisma/variants/workspaces/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +16 -5
- package/registry/combos/express-prisma/variants/workspaces/src/{rbac.repository.ts → repositories/rbac.repository.ts} +272 -64
- package/registry/combos/express-prisma/variants/workspaces/src/{workspace.repository.ts → repositories/workspace.repository.ts} +86 -23
- package/registry/combos/express-prisma/variants/workspaces/src/routers/admin.router.ts +545 -0
- package/registry/combos/express-prisma/variants/workspaces/src/routers/workspace.router.ts +194 -0
- package/registry/combos/express-prisma/variants/workspaces/src/seed.ts +108 -28
- package/registry/combos/express-prisma/variants/workspaces/src/{auth.service.ts → services/auth.service.ts} +518 -119
- package/registry/combos/nestjs-drizzle/shared/src/{auth.controller.ts → controllers/auth.controller.ts} +6 -6
- package/registry/combos/nestjs-drizzle/shared/src/{auth-core-error.filter.ts → filters/auth-core-error.filter.ts} +29 -5
- package/registry/combos/nestjs-drizzle/shared/src/{ability.guard.ts → guards/ability.guard.ts} +20 -7
- package/registry/combos/nestjs-drizzle/shared/src/{auth.guard.ts → guards/auth.guard.ts} +19 -7
- package/registry/combos/{nestjs-prisma/shared/src → nestjs-drizzle/shared/src/interceptors}/response.interceptor.ts +10 -2
- package/registry/combos/nestjs-drizzle/shared/src/repositories/oauth.repository.ts +63 -0
- package/registry/combos/nestjs-drizzle/shared/src/repositories/password-reset.repository.ts +59 -0
- package/registry/combos/nestjs-drizzle/shared/src/{session.repository.ts → repositories/session.repository.ts} +52 -14
- package/registry/combos/nestjs-drizzle/shared/src/{two-factor.repository.ts → repositories/two-factor.repository.ts} +21 -7
- package/registry/combos/nestjs-drizzle/shared/src/request-context.ts +1 -1
- package/registry/combos/nestjs-drizzle/test/prove-cycle.ts +1097 -307
- package/registry/combos/nestjs-drizzle/variants/base/src/auth.module.ts +44 -20
- package/registry/combos/nestjs-drizzle/variants/base/src/{admin.controller.ts → controllers/admin.controller.ts} +228 -62
- package/registry/combos/nestjs-drizzle/variants/base/src/{authz.guard.ts → guards/authz.guard.ts} +12 -5
- package/registry/combos/nestjs-drizzle/variants/base/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +27 -10
- package/registry/combos/nestjs-drizzle/variants/base/src/{rbac.repository.ts → repositories/rbac.repository.ts} +249 -51
- package/registry/combos/nestjs-drizzle/variants/base/src/{auth.service.ts → services/auth.service.ts} +499 -116
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/auth.module.ts +48 -22
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/{admin.controller.ts → controllers/admin.controller.ts} +269 -70
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/{workspace.controller.ts → controllers/workspace.controller.ts} +93 -26
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/{authz.guard.ts → guards/authz.guard.ts} +26 -8
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +29 -11
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/{rbac.repository.ts → repositories/rbac.repository.ts} +323 -71
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/{workspace.repository.ts → repositories/workspace.repository.ts} +131 -30
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/services/auth.service.ts +988 -0
- package/registry/combos/nestjs-prisma/shared/src/{auth.controller.ts → controllers/auth.controller.ts} +6 -6
- package/registry/combos/nestjs-prisma/shared/src/{auth-core-error.filter.ts → filters/auth-core-error.filter.ts} +29 -5
- package/registry/combos/nestjs-prisma/shared/src/{ability.guard.ts → guards/ability.guard.ts} +20 -7
- package/registry/combos/nestjs-prisma/shared/src/{auth.guard.ts → guards/auth.guard.ts} +19 -7
- package/registry/combos/{nestjs-drizzle/shared/src → nestjs-prisma/shared/src/interceptors}/response.interceptor.ts +10 -2
- package/registry/combos/nestjs-prisma/shared/src/{oauth.repository.ts → repositories/oauth.repository.ts} +25 -8
- package/registry/combos/nestjs-prisma/shared/src/repositories/password-reset.repository.ts +55 -0
- package/registry/combos/nestjs-prisma/shared/src/{session.repository.ts → repositories/session.repository.ts} +30 -9
- package/registry/combos/nestjs-prisma/shared/src/{two-factor.repository.ts → repositories/two-factor.repository.ts} +11 -4
- package/registry/combos/nestjs-prisma/shared/src/request-context.ts +1 -1
- package/registry/combos/nestjs-prisma/test/prove-cycle.ts +1097 -307
- package/registry/combos/nestjs-prisma/variants/base/src/auth.module.ts +58 -27
- package/registry/combos/nestjs-prisma/variants/base/src/{admin.controller.ts → controllers/admin.controller.ts} +469 -110
- package/registry/combos/nestjs-prisma/variants/base/src/{audit-log.gateway.ts → gateways/audit-log.gateway.ts} +22 -6
- package/registry/combos/nestjs-prisma/variants/base/src/{authz.guard.ts → guards/authz.guard.ts} +12 -5
- package/registry/combos/nestjs-prisma/variants/base/src/rbac.defaults.ts +97 -26
- package/registry/combos/nestjs-prisma/variants/base/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +16 -5
- package/registry/combos/nestjs-prisma/variants/base/src/{country.repository.ts → repositories/country.repository.ts} +98 -25
- package/registry/combos/nestjs-prisma/variants/base/src/{customer.repository.ts → repositories/customer.repository.ts} +106 -27
- package/registry/combos/nestjs-prisma/variants/base/src/{language.repository.ts → repositories/language.repository.ts} +93 -25
- package/registry/combos/nestjs-prisma/variants/base/src/{rbac.repository.ts → repositories/rbac.repository.ts} +229 -55
- package/registry/combos/nestjs-prisma/variants/base/src/seed.ts +77 -19
- package/registry/combos/nestjs-prisma/variants/base/src/services/auth.service.ts +1064 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/auth.module.ts +54 -26
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{admin.controller.ts → controllers/admin.controller.ts} +544 -128
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{workspace.controller.ts → controllers/workspace.controller.ts} +93 -26
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{authz.guard.ts → guards/authz.guard.ts} +26 -8
- package/registry/combos/nestjs-prisma/variants/workspaces/src/rbac.defaults.ts +103 -28
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +16 -5
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{country.repository.ts → repositories/country.repository.ts} +127 -26
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{customer.repository.ts → repositories/customer.repository.ts} +137 -28
- package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/language.repository.ts +243 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{rbac.repository.ts → repositories/rbac.repository.ts} +314 -74
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{workspace.repository.ts → repositories/workspace.repository.ts} +94 -24
- package/registry/combos/nestjs-prisma/variants/workspaces/src/seed.ts +108 -28
- package/registry/combos/nestjs-prisma/variants/workspaces/src/services/auth.service.ts +961 -0
- package/registry.json +4 -4
- package/simple-auth-kit.ts +42 -11
- package/registry/combos/express-drizzle/shared/src/auth.router.ts +0 -266
- package/registry/combos/express-drizzle/shared/src/password-reset.repository.ts +0 -35
- package/registry/combos/express-drizzle/variants/base/src/admin.router.ts +0 -307
- package/registry/combos/express-drizzle/variants/workspaces/src/admin.router.ts +0 -323
- package/registry/combos/express-drizzle/variants/workspaces/src/workspace.router.ts +0 -123
- package/registry/combos/express-prisma/shared/src/auth.router.ts +0 -269
- package/registry/combos/express-prisma/shared/src/password-reset.repository.ts +0 -29
- package/registry/combos/express-prisma/variants/base/src/admin.router.ts +0 -314
- package/registry/combos/express-prisma/variants/workspaces/src/admin.router.ts +0 -330
- package/registry/combos/express-prisma/variants/workspaces/src/workspace.router.ts +0 -123
- package/registry/combos/nestjs-drizzle/shared/src/oauth.repository.ts +0 -36
- package/registry/combos/nestjs-drizzle/shared/src/password-reset.repository.ts +0 -29
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/auth.service.ts +0 -553
- package/registry/combos/nestjs-prisma/shared/src/password-reset.repository.ts +0 -29
- package/registry/combos/nestjs-prisma/variants/base/src/auth.service.ts +0 -618
- package/registry/combos/nestjs-prisma/variants/workspaces/src/auth.service.ts +0 -509
- package/registry/combos/nestjs-prisma/variants/workspaces/src/language.repository.ts +0 -143
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { and, asc, eq, inArray } from "drizzle-orm";
|
|
2
|
-
import type { Database } from "
|
|
3
|
-
import {
|
|
2
|
+
import type { Database } from "../db.js";
|
|
3
|
+
import {
|
|
4
|
+
provisionDefaultRoles,
|
|
5
|
+
WORKSPACE_CREATOR_ROLES,
|
|
6
|
+
} from "../rbac.defaults.js";
|
|
4
7
|
import { RbacRepository } from "./rbac.repository.js";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
import {
|
|
9
|
+
roleMember,
|
|
10
|
+
roles,
|
|
11
|
+
users,
|
|
12
|
+
workspaceMembers,
|
|
13
|
+
workspaces,
|
|
14
|
+
} from "../schema.js";
|
|
15
|
+
import { HttpError } from "../http-error.js";
|
|
16
|
+
import { toId } from "../id.helper.js";
|
|
8
17
|
|
|
9
18
|
export interface WorkspaceSummary {
|
|
10
19
|
id: string;
|
|
@@ -42,16 +51,34 @@ export class WorkspaceRepository {
|
|
|
42
51
|
async create(userId: string, name: string): Promise<WorkspaceSummary> {
|
|
43
52
|
const userIdBig = toId(userId);
|
|
44
53
|
const workspace = await this.db.transaction(async (tx) => {
|
|
45
|
-
const [created] = await tx
|
|
54
|
+
const [created] = await tx
|
|
55
|
+
.insert(workspaces)
|
|
56
|
+
.values({ name, createdBy: userIdBig, updatedBy: userIdBig })
|
|
57
|
+
.returning();
|
|
46
58
|
await provisionDefaultRoles(tx, created.id);
|
|
47
59
|
|
|
48
60
|
const creatorRoles = await tx
|
|
49
61
|
.select({ id: roles.id })
|
|
50
62
|
.from(roles)
|
|
51
|
-
.where(
|
|
52
|
-
|
|
63
|
+
.where(
|
|
64
|
+
and(
|
|
65
|
+
eq(roles.workspaceId, created.id),
|
|
66
|
+
inArray(roles.slug, WORKSPACE_CREATOR_ROLES),
|
|
67
|
+
),
|
|
68
|
+
);
|
|
69
|
+
const [member] = await tx
|
|
70
|
+
.insert(workspaceMembers)
|
|
71
|
+
.values({ workspaceId: created.id, userId: userIdBig })
|
|
72
|
+
.returning({ id: workspaceMembers.id });
|
|
53
73
|
if (creatorRoles.length) {
|
|
54
|
-
await tx
|
|
74
|
+
await tx
|
|
75
|
+
.insert(roleMember)
|
|
76
|
+
.values(
|
|
77
|
+
creatorRoles.map((role) => ({
|
|
78
|
+
memberId: member.id,
|
|
79
|
+
roleId: role.id,
|
|
80
|
+
})),
|
|
81
|
+
);
|
|
55
82
|
}
|
|
56
83
|
return created;
|
|
57
84
|
});
|
|
@@ -73,11 +100,19 @@ export class WorkspaceRepository {
|
|
|
73
100
|
createdAt: workspaces.createdAt,
|
|
74
101
|
})
|
|
75
102
|
.from(workspaceMembers)
|
|
76
|
-
.innerJoin(
|
|
103
|
+
.innerJoin(
|
|
104
|
+
workspaces,
|
|
105
|
+
and(
|
|
106
|
+
eq(workspaces.id, workspaceMembers.workspaceId),
|
|
107
|
+
eq(workspaces.isDeleted, false),
|
|
108
|
+
),
|
|
109
|
+
)
|
|
77
110
|
.where(eq(workspaceMembers.userId, toId(userId)))
|
|
78
111
|
.orderBy(asc(workspaceMembers.createdAt));
|
|
79
112
|
|
|
80
|
-
const rolesByMember = await this.rolesByMember(
|
|
113
|
+
const rolesByMember = await this.rolesByMember(
|
|
114
|
+
memberships.map((m) => m.memberId),
|
|
115
|
+
);
|
|
81
116
|
return memberships.map((m) => ({
|
|
82
117
|
id: m.id.toString(),
|
|
83
118
|
name: m.name,
|
|
@@ -95,7 +130,10 @@ export class WorkspaceRepository {
|
|
|
95
130
|
email: users.email,
|
|
96
131
|
})
|
|
97
132
|
.from(workspaceMembers)
|
|
98
|
-
.innerJoin(
|
|
133
|
+
.innerJoin(
|
|
134
|
+
users,
|
|
135
|
+
and(eq(users.id, workspaceMembers.userId), eq(users.isDeleted, false)),
|
|
136
|
+
)
|
|
99
137
|
.where(eq(workspaceMembers.workspaceId, toId(workspaceId)))
|
|
100
138
|
.orderBy(asc(workspaceMembers.createdAt));
|
|
101
139
|
|
|
@@ -115,17 +153,33 @@ export class WorkspaceRepository {
|
|
|
115
153
|
* Shared by `addMember` (an existing account) and `createMember` (a brand-new one), so both
|
|
116
154
|
* fail the same way against an unknown role slug.
|
|
117
155
|
*/
|
|
118
|
-
private async resolveMemberRoles(
|
|
156
|
+
private async resolveMemberRoles(
|
|
157
|
+
workspaceIdBig: bigint,
|
|
158
|
+
roleSlugs?: string[],
|
|
159
|
+
): Promise<Array<{ id: bigint; slug: string }>> {
|
|
119
160
|
const granted = await this.db
|
|
120
161
|
.select({ id: roles.id, slug: roles.slug })
|
|
121
162
|
.from(roles)
|
|
122
163
|
.where(
|
|
123
164
|
roleSlugs
|
|
124
|
-
? and(
|
|
125
|
-
|
|
165
|
+
? and(
|
|
166
|
+
eq(roles.workspaceId, workspaceIdBig),
|
|
167
|
+
inArray(roles.slug, roleSlugs),
|
|
168
|
+
)
|
|
169
|
+
: and(
|
|
170
|
+
eq(roles.workspaceId, workspaceIdBig),
|
|
171
|
+
eq(roles.isDefault, true),
|
|
172
|
+
eq(roles.isActive, true),
|
|
173
|
+
),
|
|
174
|
+
);
|
|
175
|
+
const unknown = (roleSlugs ?? []).filter(
|
|
176
|
+
(slug) => !granted.some((role) => role.slug === slug),
|
|
177
|
+
);
|
|
178
|
+
if (unknown.length)
|
|
179
|
+
throw new HttpError(
|
|
180
|
+
404,
|
|
181
|
+
`role(s) not defined in this workspace: ${unknown.join(", ")}`,
|
|
126
182
|
);
|
|
127
|
-
const unknown = (roleSlugs ?? []).filter((slug) => !granted.some((role) => role.slug === slug));
|
|
128
|
-
if (unknown.length) throw new HttpError(404, `role(s) not defined in this workspace: ${unknown.join(", ")}`);
|
|
129
183
|
return granted;
|
|
130
184
|
}
|
|
131
185
|
|
|
@@ -134,23 +188,45 @@ export class WorkspaceRepository {
|
|
|
134
188
|
* consuming app's job. With no roles named, the new membership gets whichever of this
|
|
135
189
|
* workspace's roles are flagged `isDefault`, not a slug spelled in code.
|
|
136
190
|
*/
|
|
137
|
-
async addMember(
|
|
191
|
+
async addMember(
|
|
192
|
+
workspaceId: string,
|
|
193
|
+
email: string,
|
|
194
|
+
roleSlugs?: string[],
|
|
195
|
+
): Promise<MembershipSummary> {
|
|
138
196
|
const workspaceIdBig = toId(workspaceId);
|
|
139
|
-
const [user] = await this.db
|
|
197
|
+
const [user] = await this.db
|
|
198
|
+
.select({ id: users.id, email: users.email })
|
|
199
|
+
.from(users)
|
|
200
|
+
.where(eq(users.email, email))
|
|
201
|
+
.limit(1);
|
|
140
202
|
if (!user) throw new HttpError(404, "no user with that email");
|
|
141
203
|
|
|
142
204
|
const [existing] = await this.db
|
|
143
205
|
.select({ id: workspaceMembers.id })
|
|
144
206
|
.from(workspaceMembers)
|
|
145
|
-
.where(
|
|
207
|
+
.where(
|
|
208
|
+
and(
|
|
209
|
+
eq(workspaceMembers.userId, user.id),
|
|
210
|
+
eq(workspaceMembers.workspaceId, workspaceIdBig),
|
|
211
|
+
),
|
|
212
|
+
)
|
|
146
213
|
.limit(1);
|
|
147
|
-
if (existing)
|
|
214
|
+
if (existing)
|
|
215
|
+
throw new HttpError(409, "already a member of this workspace");
|
|
148
216
|
|
|
149
217
|
const granted = await this.resolveMemberRoles(workspaceIdBig, roleSlugs);
|
|
150
218
|
|
|
151
219
|
const member = await this.db.transaction(async (tx) => {
|
|
152
|
-
const [created] = await tx
|
|
153
|
-
|
|
220
|
+
const [created] = await tx
|
|
221
|
+
.insert(workspaceMembers)
|
|
222
|
+
.values({ workspaceId: workspaceIdBig, userId: user.id })
|
|
223
|
+
.returning();
|
|
224
|
+
if (granted.length)
|
|
225
|
+
await tx
|
|
226
|
+
.insert(roleMember)
|
|
227
|
+
.values(
|
|
228
|
+
granted.map((role) => ({ memberId: created.id, roleId: role.id })),
|
|
229
|
+
);
|
|
154
230
|
return created;
|
|
155
231
|
});
|
|
156
232
|
|
|
@@ -183,7 +259,11 @@ export class WorkspaceRepository {
|
|
|
183
259
|
actorUserId: string | null,
|
|
184
260
|
): Promise<MembershipSummary> {
|
|
185
261
|
const workspaceIdBig = toId(workspaceId);
|
|
186
|
-
const [existing] = await this.db
|
|
262
|
+
const [existing] = await this.db
|
|
263
|
+
.select({ id: users.id })
|
|
264
|
+
.from(users)
|
|
265
|
+
.where(eq(users.email, input.email))
|
|
266
|
+
.limit(1);
|
|
187
267
|
if (existing) throw new HttpError(409, "email already registered");
|
|
188
268
|
|
|
189
269
|
const granted = await this.resolveMemberRoles(workspaceIdBig, input.roles);
|
|
@@ -202,8 +282,16 @@ export class WorkspaceRepository {
|
|
|
202
282
|
createdBy: actorUserId ? toId(actorUserId) : null,
|
|
203
283
|
})
|
|
204
284
|
.returning();
|
|
205
|
-
const [created] = await tx
|
|
206
|
-
|
|
285
|
+
const [created] = await tx
|
|
286
|
+
.insert(workspaceMembers)
|
|
287
|
+
.values({ workspaceId: workspaceIdBig, userId: user.id })
|
|
288
|
+
.returning();
|
|
289
|
+
if (granted.length)
|
|
290
|
+
await tx
|
|
291
|
+
.insert(roleMember)
|
|
292
|
+
.values(
|
|
293
|
+
granted.map((role) => ({ memberId: created.id, roleId: role.id })),
|
|
294
|
+
);
|
|
207
295
|
return { member: created, user };
|
|
208
296
|
});
|
|
209
297
|
|
|
@@ -220,23 +308,32 @@ export class WorkspaceRepository {
|
|
|
220
308
|
const workspaceIdBig = toId(workspaceId);
|
|
221
309
|
const memberIdBig = toId(memberId);
|
|
222
310
|
const [member] = await this.db
|
|
223
|
-
.select({
|
|
311
|
+
.select({
|
|
312
|
+
id: workspaceMembers.id,
|
|
313
|
+
userId: workspaceMembers.userId,
|
|
314
|
+
workspaceId: workspaceMembers.workspaceId,
|
|
315
|
+
})
|
|
224
316
|
.from(workspaceMembers)
|
|
225
317
|
.where(eq(workspaceMembers.id, memberIdBig))
|
|
226
318
|
.limit(1);
|
|
227
|
-
if (!member || member.workspaceId !== workspaceIdBig)
|
|
319
|
+
if (!member || member.workspaceId !== workspaceIdBig)
|
|
320
|
+
throw new HttpError(404, "member not found in this workspace");
|
|
228
321
|
|
|
229
322
|
// Role assignments and direct grants belong to the membership, so they go with it — that is
|
|
230
323
|
// the point of hanging them off the member row. `onDelete: "cascade"` on both join tables is
|
|
231
324
|
// what makes that a database guarantee rather than two deletes someone has to remember.
|
|
232
|
-
await this.db
|
|
325
|
+
await this.db
|
|
326
|
+
.delete(workspaceMembers)
|
|
327
|
+
.where(eq(workspaceMembers.id, memberIdBig));
|
|
233
328
|
// A removed member must lose access on their very next request, so their entry has to become
|
|
234
329
|
// unreachable — the resolution that replaces it returns "not a member", which is never cached.
|
|
235
330
|
await this.rbac.invalidateMember(member.userId.toString(), workspaceId);
|
|
236
331
|
}
|
|
237
332
|
|
|
238
333
|
/** One read for a page's role assignments rather than one per membership. */
|
|
239
|
-
private async rolesByMember(
|
|
334
|
+
private async rolesByMember(
|
|
335
|
+
memberIds: bigint[],
|
|
336
|
+
): Promise<Map<string, string[]>> {
|
|
240
337
|
if (!memberIds.length) return new Map();
|
|
241
338
|
const rows = await this.db
|
|
242
339
|
.select({ memberId: roleMember.memberId, slug: roles.slug })
|