@jskit-ai/users-core 0.1.41 → 0.1.43
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.descriptor.mjs +6 -6
- package/package.json +6 -6
- package/src/server/accountProfile/avatarStorageService.js +3 -3
- package/src/server/common/contributors/workspaceRouteVisibilityResolver.js +12 -13
- package/src/server/common/formatters/workspaceFormatter.js +3 -2
- package/src/server/common/repositories/repositoryUtils.js +12 -3
- package/src/server/common/repositories/userSettingsRepository.js +35 -11
- package/src/server/common/repositories/usersRepository.js +44 -27
- package/src/server/common/repositories/workspaceInvitesRepository.js +49 -13
- package/src/server/common/repositories/workspaceMembershipsRepository.js +55 -22
- package/src/server/common/repositories/workspacesRepository.js +41 -11
- package/src/server/common/services/accountContextService.js +3 -2
- package/src/server/common/services/authProfileSyncService.js +7 -5
- package/src/server/common/services/workspaceContextService.js +4 -1
- package/src/server/common/support/realtimeServiceEvents.js +4 -3
- package/src/server/common/validators/authenticatedUserValidator.js +5 -4
- package/src/server/consoleSettings/consoleService.js +3 -3
- package/src/server/consoleSettings/consoleSettingsRepository.js +10 -6
- package/src/server/usersBootstrapContributor.js +7 -3
- package/src/server/workspaceBootstrapContributor.js +5 -1
- package/src/server/workspaceMembers/registerWorkspaceMembers.js +6 -4
- package/src/server/workspaceMembers/workspaceMembersService.js +23 -11
- package/src/server/workspacePendingInvitations/registerWorkspacePendingInvitations.js +5 -4
- package/src/server/workspacePendingInvitations/workspacePendingInvitationsService.js +3 -2
- package/src/server/workspaceSettings/workspaceSettingsRepository.js +29 -10
- package/src/server/workspaceSettings/workspaceSettingsService.js +3 -2
- package/src/shared/resources/workspaceMembersResource.js +25 -21
- package/src/shared/resources/workspacePendingInvitationsResource.js +7 -12
- package/src/shared/resources/workspaceResource.js +13 -9
- package/src/shared/resources/workspaceSettingsResource.js +7 -5
- package/templates/migrations/users_core_console_owner.cjs +1 -1
- package/templates/migrations/users_core_generic_initial.cjs +4 -4
- package/templates/migrations/users_core_profile_username.cjs +1 -1
- package/test/authProfileSyncService.test.js +7 -4
- package/test/avatarStorageService.test.js +3 -3
- package/test/consoleService.test.js +9 -9
- package/test/registerServiceRealtimeEvents.test.js +9 -9
- package/test/repositoryContracts.test.js +40 -0
- package/test/usersBootstrapContributor.test.js +4 -4
- package/test/workspaceBootstrapContributor.test.js +1 -1
- package/test/workspaceInvitesRepository.test.js +3 -3
- package/test/workspaceMembersService.test.js +34 -34
- package/test/workspacePendingInvitationsResource.test.js +4 -4
- package/test/workspacePendingInvitationsService.test.js +11 -11
- package/test/workspaceRouteVisibilityResolver.test.js +6 -6
- package/test/workspaceService.test.js +33 -33
- package/test/workspaceSettingsRepository.test.js +7 -6
- package/test/workspaceSettingsResource.test.js +2 -2
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
normalizeLowerText,
|
|
3
|
+
normalizeRecordId,
|
|
4
|
+
normalizeDbRecordId,
|
|
3
5
|
normalizeText,
|
|
4
6
|
toIsoString,
|
|
5
7
|
nowDb,
|
|
6
|
-
isDuplicateEntryError
|
|
8
|
+
isDuplicateEntryError,
|
|
9
|
+
createWithTransaction
|
|
7
10
|
} from "./repositoryUtils.js";
|
|
8
11
|
import { OWNER_ROLE_ID } from "../../../shared/roles.js";
|
|
9
12
|
|
|
@@ -13,9 +16,9 @@ function mapRow(row) {
|
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
return {
|
|
16
|
-
id:
|
|
17
|
-
workspaceId:
|
|
18
|
-
userId:
|
|
19
|
+
id: normalizeDbRecordId(row.id, { fallback: "" }),
|
|
20
|
+
workspaceId: normalizeDbRecordId(row.workspace_id, { fallback: "" }),
|
|
21
|
+
userId: normalizeDbRecordId(row.user_id, { fallback: "" }),
|
|
19
22
|
roleSid: normalizeLowerText(row.role_sid || "member") || "member",
|
|
20
23
|
status: normalizeLowerText(row.status || "active") || "active",
|
|
21
24
|
createdAt: toIsoString(row.created_at),
|
|
@@ -29,7 +32,7 @@ function mapMemberSummaryRow(row) {
|
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
return {
|
|
32
|
-
userId:
|
|
35
|
+
userId: normalizeDbRecordId(row.user_id, { fallback: "" }),
|
|
33
36
|
roleSid: normalizeLowerText(row.role_sid || "member") || "member",
|
|
34
37
|
status: normalizeLowerText(row.status || "active") || "active",
|
|
35
38
|
displayName: normalizeText(row.display_name),
|
|
@@ -41,35 +44,48 @@ function createRepository(knex) {
|
|
|
41
44
|
if (typeof knex !== "function") {
|
|
42
45
|
throw new TypeError("workspaceMembershipsRepository requires knex.");
|
|
43
46
|
}
|
|
47
|
+
const withTransaction = createWithTransaction(knex);
|
|
44
48
|
|
|
45
49
|
async function findByWorkspaceIdAndUserId(workspaceId, userId, options = {}) {
|
|
50
|
+
const normalizedWorkspaceId = normalizeRecordId(workspaceId, { fallback: null });
|
|
51
|
+
const normalizedUserId = normalizeRecordId(userId, { fallback: null });
|
|
52
|
+
if (!normalizedWorkspaceId || !normalizedUserId) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
46
56
|
const client = options?.trx || knex;
|
|
47
57
|
const row = await client("workspace_memberships")
|
|
48
|
-
.where({ workspace_id:
|
|
58
|
+
.where({ workspace_id: normalizedWorkspaceId, user_id: normalizedUserId })
|
|
49
59
|
.first();
|
|
50
60
|
return mapRow(row);
|
|
51
61
|
}
|
|
52
62
|
|
|
53
63
|
async function ensureOwnerMembership(workspaceId, userId, options = {}) {
|
|
64
|
+
const normalizedWorkspaceId = normalizeRecordId(workspaceId, { fallback: null });
|
|
65
|
+
const normalizedUserId = normalizeRecordId(userId, { fallback: null });
|
|
66
|
+
if (!normalizedWorkspaceId || !normalizedUserId) {
|
|
67
|
+
throw new TypeError("workspaceMembershipsRepository.ensureOwnerMembership requires workspaceId and userId.");
|
|
68
|
+
}
|
|
69
|
+
|
|
54
70
|
const client = options?.trx || knex;
|
|
55
|
-
const existing = await findByWorkspaceIdAndUserId(
|
|
71
|
+
const existing = await findByWorkspaceIdAndUserId(normalizedWorkspaceId, normalizedUserId, { trx: client });
|
|
56
72
|
if (existing) {
|
|
57
73
|
if (existing.roleSid !== OWNER_ROLE_ID || existing.status !== "active") {
|
|
58
74
|
await client("workspace_memberships")
|
|
59
|
-
.where({ workspace_id:
|
|
75
|
+
.where({ workspace_id: normalizedWorkspaceId, user_id: normalizedUserId })
|
|
60
76
|
.update({
|
|
61
77
|
role_sid: OWNER_ROLE_ID,
|
|
62
78
|
status: "active",
|
|
63
79
|
updated_at: nowDb()
|
|
64
80
|
});
|
|
65
81
|
}
|
|
66
|
-
return findByWorkspaceIdAndUserId(
|
|
82
|
+
return findByWorkspaceIdAndUserId(normalizedWorkspaceId, normalizedUserId, { trx: client });
|
|
67
83
|
}
|
|
68
84
|
|
|
69
85
|
try {
|
|
70
86
|
await client("workspace_memberships").insert({
|
|
71
|
-
workspace_id:
|
|
72
|
-
user_id:
|
|
87
|
+
workspace_id: normalizedWorkspaceId,
|
|
88
|
+
user_id: normalizedUserId,
|
|
73
89
|
role_sid: OWNER_ROLE_ID,
|
|
74
90
|
status: "active",
|
|
75
91
|
created_at: nowDb(),
|
|
@@ -81,43 +97,54 @@ function createRepository(knex) {
|
|
|
81
97
|
}
|
|
82
98
|
}
|
|
83
99
|
|
|
84
|
-
return findByWorkspaceIdAndUserId(
|
|
100
|
+
return findByWorkspaceIdAndUserId(normalizedWorkspaceId, normalizedUserId, { trx: client });
|
|
85
101
|
}
|
|
86
102
|
|
|
87
103
|
async function upsertMembership(workspaceId, userId, patch = {}, options = {}) {
|
|
104
|
+
const normalizedWorkspaceId = normalizeRecordId(workspaceId, { fallback: null });
|
|
105
|
+
const normalizedUserId = normalizeRecordId(userId, { fallback: null });
|
|
106
|
+
if (!normalizedWorkspaceId || !normalizedUserId) {
|
|
107
|
+
throw new TypeError("workspaceMembershipsRepository.upsertMembership requires workspaceId and userId.");
|
|
108
|
+
}
|
|
109
|
+
|
|
88
110
|
const client = options?.trx || knex;
|
|
89
|
-
const existing = await findByWorkspaceIdAndUserId(
|
|
111
|
+
const existing = await findByWorkspaceIdAndUserId(normalizedWorkspaceId, normalizedUserId, { trx: client });
|
|
90
112
|
const roleSid = normalizeLowerText(patch.roleSid || existing?.roleSid || "member") || "member";
|
|
91
113
|
const status = normalizeLowerText(patch.status || existing?.status || "active") || "active";
|
|
92
114
|
|
|
93
115
|
if (!existing) {
|
|
94
116
|
await client("workspace_memberships").insert({
|
|
95
|
-
workspace_id:
|
|
96
|
-
user_id:
|
|
117
|
+
workspace_id: normalizedWorkspaceId,
|
|
118
|
+
user_id: normalizedUserId,
|
|
97
119
|
role_sid: roleSid,
|
|
98
120
|
status,
|
|
99
121
|
created_at: nowDb(),
|
|
100
122
|
updated_at: nowDb()
|
|
101
123
|
});
|
|
102
|
-
return findByWorkspaceIdAndUserId(
|
|
124
|
+
return findByWorkspaceIdAndUserId(normalizedWorkspaceId, normalizedUserId, { trx: client });
|
|
103
125
|
}
|
|
104
126
|
|
|
105
127
|
await client("workspace_memberships")
|
|
106
|
-
.where({ workspace_id:
|
|
128
|
+
.where({ workspace_id: normalizedWorkspaceId, user_id: normalizedUserId })
|
|
107
129
|
.update({
|
|
108
130
|
role_sid: roleSid,
|
|
109
131
|
status,
|
|
110
132
|
updated_at: nowDb()
|
|
111
133
|
});
|
|
112
134
|
|
|
113
|
-
return findByWorkspaceIdAndUserId(
|
|
135
|
+
return findByWorkspaceIdAndUserId(normalizedWorkspaceId, normalizedUserId, { trx: client });
|
|
114
136
|
}
|
|
115
137
|
|
|
116
138
|
async function listActiveByWorkspaceId(workspaceId, options = {}) {
|
|
139
|
+
const normalizedWorkspaceId = normalizeRecordId(workspaceId, { fallback: null });
|
|
140
|
+
if (!normalizedWorkspaceId) {
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
|
|
117
144
|
const client = options?.trx || knex;
|
|
118
145
|
const rows = await client("workspace_memberships as wm")
|
|
119
146
|
.join("users as up", "up.id", "wm.user_id")
|
|
120
|
-
.where({ "wm.workspace_id":
|
|
147
|
+
.where({ "wm.workspace_id": normalizedWorkspaceId, "wm.status": "active" })
|
|
121
148
|
.orderBy("up.display_name", "asc")
|
|
122
149
|
.select([
|
|
123
150
|
"wm.user_id",
|
|
@@ -131,21 +158,27 @@ function createRepository(knex) {
|
|
|
131
158
|
}
|
|
132
159
|
|
|
133
160
|
async function listActiveWorkspaceIdsByUserId(userId, options = {}) {
|
|
161
|
+
const normalizedUserId = normalizeRecordId(userId, { fallback: null });
|
|
162
|
+
if (!normalizedUserId) {
|
|
163
|
+
return [];
|
|
164
|
+
}
|
|
165
|
+
|
|
134
166
|
const client = options?.trx || knex;
|
|
135
167
|
const rows = await client("workspace_memberships")
|
|
136
168
|
.where({
|
|
137
|
-
user_id:
|
|
169
|
+
user_id: normalizedUserId,
|
|
138
170
|
status: "active"
|
|
139
171
|
})
|
|
140
172
|
.select("workspace_id")
|
|
141
173
|
.orderBy("workspace_id", "asc");
|
|
142
174
|
|
|
143
175
|
return rows
|
|
144
|
-
.map((row) =>
|
|
145
|
-
.filter(
|
|
176
|
+
.map((row) => normalizeDbRecordId(row.workspace_id, { fallback: null }))
|
|
177
|
+
.filter(Boolean);
|
|
146
178
|
}
|
|
147
179
|
|
|
148
180
|
return Object.freeze({
|
|
181
|
+
withTransaction,
|
|
149
182
|
findByWorkspaceIdAndUserId,
|
|
150
183
|
ensureOwnerMembership,
|
|
151
184
|
upsertMembership,
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import { resolveInsertedRecordId } from "@jskit-ai/database-runtime/shared";
|
|
1
2
|
import {
|
|
3
|
+
normalizeDbRecordId,
|
|
4
|
+
normalizeRecordId,
|
|
2
5
|
normalizeText,
|
|
3
6
|
normalizeLowerText,
|
|
4
7
|
toIsoString,
|
|
5
8
|
toNullableIso,
|
|
6
9
|
nowDb,
|
|
7
|
-
isDuplicateEntryError
|
|
10
|
+
isDuplicateEntryError,
|
|
11
|
+
createWithTransaction
|
|
8
12
|
} from "./repositoryUtils.js";
|
|
9
13
|
|
|
10
14
|
function mapRow(row) {
|
|
@@ -13,10 +17,10 @@ function mapRow(row) {
|
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
return {
|
|
16
|
-
id:
|
|
20
|
+
id: normalizeDbRecordId(row.id, { fallback: "" }),
|
|
17
21
|
slug: normalizeText(row.slug),
|
|
18
22
|
name: normalizeText(row.name),
|
|
19
|
-
ownerUserId:
|
|
23
|
+
ownerUserId: normalizeDbRecordId(row.owner_user_id, { fallback: "" }),
|
|
20
24
|
isPersonal: Boolean(row.is_personal),
|
|
21
25
|
avatarUrl: row.avatar_url ? normalizeText(row.avatar_url) : "",
|
|
22
26
|
createdAt: toIsoString(row.created_at),
|
|
@@ -41,6 +45,7 @@ function createRepository(knex) {
|
|
|
41
45
|
if (typeof knex !== "function") {
|
|
42
46
|
throw new TypeError("workspacesRepository requires knex.");
|
|
43
47
|
}
|
|
48
|
+
const withTransaction = createWithTransaction(knex);
|
|
44
49
|
|
|
45
50
|
function workspaceSelectColumns({ includeMembership = false } = {}) {
|
|
46
51
|
const columns = [
|
|
@@ -61,9 +66,14 @@ function createRepository(knex) {
|
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
async function findById(workspaceId, options = {}) {
|
|
69
|
+
const normalizedWorkspaceId = normalizeRecordId(workspaceId, { fallback: null });
|
|
70
|
+
if (!normalizedWorkspaceId) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
64
74
|
const client = options?.trx || knex;
|
|
65
75
|
const row = await client("workspaces as w")
|
|
66
|
-
.where({ "w.id":
|
|
76
|
+
.where({ "w.id": normalizedWorkspaceId })
|
|
67
77
|
.select(workspaceSelectColumns())
|
|
68
78
|
.first();
|
|
69
79
|
return mapRow(row);
|
|
@@ -84,9 +94,14 @@ function createRepository(knex) {
|
|
|
84
94
|
}
|
|
85
95
|
|
|
86
96
|
async function findPersonalByOwnerUserId(userId, options = {}) {
|
|
97
|
+
const normalizedUserId = normalizeRecordId(userId, { fallback: null });
|
|
98
|
+
if (!normalizedUserId) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
87
102
|
const client = options?.trx || knex;
|
|
88
103
|
const row = await client("workspaces as w")
|
|
89
|
-
.where({ "w.owner_user_id":
|
|
104
|
+
.where({ "w.owner_user_id": normalizedUserId, "w.is_personal": 1 })
|
|
90
105
|
.orderBy("w.id", "asc")
|
|
91
106
|
.select(workspaceSelectColumns())
|
|
92
107
|
.first();
|
|
@@ -96,11 +111,15 @@ function createRepository(knex) {
|
|
|
96
111
|
async function insert(payload = {}, options = {}) {
|
|
97
112
|
const client = options?.trx || knex;
|
|
98
113
|
const source = payload && typeof payload === "object" ? payload : {};
|
|
114
|
+
const ownerUserId = normalizeRecordId(source.ownerUserId, { fallback: null });
|
|
115
|
+
if (!ownerUserId) {
|
|
116
|
+
throw new TypeError("workspacesRepository.insert requires ownerUserId.");
|
|
117
|
+
}
|
|
99
118
|
|
|
100
119
|
const insertPayload = {
|
|
101
120
|
slug: normalizeLowerText(source.slug),
|
|
102
121
|
name: normalizeText(source.name),
|
|
103
|
-
owner_user_id:
|
|
122
|
+
owner_user_id: ownerUserId,
|
|
104
123
|
is_personal: source.isPersonal ? 1 : 0,
|
|
105
124
|
avatar_url: normalizeText(source.avatarUrl),
|
|
106
125
|
created_at: nowDb(),
|
|
@@ -110,8 +129,8 @@ function createRepository(knex) {
|
|
|
110
129
|
|
|
111
130
|
try {
|
|
112
131
|
const result = await client("workspaces").insert(insertPayload);
|
|
113
|
-
const insertedId =
|
|
114
|
-
if (
|
|
132
|
+
const insertedId = resolveInsertedRecordId(result, { fallback: null });
|
|
133
|
+
if (insertedId) {
|
|
115
134
|
return findById(insertedId, { trx: client });
|
|
116
135
|
}
|
|
117
136
|
const bySlug = await findBySlug(insertPayload.slug, { trx: client });
|
|
@@ -129,6 +148,11 @@ function createRepository(knex) {
|
|
|
129
148
|
}
|
|
130
149
|
|
|
131
150
|
async function updateById(workspaceId, patch = {}, options = {}) {
|
|
151
|
+
const normalizedWorkspaceId = normalizeRecordId(workspaceId, { fallback: null });
|
|
152
|
+
if (!normalizedWorkspaceId) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
132
156
|
const client = options?.trx || knex;
|
|
133
157
|
const source = patch && typeof patch === "object" ? patch : {};
|
|
134
158
|
const dbPatch = {
|
|
@@ -142,15 +166,20 @@ function createRepository(knex) {
|
|
|
142
166
|
dbPatch.avatar_url = normalizeText(source.avatarUrl);
|
|
143
167
|
}
|
|
144
168
|
|
|
145
|
-
await client("workspaces").where({ id:
|
|
146
|
-
return findById(
|
|
169
|
+
await client("workspaces").where({ id: normalizedWorkspaceId }).update(dbPatch);
|
|
170
|
+
return findById(normalizedWorkspaceId, { trx: client });
|
|
147
171
|
}
|
|
148
172
|
|
|
149
173
|
async function listForUserId(userId, options = {}) {
|
|
174
|
+
const normalizedUserId = normalizeRecordId(userId, { fallback: null });
|
|
175
|
+
if (!normalizedUserId) {
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
|
|
150
179
|
const client = options?.trx || knex;
|
|
151
180
|
const rows = await client("workspace_memberships as wm")
|
|
152
181
|
.join("workspaces as w", "w.id", "wm.workspace_id")
|
|
153
|
-
.where({ "wm.user_id":
|
|
182
|
+
.where({ "wm.user_id": normalizedUserId })
|
|
154
183
|
.whereNull("w.deleted_at")
|
|
155
184
|
.orderBy("w.is_personal", "desc")
|
|
156
185
|
.orderBy("w.id", "asc")
|
|
@@ -160,6 +189,7 @@ function createRepository(knex) {
|
|
|
160
189
|
}
|
|
161
190
|
|
|
162
191
|
return Object.freeze({
|
|
192
|
+
withTransaction,
|
|
163
193
|
findById,
|
|
164
194
|
findBySlug,
|
|
165
195
|
findPersonalByOwnerUserId,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
1
2
|
import { normalizeIdentity } from "../repositories/usersRepository.js";
|
|
2
3
|
|
|
3
4
|
async function resolveUserProfile(usersRepository, user) {
|
|
@@ -9,8 +10,8 @@ async function resolveUserProfile(usersRepository, user) {
|
|
|
9
10
|
}
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
const userId =
|
|
13
|
-
if (
|
|
13
|
+
const userId = normalizeRecordId(user?.id, { fallback: null });
|
|
14
|
+
if (userId) {
|
|
14
15
|
const profileById = await usersRepository.findById(userId);
|
|
15
16
|
if (profileById) {
|
|
16
17
|
return profileById;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
1
2
|
import { normalizeLowerText, normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
|
|
2
3
|
import { normalizeIdentity } from "../repositories/usersRepository.js";
|
|
3
4
|
|
|
@@ -46,7 +47,7 @@ function profileNeedsUpdate(existing, nextProfile) {
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
function requireSynchronizedProfile(profile) {
|
|
49
|
-
if (profile &&
|
|
50
|
+
if (profile && normalizeRecordId(profile.id, { fallback: null }) && String(profile.displayName || "").trim()) {
|
|
50
51
|
return profile;
|
|
51
52
|
}
|
|
52
53
|
|
|
@@ -60,6 +61,9 @@ function createService({ usersRepository, workspaceProvisioningService = null, u
|
|
|
60
61
|
if (typeof usersRepository.upsert !== "function") {
|
|
61
62
|
throw new Error("authProfileSyncService requires usersRepository.upsert().");
|
|
62
63
|
}
|
|
64
|
+
if (typeof usersRepository.withTransaction !== "function") {
|
|
65
|
+
throw new Error("authProfileSyncService requires usersRepository.withTransaction().");
|
|
66
|
+
}
|
|
63
67
|
if (!userSettingsRepository || typeof userSettingsRepository.ensureForUserId !== "function") {
|
|
64
68
|
throw new Error("authProfileSyncService requires userSettingsRepository.ensureForUserId().");
|
|
65
69
|
}
|
|
@@ -118,10 +122,8 @@ function createService({ usersRepository, workspaceProvisioningService = null, u
|
|
|
118
122
|
if (options?.trx) {
|
|
119
123
|
return runSync(options.trx);
|
|
120
124
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
return runSync();
|
|
125
|
+
|
|
126
|
+
return usersRepository.withTransaction((trx) => runSync(trx));
|
|
125
127
|
}
|
|
126
128
|
|
|
127
129
|
return Object.freeze({
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
mapWorkspaceSummary
|
|
12
12
|
} from "../formatters/workspaceFormatter.js";
|
|
13
13
|
import { normalizeLowerText, normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
|
|
14
|
+
import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
14
15
|
import { authenticatedUserValidator } from "../validators/authenticatedUserValidator.js";
|
|
15
16
|
|
|
16
17
|
function toSlugPart(value) {
|
|
@@ -237,7 +238,9 @@ function createService({
|
|
|
237
238
|
normalizedUser.id,
|
|
238
239
|
options
|
|
239
240
|
);
|
|
240
|
-
const actorOwnsWorkspace =
|
|
241
|
+
const actorOwnsWorkspace =
|
|
242
|
+
normalizeRecordId(workspace.ownerUserId, { fallback: null }) ===
|
|
243
|
+
normalizeRecordId(normalizedUser.id, { fallback: null });
|
|
241
244
|
const membershipIsActive = normalizeLowerText(membership?.status) === "active";
|
|
242
245
|
|
|
243
246
|
if (!membershipIsActive && actorOwnsWorkspace) {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
1
2
|
import { deepFreeze } from "./deepFreeze.js";
|
|
2
3
|
|
|
3
4
|
function resolveActorScopedEntityId({ options } = {}) {
|
|
4
|
-
return
|
|
5
|
+
return normalizeRecordId(options?.context?.actor?.id, { fallback: "" });
|
|
5
6
|
}
|
|
6
7
|
|
|
7
8
|
function resolveWorkspaceSlugPayload({ args } = {}) {
|
|
@@ -66,7 +67,7 @@ function createWorkspaceEntityAndBootstrapEvents({
|
|
|
66
67
|
source: "workspace",
|
|
67
68
|
entity: normalizedWorkspaceEntity,
|
|
68
69
|
operation: normalizedWorkspaceOperation,
|
|
69
|
-
entityId: workspaceEntityId,
|
|
70
|
+
entityId: (payload = {}) => normalizeRecordId(workspaceEntityId(payload), { fallback: "" }),
|
|
70
71
|
realtime: {
|
|
71
72
|
event: normalizedWorkspaceRealtimeEvent,
|
|
72
73
|
payload: resolveWorkspaceSlugPayload,
|
|
@@ -78,7 +79,7 @@ function createWorkspaceEntityAndBootstrapEvents({
|
|
|
78
79
|
source: "users",
|
|
79
80
|
entity: "bootstrap",
|
|
80
81
|
operation: "updated",
|
|
81
|
-
entityId: bootstrapEntityId,
|
|
82
|
+
entityId: (payload = {}) => normalizeRecordId(bootstrapEntityId(payload), { fallback: "" }),
|
|
82
83
|
realtime: {
|
|
83
84
|
event: "users.bootstrap.changed",
|
|
84
85
|
audience: bootstrapAudience
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Type } from "@fastify/type-provider-typebox";
|
|
2
|
-
import { normalizeObjectInput } from "@jskit-ai/kernel/shared/validators
|
|
2
|
+
import { normalizeObjectInput, recordIdInputSchema } from "@jskit-ai/kernel/shared/validators";
|
|
3
3
|
import { normalizeLowerText, normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
|
|
4
|
+
import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
4
5
|
|
|
5
6
|
function normalizeAuthenticatedUser(input = {}) {
|
|
6
7
|
const source = normalizeObjectInput(input);
|
|
7
|
-
const id =
|
|
8
|
-
if (!
|
|
8
|
+
const id = normalizeRecordId(source.id, { fallback: null });
|
|
9
|
+
if (!id) {
|
|
9
10
|
return null;
|
|
10
11
|
}
|
|
11
12
|
|
|
@@ -25,7 +26,7 @@ function normalizeAuthenticatedUser(input = {}) {
|
|
|
25
26
|
const authenticatedUserValidator = Object.freeze({
|
|
26
27
|
schema: Type.Object(
|
|
27
28
|
{
|
|
28
|
-
id:
|
|
29
|
+
id: recordIdInputSchema,
|
|
29
30
|
email: Type.String({ minLength: 1 }),
|
|
30
31
|
username: Type.Optional(Type.String()),
|
|
31
32
|
displayName: Type.Optional(Type.String()),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AppError } from "@jskit-ai/kernel/server/runtime/errors";
|
|
2
|
-
import {
|
|
2
|
+
import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
3
3
|
|
|
4
4
|
function createService({ consoleSettingsRepository } = {}) {
|
|
5
5
|
if (!consoleSettingsRepository || typeof consoleSettingsRepository.ensureOwnerUserId !== "function") {
|
|
@@ -7,7 +7,7 @@ function createService({ consoleSettingsRepository } = {}) {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
async function ensureInitialConsoleMember(userId, options = {}) {
|
|
10
|
-
const normalizedUserId =
|
|
10
|
+
const normalizedUserId = normalizeRecordId(userId, { fallback: null });
|
|
11
11
|
if (!normalizedUserId) {
|
|
12
12
|
throw new AppError(400, "Invalid console user.");
|
|
13
13
|
}
|
|
@@ -16,7 +16,7 @@ function createService({ consoleSettingsRepository } = {}) {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
async function requireConsoleOwner(context = {}, options = {}) {
|
|
19
|
-
const actorUserId =
|
|
19
|
+
const actorUserId = normalizeRecordId(context?.actor?.id, { fallback: null });
|
|
20
20
|
if (!actorUserId) {
|
|
21
21
|
throw new AppError(401, "Authentication required.");
|
|
22
22
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
+
normalizeDbRecordId,
|
|
3
|
+
normalizeRecordId,
|
|
2
4
|
nowDb,
|
|
3
|
-
toIsoString
|
|
5
|
+
toIsoString,
|
|
6
|
+
createWithTransaction
|
|
4
7
|
} from "../common/repositories/repositoryUtils.js";
|
|
5
|
-
import { parsePositiveInteger } from "@jskit-ai/kernel/server/runtime";
|
|
6
8
|
import { normalizeObjectInput } from "@jskit-ai/kernel/shared/validators/inputNormalization";
|
|
7
9
|
import { consoleSettingsFields } from "../../shared/resources/consoleSettingsFields.js";
|
|
8
10
|
|
|
@@ -26,10 +28,10 @@ function mapSingletonRow(row) {
|
|
|
26
28
|
throw new Error("console_settings singleton row is missing.");
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
const ownerUserId =
|
|
31
|
+
const ownerUserId = normalizeDbRecordId(row.owner_user_id, { fallback: null });
|
|
30
32
|
return {
|
|
31
|
-
id:
|
|
32
|
-
ownerUserId
|
|
33
|
+
id: normalizeDbRecordId(row.id, { fallback: "1" }),
|
|
34
|
+
ownerUserId,
|
|
33
35
|
settings: mapSettings(row),
|
|
34
36
|
createdAt: toIsoString(row.created_at),
|
|
35
37
|
updatedAt: toIsoString(row.updated_at)
|
|
@@ -40,6 +42,7 @@ function createRepository(knex) {
|
|
|
40
42
|
if (typeof knex !== "function") {
|
|
41
43
|
throw new TypeError("consoleSettingsRepository requires knex.");
|
|
42
44
|
}
|
|
45
|
+
const withTransaction = createWithTransaction(knex);
|
|
43
46
|
|
|
44
47
|
async function readSingleton(client) {
|
|
45
48
|
return client("console_settings").where({ id: 1 }).first();
|
|
@@ -52,7 +55,7 @@ function createRepository(knex) {
|
|
|
52
55
|
|
|
53
56
|
async function ensureOwnerUserId(userId, options = {}) {
|
|
54
57
|
const client = options?.trx || knex;
|
|
55
|
-
const candidateOwnerUserId =
|
|
58
|
+
const candidateOwnerUserId = normalizeRecordId(userId, { fallback: null });
|
|
56
59
|
if (!candidateOwnerUserId) {
|
|
57
60
|
throw new TypeError("consoleSettingsRepository.ensureOwnerUserId requires a positive user id.");
|
|
58
61
|
}
|
|
@@ -102,6 +105,7 @@ function createRepository(knex) {
|
|
|
102
105
|
}
|
|
103
106
|
|
|
104
107
|
return Object.freeze({
|
|
108
|
+
withTransaction,
|
|
105
109
|
getSingleton,
|
|
106
110
|
ensureOwnerUserId,
|
|
107
111
|
updateSingleton
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { accountAvatarFormatter } from "./common/formatters/accountAvatarFormatter.js";
|
|
14
14
|
import { authenticatedUserValidator } from "./common/validators/authenticatedUserValidator.js";
|
|
15
15
|
import { userSettingsFields } from "../shared/resources/userSettingsFields.js";
|
|
16
|
+
import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
16
17
|
|
|
17
18
|
function getOAuthProviderCatalogPayload(authService) {
|
|
18
19
|
if (!authService || typeof authService.getOAuthProviderCatalog !== "function") {
|
|
@@ -194,12 +195,15 @@ function createUsersBootstrapContributor({
|
|
|
194
195
|
const latestProfile = (await usersRepository.findById(normalizedUser.id)) || normalizedUser;
|
|
195
196
|
const userSettings = await userSettingsRepository.ensureForUserId(latestProfile.id);
|
|
196
197
|
|
|
197
|
-
let seededConsoleOwnerUserId =
|
|
198
|
+
let seededConsoleOwnerUserId = null;
|
|
198
199
|
if (
|
|
199
200
|
consoleService &&
|
|
200
201
|
typeof consoleService.ensureInitialConsoleMember === "function"
|
|
201
202
|
) {
|
|
202
|
-
seededConsoleOwnerUserId =
|
|
203
|
+
seededConsoleOwnerUserId = normalizeRecordId(
|
|
204
|
+
await consoleService.ensureInitialConsoleMember(latestProfile.id),
|
|
205
|
+
{ fallback: null }
|
|
206
|
+
);
|
|
203
207
|
}
|
|
204
208
|
|
|
205
209
|
payload = {
|
|
@@ -221,7 +225,7 @@ function createUsersBootstrapContributor({
|
|
|
221
225
|
requestedWorkspace: null,
|
|
222
226
|
permissions: [],
|
|
223
227
|
surfaceAccess: {
|
|
224
|
-
consoleowner: seededConsoleOwnerUserId
|
|
228
|
+
consoleowner: Boolean(seededConsoleOwnerUserId) && seededConsoleOwnerUserId === String(latestProfile.id)
|
|
225
229
|
},
|
|
226
230
|
workspaceSettings: null,
|
|
227
231
|
userSettings: mapUserSettingsBootstrap(userSettings),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { requireServiceMethod } from "@jskit-ai/kernel/shared/actions/actionContributorHelpers";
|
|
2
|
+
import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
3
|
import { normalizeLowerText } from "@jskit-ai/kernel/shared/actions/textNormalization";
|
|
3
4
|
import {
|
|
4
5
|
TENANCY_MODE_NONE,
|
|
@@ -129,7 +130,10 @@ function createWorkspaceBootstrapContributor({
|
|
|
129
130
|
return Object.freeze({
|
|
130
131
|
contributorId,
|
|
131
132
|
async contribute({ request = null, query = {}, payload = {} } = {}) {
|
|
132
|
-
const normalizedUserId =
|
|
133
|
+
const normalizedUserId = normalizeRecordId(
|
|
134
|
+
payload?.session?.authenticated === true ? payload?.session?.userId : null,
|
|
135
|
+
{ fallback: null }
|
|
136
|
+
);
|
|
133
137
|
const normalizedWorkspaceSlug = resolveBootstrapWorkspaceSlug({ query, request });
|
|
134
138
|
if (!normalizedUserId) {
|
|
135
139
|
if (!normalizedWorkspaceSlug || resolvedTenancyProfile.mode === TENANCY_MODE_NONE) {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
|
|
2
|
+
import { normalizeDbRecordId } from "@jskit-ai/database-runtime/shared";
|
|
3
|
+
import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
4
|
import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
|
|
3
5
|
import { deepFreeze } from "../common/support/deepFreeze.js";
|
|
4
6
|
import { createService as createWorkspaceMembersService } from "./workspaceMembersService.js";
|
|
@@ -23,8 +25,8 @@ const INVITE_RECIPIENT_BOOTSTRAP_AUDIENCE = Object.freeze({
|
|
|
23
25
|
return [];
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
const inviteId =
|
|
27
|
-
if (!
|
|
28
|
+
const inviteId = normalizeRecordId(event?.entityId, { fallback: null });
|
|
29
|
+
if (!inviteId) {
|
|
28
30
|
return [];
|
|
29
31
|
}
|
|
30
32
|
|
|
@@ -33,8 +35,8 @@ const INVITE_RECIPIENT_BOOTSTRAP_AUDIENCE = Object.freeze({
|
|
|
33
35
|
.where("wi.id", inviteId)
|
|
34
36
|
.first("up.id as user_id");
|
|
35
37
|
|
|
36
|
-
const userId =
|
|
37
|
-
if (!
|
|
38
|
+
const userId = normalizeDbRecordId(row?.user_id, { fallback: null });
|
|
39
|
+
if (!userId) {
|
|
38
40
|
return [];
|
|
39
41
|
}
|
|
40
42
|
|