@simple-auth-kit/cli 1.5.0 → 1.6.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/{ability.ts → ability/ability.ts} +8 -2
- package/registry/combos/express-drizzle/shared/src/{permission-cache.ts → cache/permission-cache.ts} +25 -9
- package/registry/combos/express-drizzle/shared/src/{rate-limit.store.ts → cache/rate-limit.store.ts} +4 -1
- package/registry/combos/{express-prisma/shared/src → express-drizzle/shared/src/config}/auth.config.ts +1 -1
- package/registry/combos/express-drizzle/shared/src/{db.ts → config/db.ts} +1 -1
- package/registry/combos/express-drizzle/shared/src/{key-provider.ts → config/key-provider.ts} +6 -2
- package/registry/combos/express-drizzle/shared/src/{id.helper.ts → helpers/id.helper.ts} +6 -2
- package/registry/combos/{nestjs-drizzle/shared/src → express-drizzle/shared/src/helpers}/pagination.ts +17 -3
- package/registry/combos/express-drizzle/shared/src/middleware/auth-core-error.middleware.ts +6 -8
- package/registry/combos/express-drizzle/shared/src/middleware/auth.middleware.ts +6 -8
- package/registry/combos/{express-prisma/shared/src → express-drizzle/shared/src/openapi}/openapi-fragment.ts +13 -4
- package/registry/combos/express-drizzle/shared/src/{openapi-spec.ts → openapi/openapi-spec.ts} +251 -52
- package/registry/combos/express-drizzle/shared/src/repositories/oauth.repository.ts +2 -2
- package/registry/combos/express-drizzle/shared/src/repositories/password-reset.repository.ts +2 -4
- package/registry/combos/express-drizzle/shared/src/repositories/session.repository.ts +3 -3
- package/registry/combos/express-drizzle/shared/src/request-context.ts +1 -1
- package/registry/combos/express-drizzle/shared/src/route-tiers.ts +52 -11
- package/registry/combos/express-drizzle/shared/src/routers/auth.router.ts +2 -2
- package/registry/combos/express-drizzle/test/bootstrap.ts +4 -2
- package/registry/combos/express-drizzle/test/prove-cycle.ts +1095 -308
- package/registry/combos/express-drizzle/variants/base/src/create-auth-app.ts +5 -5
- package/registry/combos/express-drizzle/variants/base/src/index.ts +8 -0
- package/registry/combos/express-drizzle/variants/base/src/middleware/authz.middleware.ts +2 -2
- package/registry/combos/express-drizzle/variants/base/src/openapi/openapi-admin.ts +1082 -0
- package/registry/combos/express-drizzle/variants/base/src/rbac.defaults.ts +60 -17
- package/registry/combos/express-drizzle/variants/base/src/repositories/audit-log.repository.ts +2 -2
- package/registry/combos/express-drizzle/variants/base/src/repositories/rbac.repository.ts +5 -5
- package/registry/combos/express-drizzle/variants/base/src/routers/admin.router.ts +18 -22
- package/registry/combos/express-drizzle/variants/base/src/seed.ts +76 -17
- package/registry/combos/express-drizzle/variants/base/src/services/auth.service.ts +7 -9
- package/registry/combos/express-drizzle/variants/base/test/variant-hooks.ts +167 -45
- package/registry/combos/express-drizzle/variants/workspaces/src/create-auth-app.ts +5 -5
- package/registry/combos/express-drizzle/variants/workspaces/src/index.ts +8 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/middleware/authz.middleware.ts +3 -3
- package/registry/combos/express-drizzle/variants/workspaces/src/openapi/openapi-admin.ts +1176 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/openapi-workspace.ts +1 -1
- package/registry/combos/express-drizzle/variants/workspaces/src/rbac.defaults.ts +66 -19
- package/registry/combos/express-drizzle/variants/workspaces/src/repositories/audit-log.repository.ts +2 -2
- package/registry/combos/express-drizzle/variants/workspaces/src/repositories/rbac.repository.ts +5 -5
- package/registry/combos/express-drizzle/variants/workspaces/src/repositories/workspace.repository.ts +9 -11
- package/registry/combos/express-drizzle/variants/workspaces/src/routers/admin.router.ts +18 -22
- package/registry/combos/express-drizzle/variants/workspaces/src/routers/workspace.router.ts +1 -1
- package/registry/combos/express-drizzle/variants/workspaces/src/seed.ts +168 -35
- package/registry/combos/express-drizzle/variants/workspaces/src/services/auth.service.ts +7 -9
- package/registry/combos/express-drizzle/variants/workspaces/test/variant-hooks.ts +539 -133
- package/registry/combos/express-prisma/shared/src/{ability.ts → ability/ability.ts} +8 -2
- package/registry/combos/express-prisma/shared/src/{permission-cache.ts → cache/permission-cache.ts} +25 -9
- package/registry/combos/express-prisma/shared/src/{rate-limit.store.ts → cache/rate-limit.store.ts} +4 -1
- package/registry/combos/{express-drizzle/shared/src → express-prisma/shared/src/config}/auth.config.ts +1 -1
- package/registry/combos/express-prisma/shared/src/{key-provider.ts → config/key-provider.ts} +6 -2
- package/registry/combos/{nestjs-prisma/shared/src → express-prisma/shared/src/helpers}/id.helper.ts +6 -2
- package/registry/combos/{express-drizzle/shared/src → express-prisma/shared/src/helpers}/pagination.ts +17 -3
- package/registry/combos/express-prisma/shared/src/middleware/auth-core-error.middleware.ts +6 -8
- package/registry/combos/express-prisma/shared/src/middleware/auth.middleware.ts +6 -8
- package/registry/combos/{express-drizzle/shared/src → express-prisma/shared/src/openapi}/openapi-fragment.ts +13 -4
- package/registry/combos/express-prisma/shared/src/{openapi-spec.ts → openapi/openapi-spec.ts} +313 -55
- package/registry/combos/express-prisma/shared/src/repositories/oauth.repository.ts +2 -2
- package/registry/combos/express-prisma/shared/src/repositories/password-reset.repository.ts +2 -4
- package/registry/combos/express-prisma/shared/src/repositories/session.repository.ts +2 -2
- package/registry/combos/express-prisma/shared/src/request-context.ts +1 -1
- package/registry/combos/express-prisma/shared/src/route-tiers.ts +52 -11
- package/registry/combos/express-prisma/shared/src/routers/auth.router.ts +2 -2
- package/registry/combos/express-prisma/test/bootstrap.ts +7 -3
- package/registry/combos/express-prisma/test/prove-cycle.ts +1095 -308
- package/registry/combos/express-prisma/variants/base/src/create-auth-app.ts +5 -5
- package/registry/combos/express-prisma/variants/base/src/index.ts +8 -0
- package/registry/combos/express-prisma/variants/base/src/middleware/authz.middleware.ts +2 -2
- package/registry/combos/express-prisma/variants/base/src/openapi/openapi-admin.ts +1102 -0
- package/registry/combos/express-prisma/variants/base/src/repositories/audit-log.repository.ts +2 -2
- package/registry/combos/express-prisma/variants/base/src/repositories/rbac.repository.ts +4 -4
- package/registry/combos/express-prisma/variants/base/src/routers/admin.router.ts +18 -22
- package/registry/combos/express-prisma/variants/base/src/services/auth.service.ts +6 -8
- package/registry/combos/express-prisma/variants/base/test/variant-hooks.ts +176 -47
- package/registry/combos/express-prisma/variants/workspaces/src/create-auth-app.ts +5 -5
- package/registry/combos/express-prisma/variants/workspaces/src/index.ts +8 -0
- package/registry/combos/express-prisma/variants/workspaces/src/middleware/authz.middleware.ts +3 -3
- package/registry/combos/express-prisma/variants/workspaces/src/openapi/openapi-admin.ts +1218 -0
- package/registry/combos/express-prisma/variants/workspaces/src/openapi-workspace.ts +1 -1
- package/registry/combos/express-prisma/variants/workspaces/src/repositories/audit-log.repository.ts +2 -2
- package/registry/combos/express-prisma/variants/workspaces/src/repositories/rbac.repository.ts +4 -4
- package/registry/combos/express-prisma/variants/workspaces/src/repositories/workspace.repository.ts +2 -2
- package/registry/combos/express-prisma/variants/workspaces/src/routers/admin.router.ts +18 -22
- package/registry/combos/express-prisma/variants/workspaces/src/routers/workspace.router.ts +1 -1
- package/registry/combos/express-prisma/variants/workspaces/src/services/auth.service.ts +6 -8
- package/registry/combos/express-prisma/variants/workspaces/test/variant-hooks.ts +538 -134
- package/registry/combos/nestjs-drizzle/shared/src/{guards → ability}/ability.guard.ts +1 -1
- package/registry/combos/nestjs-drizzle/shared/src/{ability.ts → ability/ability.ts} +8 -2
- package/registry/combos/nestjs-drizzle/shared/src/{permission-cache.ts → cache/permission-cache.ts} +27 -10
- package/registry/combos/{nestjs-prisma/shared/src → nestjs-drizzle/shared/src/cache}/rate-limit.store.ts +4 -1
- package/registry/combos/nestjs-drizzle/shared/src/{auth.config.ts → config/auth.config.ts} +1 -1
- package/registry/combos/nestjs-drizzle/shared/src/{db.ts → config/db.ts} +1 -1
- package/registry/combos/nestjs-drizzle/shared/src/{key-provider.ts → config/key-provider.ts} +6 -2
- package/registry/combos/nestjs-drizzle/shared/src/controllers/auth.controller.ts +1 -1
- package/registry/combos/nestjs-drizzle/shared/src/guards/auth.guard.ts +1 -1
- package/registry/combos/nestjs-drizzle/shared/src/{id.helper.ts → helpers/id.helper.ts} +6 -2
- package/registry/combos/{express-prisma/shared/src → nestjs-drizzle/shared/src/helpers}/pagination.ts +17 -3
- package/registry/combos/nestjs-drizzle/shared/src/repositories/oauth.repository.ts +2 -2
- package/registry/combos/nestjs-drizzle/shared/src/repositories/password-reset.repository.ts +8 -12
- package/registry/combos/nestjs-drizzle/shared/src/repositories/session.repository.ts +3 -3
- package/registry/combos/nestjs-drizzle/shared/src/repositories/two-factor.repository.ts +1 -1
- package/registry/combos/nestjs-drizzle/shared/src/request-context.ts +1 -1
- package/registry/combos/nestjs-drizzle/test/bootstrap.ts +1 -1
- package/registry/combos/nestjs-drizzle/test/prove-cycle.ts +1 -1
- package/registry/combos/nestjs-drizzle/variants/base/src/auth.module.ts +10 -6
- package/registry/combos/nestjs-drizzle/variants/base/src/controllers/admin.controller.ts +1 -1
- package/registry/combos/nestjs-drizzle/variants/base/src/guards/authz.guard.ts +2 -2
- package/registry/combos/nestjs-drizzle/variants/base/src/index.ts +9 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/rbac.defaults.ts +58 -16
- package/registry/combos/nestjs-drizzle/variants/base/src/repositories/audit-log.repository.ts +3 -3
- package/registry/combos/nestjs-drizzle/variants/base/src/repositories/rbac.repository.ts +4 -4
- package/registry/combos/nestjs-drizzle/variants/base/src/seed.ts +76 -17
- package/registry/combos/nestjs-drizzle/variants/base/src/services/auth.service.ts +7 -9
- package/registry/combos/nestjs-drizzle/variants/base/test/variant-hooks.ts +167 -45
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/auth.module.ts +10 -6
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/controllers/admin.controller.ts +1 -1
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/controllers/workspace.controller.ts +1 -1
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/guards/authz.guard.ts +2 -2
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/index.ts +9 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/rbac.defaults.ts +64 -18
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/repositories/audit-log.repository.ts +3 -3
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/repositories/rbac.repository.ts +4 -4
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/repositories/workspace.repository.ts +8 -10
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/seed.ts +171 -36
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/services/auth.service.ts +7 -9
- package/registry/combos/nestjs-drizzle/variants/workspaces/test/variant-hooks.ts +539 -133
- package/registry/combos/nestjs-prisma/shared/src/{guards → ability}/ability.guard.ts +1 -1
- package/registry/combos/nestjs-prisma/shared/src/{ability.ts → ability/ability.ts} +8 -2
- package/registry/combos/nestjs-prisma/shared/src/{permission-cache.ts → cache/permission-cache.ts} +27 -10
- package/registry/combos/{nestjs-drizzle/shared/src → nestjs-prisma/shared/src/cache}/rate-limit.store.ts +4 -1
- package/registry/combos/nestjs-prisma/shared/src/{auth.config.ts → config/auth.config.ts} +1 -1
- package/registry/combos/nestjs-prisma/shared/src/{key-provider.ts → config/key-provider.ts} +6 -2
- package/registry/combos/nestjs-prisma/shared/src/controllers/auth.controller.ts +1 -1
- package/registry/combos/nestjs-prisma/shared/src/guards/auth.guard.ts +1 -1
- package/registry/combos/{express-prisma/shared/src → nestjs-prisma/shared/src/helpers}/id.helper.ts +6 -2
- package/registry/combos/nestjs-prisma/shared/src/{pagination.ts → helpers/pagination.ts} +17 -3
- package/registry/combos/nestjs-prisma/shared/src/repositories/oauth.repository.ts +1 -1
- package/registry/combos/nestjs-prisma/shared/src/repositories/password-reset.repository.ts +2 -4
- package/registry/combos/nestjs-prisma/shared/src/repositories/session.repository.ts +2 -2
- package/registry/combos/nestjs-prisma/shared/src/request-context.ts +1 -1
- package/registry/combos/nestjs-prisma/test/bootstrap.ts +1 -1
- package/registry/combos/nestjs-prisma/test/prove-cycle.ts +1 -1
- package/registry/combos/nestjs-prisma/variants/base/src/auth.module.ts +9 -5
- package/registry/combos/nestjs-prisma/variants/base/src/controllers/admin.controller.ts +1 -1
- package/registry/combos/nestjs-prisma/variants/base/src/gateways/audit-log.gateway.ts +1 -1
- package/registry/combos/nestjs-prisma/variants/base/src/guards/authz.guard.ts +2 -2
- package/registry/combos/nestjs-prisma/variants/base/src/index.ts +9 -0
- package/registry/combos/nestjs-prisma/variants/base/src/{docs.ts → openapi/docs.ts} +23 -6
- package/registry/combos/nestjs-prisma/variants/base/src/repositories/audit-log.repository.ts +2 -2
- package/registry/combos/nestjs-prisma/variants/base/src/repositories/country.repository.ts +2 -2
- package/registry/combos/nestjs-prisma/variants/base/src/repositories/customer.repository.ts +6 -5
- package/registry/combos/nestjs-prisma/variants/base/src/repositories/language.repository.ts +2 -2
- package/registry/combos/nestjs-prisma/variants/base/src/repositories/rbac.repository.ts +3 -3
- package/registry/combos/nestjs-prisma/variants/base/src/services/auth.service.ts +6 -8
- package/registry/combos/nestjs-prisma/variants/base/test/variant-hooks.ts +176 -47
- package/registry/combos/nestjs-prisma/variants/workspaces/src/auth.module.ts +9 -5
- package/registry/combos/nestjs-prisma/variants/workspaces/src/controllers/admin.controller.ts +1 -1
- package/registry/combos/nestjs-prisma/variants/workspaces/src/controllers/workspace.controller.ts +1 -1
- package/registry/combos/nestjs-prisma/variants/workspaces/src/guards/authz.guard.ts +2 -2
- package/registry/combos/nestjs-prisma/variants/workspaces/src/index.ts +9 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/audit-log.repository.ts +2 -2
- package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/country.repository.ts +2 -2
- package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/customer.repository.ts +6 -5
- package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/language.repository.ts +2 -2
- package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/rbac.repository.ts +3 -3
- package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/workspace.repository.ts +1 -1
- package/registry/combos/nestjs-prisma/variants/workspaces/src/services/auth.service.ts +6 -8
- package/registry/combos/nestjs-prisma/variants/workspaces/test/variant-hooks.ts +538 -134
- package/simple-auth-kit.ts +13 -9
- package/registry/combos/express-drizzle/variants/base/src/openapi-admin.ts +0 -565
- package/registry/combos/express-drizzle/variants/workspaces/src/openapi-admin.ts +0 -596
- package/registry/combos/express-prisma/variants/base/src/openapi-admin.ts +0 -569
- package/registry/combos/express-prisma/variants/workspaces/src/openapi-admin.ts +0 -601
- /package/registry/combos/express-drizzle/shared/src/{http-error.ts → errors/http-error.ts} +0 -0
- /package/registry/combos/express-prisma/shared/src/{http-error.ts → errors/http-error.ts} +0 -0
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
missingPermission,
|
|
7
7
|
requiresPermission,
|
|
8
8
|
type OpenApiFragment,
|
|
9
|
-
} from "./openapi-fragment.js";
|
|
9
|
+
} from "./openapi/openapi-fragment.js";
|
|
10
10
|
import { WORKSPACE_HEADER } from "./middleware/authz.middleware.js";
|
|
11
11
|
|
|
12
12
|
/** Every route that acts *inside* a workspace names it with this header, never a path segment. */
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
// same transaction that creates it, so its creator is never locked out of a workspace they just
|
|
30
30
|
// made).
|
|
31
31
|
import { and, eq, inArray } from "drizzle-orm";
|
|
32
|
-
import type { Database } from "./db.js";
|
|
32
|
+
import type { Database } from "./config/db.js";
|
|
33
33
|
import { permissionRole, permissions, roles } from "./schema.js";
|
|
34
34
|
|
|
35
35
|
/** The display metadata a seeded permission carries. `group`/`order` exist so an admin console can render a stable matrix. */
|
|
@@ -55,11 +55,22 @@ export interface PermissionSeed {
|
|
|
55
55
|
* evaluate.
|
|
56
56
|
*/
|
|
57
57
|
export const PERMISSION_CATALOG = {
|
|
58
|
-
"users:read": {
|
|
59
|
-
|
|
58
|
+
"users:read": {
|
|
59
|
+
displayName: "List members",
|
|
60
|
+
description: "GET /auth/admin/users",
|
|
61
|
+
group: "Users",
|
|
62
|
+
order: 1,
|
|
63
|
+
},
|
|
64
|
+
"users:block": {
|
|
65
|
+
displayName: "Block and unblock members",
|
|
66
|
+
description: "POST /auth/admin/users/:userId/block, .../unblock",
|
|
67
|
+
group: "Users",
|
|
68
|
+
order: 2,
|
|
69
|
+
},
|
|
60
70
|
"users:manage": {
|
|
61
71
|
displayName: "Edit and delete members",
|
|
62
|
-
description:
|
|
72
|
+
description:
|
|
73
|
+
"Edit a member's profile or delete their account — PATCH /auth/admin/users/:userId, DELETE /auth/admin/users/:userId",
|
|
63
74
|
group: "Users",
|
|
64
75
|
order: 3,
|
|
65
76
|
},
|
|
@@ -72,30 +83,44 @@ export const PERMISSION_CATALOG = {
|
|
|
72
83
|
},
|
|
73
84
|
"roles:assign": {
|
|
74
85
|
displayName: "Assign roles",
|
|
75
|
-
description:
|
|
86
|
+
description:
|
|
87
|
+
"Assign and revoke a member's roles — POST /auth/admin/users/:userId/roles, .../roles/:roleSlug/revoke, PUT /workspaces/members/:memberId/roles",
|
|
76
88
|
group: "Roles",
|
|
77
89
|
order: 2,
|
|
78
90
|
},
|
|
79
|
-
"permissions:read": {
|
|
91
|
+
"permissions:read": {
|
|
92
|
+
displayName: "Read the permission catalog",
|
|
93
|
+
description: "GET /auth/admin/permissions",
|
|
94
|
+
group: "Permissions",
|
|
95
|
+
order: 1,
|
|
96
|
+
},
|
|
80
97
|
"permissions:define": {
|
|
81
98
|
displayName: "Define permissions",
|
|
82
|
-
description:
|
|
99
|
+
description:
|
|
100
|
+
"Create a permission, rename it, or deactivate it — POST /auth/admin/permissions. The authority that defines all the others.",
|
|
83
101
|
group: "Permissions",
|
|
84
102
|
order: 2,
|
|
85
103
|
},
|
|
86
104
|
"permissions:grant": {
|
|
87
105
|
displayName: "Grant permissions directly",
|
|
88
|
-
description:
|
|
106
|
+
description:
|
|
107
|
+
"Grant and revoke a permission straight to a member, bypassing roles — POST /auth/admin/users/:userId/permissions, .../permissions/:slug/revoke",
|
|
89
108
|
group: "Permissions",
|
|
90
109
|
order: 3,
|
|
91
110
|
},
|
|
92
111
|
"members:manage": {
|
|
93
112
|
displayName: "Add and remove members",
|
|
94
|
-
description:
|
|
113
|
+
description:
|
|
114
|
+
"POST /workspaces/members, DELETE /workspaces/members/:memberId",
|
|
95
115
|
group: "Members",
|
|
96
116
|
order: 1,
|
|
97
117
|
},
|
|
98
|
-
"audit-log:read": {
|
|
118
|
+
"audit-log:read": {
|
|
119
|
+
displayName: "Read the audit log",
|
|
120
|
+
description: "GET /auth/admin/audit-log",
|
|
121
|
+
group: "Audit",
|
|
122
|
+
order: 1,
|
|
123
|
+
},
|
|
99
124
|
} as const satisfies Record<string, PermissionSeed>;
|
|
100
125
|
|
|
101
126
|
/**
|
|
@@ -107,10 +132,14 @@ export const PERMISSION_CATALOG = {
|
|
|
107
132
|
*/
|
|
108
133
|
export type PermissionSlug = keyof typeof PERMISSION_CATALOG;
|
|
109
134
|
|
|
110
|
-
export const PERMISSION_SLUGS = Object.keys(
|
|
135
|
+
export const PERMISSION_SLUGS = Object.keys(
|
|
136
|
+
PERMISSION_CATALOG,
|
|
137
|
+
) as PermissionSlug[];
|
|
111
138
|
|
|
112
139
|
/** Groups, in the order an admin console should render them. Derived from the catalog so it cannot drift from it. */
|
|
113
|
-
export const PERMISSION_GROUP_ORDER: string[] = [
|
|
140
|
+
export const PERMISSION_GROUP_ORDER: string[] = [
|
|
141
|
+
...new Set(PERMISSION_SLUGS.map((slug) => PERMISSION_CATALOG[slug].group)),
|
|
142
|
+
];
|
|
114
143
|
|
|
115
144
|
/** The display metadata a seeded role carries, plus what it starts out granting. */
|
|
116
145
|
export interface RoleSeed {
|
|
@@ -146,7 +175,8 @@ export const DEFAULT_ROLES: readonly RoleSeed[] = [
|
|
|
146
175
|
{
|
|
147
176
|
slug: "superadmin",
|
|
148
177
|
displayName: "Super Admin",
|
|
149
|
-
description:
|
|
178
|
+
description:
|
|
179
|
+
"Carries every permission in the catalog — same authority as admin, held by the seeded super_admin account's membership (see SEED_SUPERADMIN_* in seed.ts).",
|
|
150
180
|
isDefault: false,
|
|
151
181
|
order: 1,
|
|
152
182
|
permissions: PERMISSION_SLUGS,
|
|
@@ -154,7 +184,8 @@ export const DEFAULT_ROLES: readonly RoleSeed[] = [
|
|
|
154
184
|
{
|
|
155
185
|
slug: "member",
|
|
156
186
|
displayName: "Member",
|
|
157
|
-
description:
|
|
187
|
+
description:
|
|
188
|
+
"The default for a new membership. Carries no administrative permission.",
|
|
158
189
|
isDefault: true,
|
|
159
190
|
order: 2,
|
|
160
191
|
permissions: [],
|
|
@@ -191,7 +222,10 @@ export type RbacWriter = Pick<Database, "insert" | "select">;
|
|
|
191
222
|
*/
|
|
192
223
|
// Takes bigint directly: both callers (`WorkspaceRepository.create`, `seed.ts`) already have the
|
|
193
224
|
// workspace row's id in hand from their own database call.
|
|
194
|
-
export async function provisionDefaultRoles(
|
|
225
|
+
export async function provisionDefaultRoles(
|
|
226
|
+
db: RbacWriter,
|
|
227
|
+
workspaceId: bigint,
|
|
228
|
+
): Promise<void> {
|
|
195
229
|
// Insert-then-read rather than `.returning()`: ON CONFLICT DO NOTHING returns no row for the
|
|
196
230
|
// slugs that already existed, and the ids of *those* are exactly the ones a re-run needs.
|
|
197
231
|
await db
|
|
@@ -203,12 +237,17 @@ export async function provisionDefaultRoles(db: RbacWriter, workspaceId: bigint)
|
|
|
203
237
|
displayName: PERMISSION_CATALOG[slug].displayName,
|
|
204
238
|
description: PERMISSION_CATALOG[slug].description,
|
|
205
239
|
group: PERMISSION_CATALOG[slug].group,
|
|
206
|
-
groupOrder: PERMISSION_GROUP_ORDER.indexOf(
|
|
240
|
+
groupOrder: PERMISSION_GROUP_ORDER.indexOf(
|
|
241
|
+
PERMISSION_CATALOG[slug].group,
|
|
242
|
+
),
|
|
207
243
|
order: PERMISSION_CATALOG[slug].order,
|
|
208
244
|
})),
|
|
209
245
|
)
|
|
210
246
|
.onConflictDoNothing({ target: permissions.slug });
|
|
211
|
-
const permissionRows = await db
|
|
247
|
+
const permissionRows = await db
|
|
248
|
+
.select({ id: permissions.id, slug: permissions.slug })
|
|
249
|
+
.from(permissions)
|
|
250
|
+
.where(inArray(permissions.slug, PERMISSION_SLUGS));
|
|
212
251
|
const permissionId = new Map(permissionRows.map((p) => [p.slug, p.id]));
|
|
213
252
|
|
|
214
253
|
await db
|
|
@@ -240,9 +279,17 @@ export async function provisionDefaultRoles(db: RbacWriter, workspaceId: bigint)
|
|
|
240
279
|
const seedBySlug = new Map(DEFAULT_ROLES.map((role) => [role.slug, role]));
|
|
241
280
|
|
|
242
281
|
const pairs = roleRows.flatMap((role) =>
|
|
243
|
-
(seedBySlug.get(role.slug)?.permissions ?? []).map((slug) => ({
|
|
282
|
+
(seedBySlug.get(role.slug)?.permissions ?? []).map((slug) => ({
|
|
283
|
+
roleId: role.id,
|
|
284
|
+
permissionId: permissionId.get(slug)!,
|
|
285
|
+
})),
|
|
244
286
|
);
|
|
245
287
|
if (pairs.length) {
|
|
246
|
-
await db
|
|
288
|
+
await db
|
|
289
|
+
.insert(permissionRole)
|
|
290
|
+
.values(pairs)
|
|
291
|
+
.onConflictDoNothing({
|
|
292
|
+
target: [permissionRole.permissionId, permissionRole.roleId],
|
|
293
|
+
});
|
|
247
294
|
}
|
|
248
295
|
}
|
package/registry/combos/express-drizzle/variants/workspaces/src/repositories/audit-log.repository.ts
CHANGED
|
@@ -6,9 +6,9 @@ import {
|
|
|
6
6
|
normalizeLimit,
|
|
7
7
|
normalizePage,
|
|
8
8
|
type Paginated,
|
|
9
|
-
} from "../pagination.js";
|
|
9
|
+
} from "../helpers/pagination.js";
|
|
10
10
|
import * as schema from "../schema.js";
|
|
11
|
-
import { toIdOrNull, toIdOrUndefined } from "../id.helper.js";
|
|
11
|
+
import { toIdOrNull, toIdOrUndefined } from "../helpers/id.helper.js";
|
|
12
12
|
|
|
13
13
|
export interface AuditLogEntry {
|
|
14
14
|
id: string;
|
package/registry/combos/express-drizzle/variants/workspaces/src/repositories/rbac.repository.ts
CHANGED
|
@@ -10,14 +10,14 @@ import {
|
|
|
10
10
|
} from "drizzle-orm";
|
|
11
11
|
import { resolvePermissions } from "@/lib/auth/core/rbac.js";
|
|
12
12
|
import type { AuthzContext } from "../middleware/authz.middleware.js";
|
|
13
|
-
import type { Database } from "../db.js";
|
|
13
|
+
import type { Database } from "../config/db.js";
|
|
14
14
|
import {
|
|
15
15
|
buildPageMeta,
|
|
16
16
|
normalizeLimit,
|
|
17
17
|
normalizePage,
|
|
18
18
|
type Paginated,
|
|
19
|
-
} from "../pagination.js";
|
|
20
|
-
import { PermissionCache } from "../permission-cache.js";
|
|
19
|
+
} from "../helpers/pagination.js";
|
|
20
|
+
import { PermissionCache } from "../cache/permission-cache.js";
|
|
21
21
|
import {
|
|
22
22
|
permissionMember,
|
|
23
23
|
permissionRole,
|
|
@@ -27,8 +27,8 @@ import {
|
|
|
27
27
|
users,
|
|
28
28
|
workspaceMembers,
|
|
29
29
|
} from "../schema.js";
|
|
30
|
-
import { HttpError } from "../http-error.js";
|
|
31
|
-
import { toId, toIdOrNull } from "../id.helper.js";
|
|
30
|
+
import { HttpError } from "../errors/http-error.js";
|
|
31
|
+
import { toId, toIdOrNull } from "../helpers/id.helper.js";
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* Every column the `users` table has (plus the membership's own `memberId`/`createdAt`), except
|
package/registry/combos/express-drizzle/variants/workspaces/src/repositories/workspace.repository.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { and, asc, eq, inArray } from "drizzle-orm";
|
|
2
|
-
import type { Database } from "../db.js";
|
|
2
|
+
import type { Database } from "../config/db.js";
|
|
3
3
|
import {
|
|
4
4
|
provisionDefaultRoles,
|
|
5
5
|
WORKSPACE_CREATOR_ROLES,
|
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
workspaceMembers,
|
|
13
13
|
workspaces,
|
|
14
14
|
} from "../schema.js";
|
|
15
|
-
import { HttpError } from "../http-error.js";
|
|
16
|
-
import { toId } from "../id.helper.js";
|
|
15
|
+
import { HttpError } from "../errors/http-error.js";
|
|
16
|
+
import { toId } from "../helpers/id.helper.js";
|
|
17
17
|
|
|
18
18
|
export interface WorkspaceSummary {
|
|
19
19
|
id: string;
|
|
@@ -71,14 +71,12 @@ export class WorkspaceRepository {
|
|
|
71
71
|
.values({ workspaceId: created.id, userId: userIdBig })
|
|
72
72
|
.returning({ id: workspaceMembers.id });
|
|
73
73
|
if (creatorRoles.length) {
|
|
74
|
-
await tx
|
|
75
|
-
.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
})),
|
|
81
|
-
);
|
|
74
|
+
await tx.insert(roleMember).values(
|
|
75
|
+
creatorRoles.map((role) => ({
|
|
76
|
+
memberId: member.id,
|
|
77
|
+
roleId: role.id,
|
|
78
|
+
})),
|
|
79
|
+
);
|
|
82
80
|
}
|
|
83
81
|
return created;
|
|
84
82
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { NextFunction, Request, RequestHandler, Response } from "express";
|
|
2
2
|
import { hashPassword } from "@/lib/auth/core/crypto.js";
|
|
3
3
|
import { AuthService } from "../services/auth.service.js";
|
|
4
|
-
import { HttpError } from "../http-error.js";
|
|
4
|
+
import { HttpError } from "../errors/http-error.js";
|
|
5
5
|
import { ability, createTieredRouter } from "../route-tiers.js";
|
|
6
6
|
import { WorkspaceRepository } from "../repositories/workspace.repository.js";
|
|
7
7
|
import "../request-context.js";
|
|
@@ -65,15 +65,13 @@ export function createAdminRouter(deps: AdminRouterDeps): RequestHandler {
|
|
|
65
65
|
string,
|
|
66
66
|
string | undefined
|
|
67
67
|
>;
|
|
68
|
-
res
|
|
69
|
-
.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}),
|
|
76
|
-
);
|
|
68
|
+
res.status(200).json(
|
|
69
|
+
await auth.listUsers(req.authz!, {
|
|
70
|
+
search,
|
|
71
|
+
page: page ? Number(page) : undefined,
|
|
72
|
+
limit: limit ? Number(limit) : undefined,
|
|
73
|
+
}),
|
|
74
|
+
);
|
|
77
75
|
} catch (err) {
|
|
78
76
|
next(err);
|
|
79
77
|
}
|
|
@@ -200,18 +198,16 @@ export function createAdminRouter(deps: AdminRouterDeps): RequestHandler {
|
|
|
200
198
|
try {
|
|
201
199
|
const { userId, action, since, until, page, limit } =
|
|
202
200
|
req.query as Record<string, string | undefined>;
|
|
203
|
-
res
|
|
204
|
-
.
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}),
|
|
214
|
-
);
|
|
201
|
+
res.status(200).json(
|
|
202
|
+
await auth.listAuditLog(req.authz!, {
|
|
203
|
+
userId,
|
|
204
|
+
action,
|
|
205
|
+
since,
|
|
206
|
+
until,
|
|
207
|
+
page: page ? Number(page) : undefined,
|
|
208
|
+
limit: limit ? Number(limit) : undefined,
|
|
209
|
+
}),
|
|
210
|
+
);
|
|
215
211
|
} catch (err) {
|
|
216
212
|
next(err);
|
|
217
213
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Router } from "express";
|
|
2
2
|
import type { NextFunction, Request, RequestHandler, Response } from "express";
|
|
3
|
-
import { HttpError } from "../http-error.js";
|
|
3
|
+
import { HttpError } from "../errors/http-error.js";
|
|
4
4
|
import { RbacRepository } from "../repositories/rbac.repository.js";
|
|
5
5
|
import { ability, authenticated, createTieredRouter } from "../route-tiers.js";
|
|
6
6
|
import { WorkspaceRepository } from "../repositories/workspace.repository.js";
|
|
@@ -31,23 +31,46 @@ import { and, asc, eq, inArray, notInArray } from "drizzle-orm";
|
|
|
31
31
|
import { drizzle } from "drizzle-orm/node-postgres";
|
|
32
32
|
import { Pool } from "pg";
|
|
33
33
|
import { hashPassword } from "@/lib/auth/core/crypto.js";
|
|
34
|
-
import type { Database } from "./db.js";
|
|
35
|
-
import {
|
|
34
|
+
import type { Database } from "./config/db.js";
|
|
35
|
+
import {
|
|
36
|
+
DEFAULT_ROLES,
|
|
37
|
+
PERMISSION_SLUGS,
|
|
38
|
+
SEED_SUPERADMIN_ROLES,
|
|
39
|
+
WORKSPACE_CREATOR_ROLES,
|
|
40
|
+
provisionDefaultRoles,
|
|
41
|
+
} from "./rbac.defaults.js";
|
|
36
42
|
import * as schema from "./schema.js";
|
|
37
|
-
import {
|
|
43
|
+
import {
|
|
44
|
+
permissions,
|
|
45
|
+
roleMember,
|
|
46
|
+
roles,
|
|
47
|
+
users,
|
|
48
|
+
workspaceMembers,
|
|
49
|
+
workspaces,
|
|
50
|
+
} from "./schema.js";
|
|
38
51
|
|
|
39
52
|
const DEFAULT_WORKSPACE_NAME = "Default workspace";
|
|
40
53
|
|
|
41
54
|
function requireEnv(name: string): string {
|
|
42
55
|
const value = process.env[name];
|
|
43
|
-
if (!value)
|
|
56
|
+
if (!value)
|
|
57
|
+
throw new Error(
|
|
58
|
+
`${name} is not set — the seeder cannot reach the database without it`,
|
|
59
|
+
);
|
|
44
60
|
return value;
|
|
45
61
|
}
|
|
46
62
|
|
|
47
63
|
/** Workspace names are not unique in the schema, so re-running adopts the one it made last time rather than making another. */
|
|
48
|
-
async function seedWorkspace(
|
|
64
|
+
async function seedWorkspace(
|
|
65
|
+
db: Database,
|
|
66
|
+
): Promise<{ id: bigint; name: string }> {
|
|
49
67
|
const name = process.env["SEED_WORKSPACE_NAME"] || DEFAULT_WORKSPACE_NAME;
|
|
50
|
-
const [existing] = await db
|
|
68
|
+
const [existing] = await db
|
|
69
|
+
.select()
|
|
70
|
+
.from(workspaces)
|
|
71
|
+
.where(eq(workspaces.name, name))
|
|
72
|
+
.orderBy(asc(workspaces.createdAt))
|
|
73
|
+
.limit(1);
|
|
51
74
|
if (existing) {
|
|
52
75
|
console.log(`workspace: "${name}" already exists (${existing.id})`);
|
|
53
76
|
return existing;
|
|
@@ -58,91 +81,201 @@ async function seedWorkspace(db: Database): Promise<{ id: bigint; name: string }
|
|
|
58
81
|
}
|
|
59
82
|
|
|
60
83
|
/** The catalog and this workspace's roles come from rbac.defaults.ts; this only reports what it wrote. */
|
|
61
|
-
async function seedRbacDefaults(
|
|
84
|
+
async function seedRbacDefaults(
|
|
85
|
+
db: Database,
|
|
86
|
+
workspaceId: bigint,
|
|
87
|
+
): Promise<void> {
|
|
62
88
|
await provisionDefaultRoles(db, workspaceId);
|
|
63
89
|
console.log(`permissions: ${PERMISSION_SLUGS.length} slug(s) in the catalog`);
|
|
64
|
-
for (const role of DEFAULT_ROLES)
|
|
90
|
+
for (const role of DEFAULT_ROLES)
|
|
91
|
+
console.log(
|
|
92
|
+
`role "${role.slug}": ${role.permissions.length} permission(s)${role.isDefault ? " (new-member default)" : ""}`,
|
|
93
|
+
);
|
|
65
94
|
|
|
66
95
|
// Slugs that exist in the database but not in this build's catalog. They are not an error —
|
|
67
96
|
// `POST /auth/admin/permissions` exists so a deployment can define its own, and a grant can
|
|
68
97
|
// create one — but a slug no route names grants nothing, so a typo would otherwise be a
|
|
69
98
|
// permission that mysteriously never works. Printing them is what makes it visible.
|
|
70
|
-
const unknown = await db
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
99
|
+
const unknown = await db
|
|
100
|
+
.select({ slug: permissions.slug })
|
|
101
|
+
.from(permissions)
|
|
102
|
+
.where(notInArray(permissions.slug, PERMISSION_SLUGS));
|
|
103
|
+
if (unknown.length)
|
|
104
|
+
console.log(
|
|
105
|
+
`permissions: ${unknown.length} slug(s) outside this build's catalog (no route names them): ${unknown.map((r) => r.slug).join(", ")}`,
|
|
106
|
+
);
|
|
107
|
+
const inactive = await db
|
|
108
|
+
.select({ slug: permissions.slug })
|
|
109
|
+
.from(permissions)
|
|
110
|
+
.where(eq(permissions.isActive, false));
|
|
111
|
+
if (inactive.length)
|
|
112
|
+
console.log(
|
|
113
|
+
`permissions: ${inactive.length} deactivated, granting nothing: ${inactive.map((r) => r.slug).join(", ")}`,
|
|
114
|
+
);
|
|
74
115
|
}
|
|
75
116
|
|
|
76
117
|
/** The user is the authentication principal; the membership below is the authorization one. */
|
|
77
|
-
async function seedAdminUser(
|
|
118
|
+
async function seedAdminUser(
|
|
119
|
+
db: Database,
|
|
120
|
+
workspace: { id: bigint; name: string },
|
|
121
|
+
): Promise<void> {
|
|
78
122
|
const email = process.env["SEED_ADMIN_EMAIL"];
|
|
79
123
|
const password = process.env["SEED_ADMIN_PASSWORD"];
|
|
80
124
|
if (!email || !password) {
|
|
81
|
-
console.log(
|
|
82
|
-
|
|
125
|
+
console.log(
|
|
126
|
+
`admin: skipped — set SEED_ADMIN_EMAIL and SEED_ADMIN_PASSWORD to create one (there is no default password)`,
|
|
127
|
+
);
|
|
128
|
+
console.log(
|
|
129
|
+
`admin: "${workspace.name}" has no members until you re-run with them set`,
|
|
130
|
+
);
|
|
83
131
|
return;
|
|
84
132
|
}
|
|
85
133
|
|
|
86
134
|
// Their password may have been changed since; rewriting it here would silently reset it.
|
|
87
|
-
const [existing] = await db
|
|
88
|
-
|
|
89
|
-
|
|
135
|
+
const [existing] = await db
|
|
136
|
+
.select({ id: users.id })
|
|
137
|
+
.from(users)
|
|
138
|
+
.where(eq(users.email, email))
|
|
139
|
+
.limit(1);
|
|
140
|
+
const user =
|
|
141
|
+
existing ??
|
|
142
|
+
(
|
|
143
|
+
await db
|
|
144
|
+
.insert(users)
|
|
145
|
+
.values({ email, passwordHash: await hashPassword(password) })
|
|
146
|
+
.returning({ id: users.id })
|
|
147
|
+
)[0];
|
|
148
|
+
console.log(
|
|
149
|
+
existing
|
|
150
|
+
? `admin: ${email} already exists — password left unchanged`
|
|
151
|
+
: `admin: created ${email}`,
|
|
152
|
+
);
|
|
90
153
|
|
|
91
154
|
const [existingMembership] = await db
|
|
92
155
|
.select({ id: workspaceMembers.id })
|
|
93
156
|
.from(workspaceMembers)
|
|
94
|
-
.where(
|
|
157
|
+
.where(
|
|
158
|
+
and(
|
|
159
|
+
eq(workspaceMembers.userId, user.id),
|
|
160
|
+
eq(workspaceMembers.workspaceId, workspace.id),
|
|
161
|
+
),
|
|
162
|
+
)
|
|
95
163
|
.limit(1);
|
|
96
164
|
const membership =
|
|
97
|
-
existingMembership ??
|
|
165
|
+
existingMembership ??
|
|
166
|
+
(
|
|
167
|
+
await db
|
|
168
|
+
.insert(workspaceMembers)
|
|
169
|
+
.values({ userId: user.id, workspaceId: workspace.id })
|
|
170
|
+
.returning({ id: workspaceMembers.id })
|
|
171
|
+
)[0];
|
|
98
172
|
|
|
99
173
|
const adminRoles = await db
|
|
100
174
|
.select({ id: roles.id, slug: roles.slug })
|
|
101
175
|
.from(roles)
|
|
102
|
-
.where(
|
|
176
|
+
.where(
|
|
177
|
+
and(
|
|
178
|
+
eq(roles.workspaceId, workspace.id),
|
|
179
|
+
inArray(roles.slug, WORKSPACE_CREATOR_ROLES),
|
|
180
|
+
),
|
|
181
|
+
);
|
|
103
182
|
if (adminRoles.length) {
|
|
104
183
|
await db
|
|
105
184
|
.insert(roleMember)
|
|
106
|
-
.values(
|
|
107
|
-
|
|
185
|
+
.values(
|
|
186
|
+
adminRoles.map((role) => ({
|
|
187
|
+
memberId: membership.id,
|
|
188
|
+
roleId: role.id,
|
|
189
|
+
})),
|
|
190
|
+
)
|
|
191
|
+
.onConflictDoNothing({
|
|
192
|
+
target: [roleMember.memberId, roleMember.roleId],
|
|
193
|
+
});
|
|
108
194
|
}
|
|
109
|
-
console.log(
|
|
195
|
+
console.log(
|
|
196
|
+
`admin: member of "${workspace.name}" with roles ${adminRoles.map((r) => r.slug).join(", ")}`,
|
|
197
|
+
);
|
|
110
198
|
}
|
|
111
199
|
|
|
112
200
|
/** Same shape as seedAdminUser, for the seeded super_admin account — a distinct account and membership holding the "superadmin" role. */
|
|
113
|
-
async function seedSuperAdminUser(
|
|
201
|
+
async function seedSuperAdminUser(
|
|
202
|
+
db: Database,
|
|
203
|
+
workspace: { id: bigint; name: string },
|
|
204
|
+
): Promise<void> {
|
|
114
205
|
const email = process.env["SEED_SUPERADMIN_EMAIL"];
|
|
115
206
|
const password = process.env["SEED_SUPERADMIN_PASSWORD"];
|
|
116
207
|
if (!email || !password) {
|
|
117
|
-
console.log(
|
|
208
|
+
console.log(
|
|
209
|
+
`super admin: skipped — set SEED_SUPERADMIN_EMAIL and SEED_SUPERADMIN_PASSWORD to create one (there is no default password)`,
|
|
210
|
+
);
|
|
118
211
|
return;
|
|
119
212
|
}
|
|
120
213
|
const username = process.env["SEED_SUPERADMIN_USERNAME"] || undefined;
|
|
121
214
|
|
|
122
215
|
// Their password may have been changed since; rewriting it here would silently reset it.
|
|
123
|
-
const [existing] = await db
|
|
124
|
-
|
|
125
|
-
|
|
216
|
+
const [existing] = await db
|
|
217
|
+
.select({ id: users.id })
|
|
218
|
+
.from(users)
|
|
219
|
+
.where(eq(users.email, email))
|
|
220
|
+
.limit(1);
|
|
221
|
+
const user =
|
|
222
|
+
existing ??
|
|
223
|
+
(
|
|
224
|
+
await db
|
|
225
|
+
.insert(users)
|
|
226
|
+
.values({ email, username, passwordHash: await hashPassword(password) })
|
|
227
|
+
.returning({ id: users.id })
|
|
228
|
+
)[0];
|
|
229
|
+
console.log(
|
|
230
|
+
existing
|
|
231
|
+
? `super admin: ${email} already exists — password left unchanged`
|
|
232
|
+
: `super admin: created ${email}${username ? ` (username ${username})` : ""}`,
|
|
233
|
+
);
|
|
126
234
|
|
|
127
235
|
const [existingMembership] = await db
|
|
128
236
|
.select({ id: workspaceMembers.id })
|
|
129
237
|
.from(workspaceMembers)
|
|
130
|
-
.where(
|
|
238
|
+
.where(
|
|
239
|
+
and(
|
|
240
|
+
eq(workspaceMembers.userId, user.id),
|
|
241
|
+
eq(workspaceMembers.workspaceId, workspace.id),
|
|
242
|
+
),
|
|
243
|
+
)
|
|
131
244
|
.limit(1);
|
|
132
245
|
const membership =
|
|
133
|
-
existingMembership ??
|
|
246
|
+
existingMembership ??
|
|
247
|
+
(
|
|
248
|
+
await db
|
|
249
|
+
.insert(workspaceMembers)
|
|
250
|
+
.values({ userId: user.id, workspaceId: workspace.id })
|
|
251
|
+
.returning({ id: workspaceMembers.id })
|
|
252
|
+
)[0];
|
|
134
253
|
|
|
135
254
|
const superAdminRoles = await db
|
|
136
255
|
.select({ id: roles.id, slug: roles.slug })
|
|
137
256
|
.from(roles)
|
|
138
|
-
.where(
|
|
257
|
+
.where(
|
|
258
|
+
and(
|
|
259
|
+
eq(roles.workspaceId, workspace.id),
|
|
260
|
+
inArray(roles.slug, SEED_SUPERADMIN_ROLES),
|
|
261
|
+
),
|
|
262
|
+
);
|
|
139
263
|
if (superAdminRoles.length) {
|
|
140
264
|
await db
|
|
141
265
|
.insert(roleMember)
|
|
142
|
-
.values(
|
|
143
|
-
|
|
266
|
+
.values(
|
|
267
|
+
superAdminRoles.map((role) => ({
|
|
268
|
+
memberId: membership.id,
|
|
269
|
+
roleId: role.id,
|
|
270
|
+
})),
|
|
271
|
+
)
|
|
272
|
+
.onConflictDoNothing({
|
|
273
|
+
target: [roleMember.memberId, roleMember.roleId],
|
|
274
|
+
});
|
|
144
275
|
}
|
|
145
|
-
console.log(
|
|
276
|
+
console.log(
|
|
277
|
+
`super admin: member of "${workspace.name}" with roles ${superAdminRoles.map((r) => r.slug).join(", ")}`,
|
|
278
|
+
);
|
|
146
279
|
}
|
|
147
280
|
|
|
148
281
|
async function main(): Promise<void> {
|
|
@@ -42,17 +42,17 @@ import {
|
|
|
42
42
|
generateTotpSecret,
|
|
43
43
|
verifyTotpCode,
|
|
44
44
|
} from "@/lib/auth/core/two-factor.js";
|
|
45
|
-
import type { AuthConfig } from "../auth.config.js";
|
|
46
|
-
import { HttpError } from "../http-error.js";
|
|
45
|
+
import type { AuthConfig } from "../config/auth.config.js";
|
|
46
|
+
import { HttpError } from "../errors/http-error.js";
|
|
47
47
|
import {
|
|
48
48
|
AuditLogEntry,
|
|
49
49
|
AuditLogListFilter,
|
|
50
50
|
AuditLogRepository,
|
|
51
51
|
toAuditLogEntry,
|
|
52
52
|
} from "../repositories/audit-log.repository.js";
|
|
53
|
-
import type { Paginated } from "../pagination.js";
|
|
54
|
-
import type { Database } from "../db.js";
|
|
55
|
-
import { KeyProviderService } from "../key-provider.js";
|
|
53
|
+
import type { Paginated } from "../helpers/pagination.js";
|
|
54
|
+
import type { Database } from "../config/db.js";
|
|
55
|
+
import { KeyProviderService } from "../config/key-provider.js";
|
|
56
56
|
import { OAuthRepository } from "../repositories/oauth.repository.js";
|
|
57
57
|
import { PasswordResetRepository } from "../repositories/password-reset.repository.js";
|
|
58
58
|
import type { Revoker } from "@/lib/auth/core/types.js";
|
|
@@ -70,7 +70,7 @@ import {
|
|
|
70
70
|
import { sessions, users } from "../schema.js";
|
|
71
71
|
import { SessionRepository } from "../repositories/session.repository.js";
|
|
72
72
|
import { TwoFactorRepository } from "../repositories/two-factor.repository.js";
|
|
73
|
-
import { toId, toIdOrNull } from "../id.helper.js";
|
|
73
|
+
import { toId, toIdOrNull } from "../helpers/id.helper.js";
|
|
74
74
|
|
|
75
75
|
export interface AuthTokens {
|
|
76
76
|
accessToken: string;
|
|
@@ -470,9 +470,7 @@ export class AuthService {
|
|
|
470
470
|
return { twoFactorEnabled: user.twoFactorEnabled };
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
-
async listActiveSessions(
|
|
474
|
-
userId: string,
|
|
475
|
-
): Promise<
|
|
473
|
+
async listActiveSessions(userId: string): Promise<
|
|
476
474
|
Array<{
|
|
477
475
|
id: string;
|
|
478
476
|
createdAt: string;
|