@jskit-ai/users-core 0.1.30 → 0.1.32
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 +8 -6
- package/package.json +6 -6
- package/src/server/UsersCoreServiceProvider.js +1 -3
- package/src/server/accountProfile/avatarService.js +18 -59
- package/src/server/accountProfile/avatarStorageService.js +14 -95
- package/src/server/accountProfile/bootAccountProfileRoutes.js +10 -14
- package/src/server/common/formatters/workspaceFormatter.js +2 -2
- package/src/server/common/repositories/userProfilesRepository.js +6 -6
- package/src/server/common/repositories/workspaceInvitesRepository.js +2 -2
- package/src/server/common/repositories/workspaceMembershipsRepository.js +9 -9
- package/src/server/common/repositories/workspacesRepository.js +2 -2
- package/src/server/common/services/authProfileSyncService.js +5 -5
- package/src/server/common/services/workspaceContextService.js +3 -3
- package/src/server/common/validators/authenticatedUserValidator.js +2 -2
- package/src/server/workspaceBootstrapContributor.js +2 -2
- package/src/server/workspaceMembers/bootWorkspaceMembers.js +2 -2
- package/src/server/workspaceMembers/workspaceMembersActions.js +2 -2
- package/src/server/workspaceMembers/workspaceMembersService.js +11 -11
- package/src/server/workspacePendingInvitations/workspacePendingInvitationsService.js +1 -1
- package/src/shared/resources/workspaceMembersResource.js +11 -11
- package/src/shared/resources/workspacePendingInvitationsResource.js +2 -2
- package/src/shared/resources/workspaceResource.js +2 -2
- package/src/shared/roles.js +8 -8
- package/templates/migrations/users_core_initial.cjs +5 -5
- package/test/authProfileSyncService.test.js +5 -5
- package/test/avatarService.test.js +4 -4
- package/test/usersRouteRequestInputValidator.test.js +4 -4
- package/test/workspaceActionContextContributor.test.js +9 -9
- package/test/workspaceAuthPolicyContextResolver.test.js +2 -2
- package/test/workspaceBootstrapContributor.test.js +1 -1
- package/test/workspaceInvitesRepository.test.js +3 -3
- package/test/workspaceMembersService.test.js +10 -10
- package/test/workspacePendingInvitationsResource.test.js +2 -2
- package/test/workspacePendingInvitationsService.test.js +3 -3
- package/test/workspaceService.test.js +10 -10
- package/src/server/accountProfile/registerAvatarMultipartSupport.js +0 -40
- package/test/registerAvatarMultipartSupport.test.js +0 -63
|
@@ -52,7 +52,7 @@ function createFixture() {
|
|
|
52
52
|
return [
|
|
53
53
|
{
|
|
54
54
|
userId: 11,
|
|
55
|
-
|
|
55
|
+
roleSid: "member",
|
|
56
56
|
status: "active",
|
|
57
57
|
displayName: "Alice",
|
|
58
58
|
email: "alice@example.com"
|
|
@@ -65,7 +65,7 @@ function createFixture() {
|
|
|
65
65
|
return {
|
|
66
66
|
workspaceId: 7,
|
|
67
67
|
userId: 11,
|
|
68
|
-
|
|
68
|
+
roleSid: "member",
|
|
69
69
|
status: "active"
|
|
70
70
|
};
|
|
71
71
|
},
|
|
@@ -73,7 +73,7 @@ function createFixture() {
|
|
|
73
73
|
assert.equal(Number(workspaceId), 7);
|
|
74
74
|
assert.equal(Number(userId), 11);
|
|
75
75
|
assert.deepEqual(patch, {
|
|
76
|
-
|
|
76
|
+
roleSid: "admin",
|
|
77
77
|
status: "active"
|
|
78
78
|
});
|
|
79
79
|
}
|
|
@@ -134,7 +134,7 @@ test("workspaceMembersService.createInvite uses configured inviteExpiresInMs", a
|
|
|
134
134
|
{ id: 11 },
|
|
135
135
|
{
|
|
136
136
|
email: "alice@example.com",
|
|
137
|
-
|
|
137
|
+
roleSid: "member"
|
|
138
138
|
},
|
|
139
139
|
authorizedOptions(["workspace.members.invite"])
|
|
140
140
|
);
|
|
@@ -260,13 +260,13 @@ test("workspaceMembersService.updateMemberRole returns the refreshed member list
|
|
|
260
260
|
workspace,
|
|
261
261
|
{
|
|
262
262
|
memberUserId: 11,
|
|
263
|
-
|
|
263
|
+
roleSid: "admin"
|
|
264
264
|
},
|
|
265
265
|
authorizedOptions(["workspace.members.manage"])
|
|
266
266
|
);
|
|
267
267
|
|
|
268
268
|
assert.equal(response.members.length, 1);
|
|
269
|
-
assert.equal(response.members[0].
|
|
269
|
+
assert.equal(response.members[0].roleSid, "member");
|
|
270
270
|
});
|
|
271
271
|
|
|
272
272
|
test("workspaceMembersService.removeMember marks membership revoked and returns refreshed members", async () => {
|
|
@@ -287,7 +287,7 @@ test("workspaceMembersService.removeMember marks membership revoked and returns
|
|
|
287
287
|
: [
|
|
288
288
|
{
|
|
289
289
|
userId: 11,
|
|
290
|
-
|
|
290
|
+
roleSid: "member",
|
|
291
291
|
status: "active",
|
|
292
292
|
displayName: "Alice",
|
|
293
293
|
email: "alice@example.com"
|
|
@@ -300,7 +300,7 @@ test("workspaceMembersService.removeMember marks membership revoked and returns
|
|
|
300
300
|
return {
|
|
301
301
|
workspaceId: 7,
|
|
302
302
|
userId: 11,
|
|
303
|
-
|
|
303
|
+
roleSid: "member",
|
|
304
304
|
status: "active"
|
|
305
305
|
};
|
|
306
306
|
},
|
|
@@ -308,7 +308,7 @@ test("workspaceMembersService.removeMember marks membership revoked and returns
|
|
|
308
308
|
assert.equal(Number(workspaceId), 7);
|
|
309
309
|
assert.equal(Number(userId), 11);
|
|
310
310
|
assert.deepEqual(patch, {
|
|
311
|
-
|
|
311
|
+
roleSid: "member",
|
|
312
312
|
status: "revoked"
|
|
313
313
|
});
|
|
314
314
|
removed = true;
|
|
@@ -359,7 +359,7 @@ test("workspaceMembersService.removeMember rejects removing the owner", async ()
|
|
|
359
359
|
return {
|
|
360
360
|
workspaceId: 7,
|
|
361
361
|
userId: 9,
|
|
362
|
-
|
|
362
|
+
roleSid: "owner",
|
|
363
363
|
status: "active"
|
|
364
364
|
};
|
|
365
365
|
},
|
|
@@ -14,7 +14,7 @@ test("workspacePendingInvitationsResource output normalizer shapes raw invite ro
|
|
|
14
14
|
workspaceSlug: "tonymobily3",
|
|
15
15
|
workspaceName: "",
|
|
16
16
|
workspaceAvatarUrl: "",
|
|
17
|
-
|
|
17
|
+
roleSid: "Member",
|
|
18
18
|
status: "Pending",
|
|
19
19
|
expiresAt: "2030-01-01T00:00:00.000Z",
|
|
20
20
|
tokenHash
|
|
@@ -29,7 +29,7 @@ test("workspacePendingInvitationsResource output normalizer shapes raw invite ro
|
|
|
29
29
|
workspaceSlug: "tonymobily3",
|
|
30
30
|
workspaceName: "tonymobily3",
|
|
31
31
|
workspaceAvatarUrl: "",
|
|
32
|
-
|
|
32
|
+
roleSid: "member",
|
|
33
33
|
status: "pending",
|
|
34
34
|
expiresAt: "2030-01-01T00:00:00.000Z",
|
|
35
35
|
token: encodeInviteTokenHash(tokenHash)
|
|
@@ -64,7 +64,7 @@ test("listPendingInvitesForUser returns raw pending invite rows for the action l
|
|
|
64
64
|
workspaceSlug: "tonymobily3",
|
|
65
65
|
workspaceName: "TonyMobily3",
|
|
66
66
|
workspaceAvatarUrl: "",
|
|
67
|
-
|
|
67
|
+
roleSid: "member",
|
|
68
68
|
status: "pending",
|
|
69
69
|
expiresAt: "2030-01-01T00:00:00.000Z",
|
|
70
70
|
tokenHash
|
|
@@ -91,7 +91,7 @@ test("acceptInviteByToken accepts opaque invite token and resolves invite by dec
|
|
|
91
91
|
id: 44,
|
|
92
92
|
workspaceId: 1,
|
|
93
93
|
email: "chiaramobily@gmail.com",
|
|
94
|
-
|
|
94
|
+
roleSid: "member",
|
|
95
95
|
status: "pending",
|
|
96
96
|
tokenHash,
|
|
97
97
|
expiresAt: "2030-01-01T00:00:00.000Z"
|
|
@@ -125,7 +125,7 @@ test("refuseInviteByToken revokes the invite and returns refused", async () => {
|
|
|
125
125
|
id: 45,
|
|
126
126
|
workspaceId: 1,
|
|
127
127
|
email: "chiaramobily@gmail.com",
|
|
128
|
-
|
|
128
|
+
roleSid: "member",
|
|
129
129
|
status: "pending",
|
|
130
130
|
tokenHash,
|
|
131
131
|
expiresAt: "2030-01-01T00:00:00.000Z"
|
|
@@ -95,7 +95,7 @@ function createWorkspaceServiceFixture({
|
|
|
95
95
|
slug: "tonymobily3",
|
|
96
96
|
name: "TonyMobily3",
|
|
97
97
|
avatarUrl: "",
|
|
98
|
-
|
|
98
|
+
roleSid: "owner",
|
|
99
99
|
membershipStatus: "active"
|
|
100
100
|
},
|
|
101
101
|
{
|
|
@@ -103,7 +103,7 @@ function createWorkspaceServiceFixture({
|
|
|
103
103
|
slug: "pending-workspace",
|
|
104
104
|
name: "Pending Workspace",
|
|
105
105
|
avatarUrl: "",
|
|
106
|
-
|
|
106
|
+
roleSid: "member",
|
|
107
107
|
membershipStatus: "pending"
|
|
108
108
|
}
|
|
109
109
|
];
|
|
@@ -158,7 +158,7 @@ function createWorkspaceServiceFixture({
|
|
|
158
158
|
return {
|
|
159
159
|
workspaceId,
|
|
160
160
|
userId,
|
|
161
|
-
|
|
161
|
+
roleSid: "owner",
|
|
162
162
|
status: "active"
|
|
163
163
|
};
|
|
164
164
|
}
|
|
@@ -190,7 +190,7 @@ test("workspaceService.listWorkspacesForUser returns only accessible workspaces"
|
|
|
190
190
|
|
|
191
191
|
assert.equal(workspaces.length, 1);
|
|
192
192
|
assert.equal(workspaces[0].slug, "tonymobily3");
|
|
193
|
-
assert.equal(workspaces[0].
|
|
193
|
+
assert.equal(workspaces[0].roleSid, "owner");
|
|
194
194
|
assert.equal(calls.listForUserId, 1);
|
|
195
195
|
assert.equal(calls.insert, 0);
|
|
196
196
|
});
|
|
@@ -220,7 +220,7 @@ test("workspaceService.listWorkspacesForUser returns all active memberships in p
|
|
|
220
220
|
slug: "chiaramobily",
|
|
221
221
|
name: "Chiara Personal",
|
|
222
222
|
avatarUrl: "",
|
|
223
|
-
|
|
223
|
+
roleSid: "owner",
|
|
224
224
|
membershipStatus: "active"
|
|
225
225
|
},
|
|
226
226
|
{
|
|
@@ -228,7 +228,7 @@ test("workspaceService.listWorkspacesForUser returns all active memberships in p
|
|
|
228
228
|
slug: "tonymobily",
|
|
229
229
|
name: "Tony Workspace",
|
|
230
230
|
avatarUrl: "",
|
|
231
|
-
|
|
231
|
+
roleSid: "member",
|
|
232
232
|
membershipStatus: "active"
|
|
233
233
|
},
|
|
234
234
|
{
|
|
@@ -236,7 +236,7 @@ test("workspaceService.listWorkspacesForUser returns all active memberships in p
|
|
|
236
236
|
slug: "pending-workspace",
|
|
237
237
|
name: "Pending Workspace",
|
|
238
238
|
avatarUrl: "",
|
|
239
|
-
|
|
239
|
+
roleSid: "member",
|
|
240
240
|
membershipStatus: "pending"
|
|
241
241
|
}
|
|
242
242
|
]
|
|
@@ -393,7 +393,7 @@ test("workspaceService.resolveWorkspaceContextForUserBySlug allows personal tena
|
|
|
393
393
|
);
|
|
394
394
|
|
|
395
395
|
assert.equal(context.workspace.slug, "team-alpha");
|
|
396
|
-
assert.equal(context.membership.
|
|
396
|
+
assert.equal(context.membership.roleSid, "owner");
|
|
397
397
|
assert.deepEqual(context.permissions, ["*"]);
|
|
398
398
|
});
|
|
399
399
|
|
|
@@ -439,7 +439,7 @@ test("workspaceService.resolveWorkspaceContextForUserBySlug grants owner access
|
|
|
439
439
|
membershipRecord = {
|
|
440
440
|
workspaceId,
|
|
441
441
|
userId,
|
|
442
|
-
|
|
442
|
+
roleSid: "owner",
|
|
443
443
|
status: "active"
|
|
444
444
|
};
|
|
445
445
|
return membershipRecord;
|
|
@@ -464,7 +464,7 @@ test("workspaceService.resolveWorkspaceContextForUserBySlug grants owner access
|
|
|
464
464
|
);
|
|
465
465
|
|
|
466
466
|
assert.equal(ensuredMembershipCount, 1);
|
|
467
|
-
assert.equal(context.membership.
|
|
467
|
+
assert.equal(context.membership.roleSid, "owner");
|
|
468
468
|
assert.deepEqual(context.permissions, ["*"]);
|
|
469
469
|
});
|
|
470
470
|
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import fastifyMultipart from "@fastify/multipart";
|
|
2
|
-
|
|
3
|
-
async function registerAvatarMultipartSupport(app) {
|
|
4
|
-
if (!app || typeof app.has !== "function" || typeof app.make !== "function") {
|
|
5
|
-
throw new Error("registerAvatarMultipartSupport requires application has()/make().");
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
if (!app.has("jskit.fastify")) {
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const fastify = app.make("jskit.fastify");
|
|
13
|
-
if (!fastify || typeof fastify.register !== "function") {
|
|
14
|
-
throw new Error("registerAvatarMultipartSupport requires Fastify register().");
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
if (fastify["jskit.users-core.avatar.multipart.support"] === true) {
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (typeof fastify.hasContentTypeParser === "function" && fastify.hasContentTypeParser("multipart")) {
|
|
22
|
-
Object.defineProperty(fastify, "jskit.users-core.avatar.multipart.support", {
|
|
23
|
-
value: true,
|
|
24
|
-
configurable: false,
|
|
25
|
-
enumerable: false,
|
|
26
|
-
writable: false
|
|
27
|
-
});
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
await fastify.register(fastifyMultipart);
|
|
32
|
-
Object.defineProperty(fastify, "jskit.users-core.avatar.multipart.support", {
|
|
33
|
-
value: true,
|
|
34
|
-
configurable: false,
|
|
35
|
-
enumerable: false,
|
|
36
|
-
writable: false
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export { registerAvatarMultipartSupport };
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { registerAvatarMultipartSupport } from "../src/server/accountProfile/registerAvatarMultipartSupport.js";
|
|
4
|
-
|
|
5
|
-
function createAppStub({ hasFastify = true, fastify = null } = {}) {
|
|
6
|
-
const resolvedFastify =
|
|
7
|
-
fastify ||
|
|
8
|
-
{
|
|
9
|
-
register: async () => {},
|
|
10
|
-
hasContentTypeParser: () => false
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
return {
|
|
14
|
-
has(token) {
|
|
15
|
-
if (token === "jskit.fastify") {
|
|
16
|
-
return hasFastify;
|
|
17
|
-
}
|
|
18
|
-
return false;
|
|
19
|
-
},
|
|
20
|
-
make(token) {
|
|
21
|
-
if (token === "jskit.fastify") {
|
|
22
|
-
return resolvedFastify;
|
|
23
|
-
}
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
test("registerAvatarMultipartSupport returns early when Fastify is not available", async () => {
|
|
30
|
-
const app = createAppStub({ hasFastify: false });
|
|
31
|
-
await assert.doesNotReject(async () => registerAvatarMultipartSupport(app));
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
test("registerAvatarMultipartSupport registers multipart parser only once", async () => {
|
|
35
|
-
let registerCount = 0;
|
|
36
|
-
const fastify = {
|
|
37
|
-
register: async () => {
|
|
38
|
-
registerCount += 1;
|
|
39
|
-
},
|
|
40
|
-
hasContentTypeParser: () => false
|
|
41
|
-
};
|
|
42
|
-
const app = createAppStub({ fastify });
|
|
43
|
-
|
|
44
|
-
await registerAvatarMultipartSupport(app);
|
|
45
|
-
await registerAvatarMultipartSupport(app);
|
|
46
|
-
|
|
47
|
-
assert.equal(registerCount, 1);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test("registerAvatarMultipartSupport skips registration when parser already exists", async () => {
|
|
51
|
-
let registerCount = 0;
|
|
52
|
-
const fastify = {
|
|
53
|
-
register: async () => {
|
|
54
|
-
registerCount += 1;
|
|
55
|
-
},
|
|
56
|
-
hasContentTypeParser: (contentType) => String(contentType || "").trim().toLowerCase() === "multipart"
|
|
57
|
-
};
|
|
58
|
-
const app = createAppStub({ fastify });
|
|
59
|
-
|
|
60
|
-
await registerAvatarMultipartSupport(app);
|
|
61
|
-
|
|
62
|
-
assert.equal(registerCount, 0);
|
|
63
|
-
});
|