@simple-auth-kit/cli 1.4.2 → 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.
- 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 +57 -22
- 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 +67 -19
- 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 +78 -20
- 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 +81 -26
- 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 +73 -21
- 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 +109 -29
- 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 +59 -28
- 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 +98 -27
- 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 +78 -20
- 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 +55 -27
- 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 +104 -29
- 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 +109 -29
- package/registry/combos/nestjs-prisma/variants/workspaces/src/services/auth.service.ts +961 -0
- package/registry.json +6 -4
- package/simple-auth-kit.ts +582 -124
- 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,8 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
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 "
|
|
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({
|
|
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({
|
|
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: {
|
|
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: {
|
|
55
|
-
|
|
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: {
|
|
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(
|
|
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
|
-
? {
|
|
85
|
-
|
|
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(
|
|
89
|
-
|
|
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(
|
|
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({
|
|
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({
|
|
101
|
-
|
|
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: {
|
|
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({
|
|
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: {
|
|
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({
|
|
170
|
-
|
|
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 "
|
|
15
|
-
import {
|
|
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)
|
|
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(
|
|
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({
|
|
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(
|
|
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)
|
|
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({
|
|
46
|
-
|
|
47
|
-
|
|
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)
|
|
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(
|
|
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(
|
|
57
|
-
|
|
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 =
|
|
64
|
-
|
|
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: {
|
|
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({
|
|
72
|
-
|
|
73
|
-
|
|
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(
|
|
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(
|
|
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 =
|
|
88
|
-
|
|
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: {
|
|
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({
|
|
96
|
-
|
|
97
|
-
|
|
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({
|
|
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);
|