@jskit-ai/users-core 0.1.172 → 0.1.173
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 +41 -253
- package/patterns/user-administration-server/PATTERN.md +64 -0
- package/patterns/user-administration-server/example/packages/users/package.json +42 -0
- package/patterns/user-administration-server/example/packages/users/src/server/UsersFeature.js +13 -0
- package/patterns/user-administration-server/example/packages/users/src/shared/index.js +1 -0
- package/patterns/user-administration-server/example/packages/users-workspace/package.json +43 -0
- package/patterns/user-administration-server/example/packages/users-workspace/src/server/UsersWorkspaceFeature.js +22 -0
- package/patterns/user-administration-server/example/packages/users-workspace/src/shared/userResource.js +56 -0
- package/src/server/UsersExtensionsProvider.js +14 -0
- package/src/server/UsersFeature.js +119 -0
- package/src/server/UsersIdentityProvider.js +57 -0
- package/src/server/accountNotifications/accountNotificationsActions.js +17 -5
- package/src/server/accountNotifications/bootAccountNotificationsRoutes.js +4 -6
- package/src/server/accountPreferences/accountPreferencesActions.js +17 -5
- package/src/server/accountPreferences/bootAccountPreferencesRoutes.js +4 -6
- package/src/server/accountProfile/accountProfileActions.js +28 -14
- package/src/server/accountProfile/bootAccountProfileRoutes.js +11 -10
- package/src/server/accountSecurity/accountSecurityActions.js +27 -17
- package/src/server/accountSecurity/bootAccountSecurityRoutes.js +4 -7
- package/src/server/common/services/authProfileSyncService.js +11 -4
- package/src/server/common/support/realtimeServiceEvents.js +8 -11
- package/src/server/usersBootstrapContributor.js +4 -28
- package/src/server/usersExtensions.js +65 -0
- package/test/authProfileSyncService.test.js +2 -2
- package/test/exportsContract.test.js +6 -1
- package/test/featureRuntime.test.js +78 -0
- package/test/usersBootstrapContributor.test.js +1 -43
- package/test/usersPackageScaffoldContract.test.js +58 -204
- package/test/usersRouteRequestInputValidator.test.js +19 -43
- package/src/server/UsersCoreServiceProvider.js +0 -56
- package/src/server/accountNotifications/registerAccountNotifications.js +0 -37
- package/src/server/accountPreferences/registerAccountPreferences.js +0 -37
- package/src/server/accountProfile/registerAccountProfile.js +0 -56
- package/src/server/accountSecurity/registerAccountSecurity.js +0 -29
- package/src/server/common/registerCommonRepositories.js +0 -23
- package/src/server/common/registerSharedApi.js +0 -9
- package/src/server/profileSyncLifecycleContributorRegistry.js +0 -56
- package/src/server/registerUsersBootstrap.js +0 -20
- package/src/server/registerUsersCore.js +0 -22
- package/templates/packages/users/package.json +0 -82
- package/templates/packages/users/src/server/UsersProvider.js +0 -90
- package/templates/packages/users/src/server/actions.js +0 -68
- package/templates/packages/users/src/server/registerRoutes.js +0 -101
- package/templates/packages/users/src/server/repository.js +0 -51
- package/templates/packages/users/src/server/service.js +0 -32
- package/templates/packages/users-workspace/package.json +0 -83
- package/templates/packages/users-workspace/src/server/UsersProvider.js +0 -91
- package/templates/packages/users-workspace/src/server/actions.js +0 -77
- package/templates/packages/users-workspace/src/server/registerRoutes.js +0 -111
- package/test/providerLifecycle.test.js +0 -63
- package/test/registerCommonRepositories.test.js +0 -57
- package/test/registerServiceRealtimeEvents.test.js +0 -57
- package/test/registerUsersCore.test.js +0 -53
- /package/{templates/migrations → migrations}/users_core_generic_initial.cjs +0 -0
- /package/{templates/migrations → migrations}/users_core_profile_updated_at.cjs +0 -0
- /package/{templates/migrations → migrations}/users_core_profile_username.cjs +0 -0
- /package/{templates → patterns/user-administration-server/example}/packages/users/src/shared/userResource.js +0 -0
- /package/{templates/packages/users → patterns/user-administration-server/example/packages/users-workspace}/src/shared/index.js +0 -0
|
@@ -5,223 +5,77 @@ import test from "node:test";
|
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
6
|
import packageJson from "../package.json" with { type: "json" };
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
assert.equal(packageMetadata.mutations.dependencies.runtime["@local/users"], "file:packages/users");
|
|
21
|
-
assert.equal(packageMetadata.mutations.dependencies.runtime["@jskit-ai/crud-core"], crudCorePackage.version);
|
|
22
|
-
assert.equal(
|
|
23
|
-
packageMetadata.mutations.dependencies.runtime["@jskit-ai/resource-crud-core"],
|
|
24
|
-
resourceCrudCorePackage.version
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
const expectedFileIds = [
|
|
28
|
-
"users-core-users-package-json-base",
|
|
29
|
-
"users-core-users-package-json-workspace",
|
|
30
|
-
"users-core-users-provider-base",
|
|
31
|
-
"users-core-users-provider-workspace",
|
|
32
|
-
"users-core-users-actions-base",
|
|
33
|
-
"users-core-users-actions-workspace",
|
|
34
|
-
"users-core-users-routes-base",
|
|
35
|
-
"users-core-users-routes-workspace",
|
|
36
|
-
"users-core-users-repository",
|
|
37
|
-
"users-core-users-service",
|
|
38
|
-
"users-core-users-shared-index",
|
|
39
|
-
"users-core-users-resource"
|
|
40
|
-
];
|
|
41
|
-
|
|
42
|
-
for (const fileId of expectedFileIds) {
|
|
43
|
-
const mutation = readFileMutationById(fileId);
|
|
44
|
-
assert.ok(mutation, `Missing users-core scaffold file mutation ${fileId}.`);
|
|
45
|
-
assert.equal(mutation.ownership, "app", `${fileId} must remain app-owned.`);
|
|
46
|
-
assert.equal(mutation.preserveOnRemove, true, `${fileId} must remain preserved on remove.`);
|
|
47
|
-
assert.ok(mutation.to.startsWith("packages/users/"), `${fileId} must target packages/users.`);
|
|
48
|
-
assert.ok(mutation.from, `${fileId} must define a template source.`);
|
|
49
|
-
assert.ok(mutation.reason, `${fileId} must document why it exists.`);
|
|
50
|
-
}
|
|
8
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
9
|
+
const patternRoot = path.join(packageRoot, "patterns", "user-administration-server");
|
|
10
|
+
|
|
11
|
+
test("users-core publishes agent-readable administration patterns and package-owned migrations", async () => {
|
|
12
|
+
assert.equal(packageJson.jskit?.mutations, undefined);
|
|
13
|
+
assert.deepEqual(packageJson.jskit?.migrations, { directories: ["migrations"] });
|
|
14
|
+
|
|
15
|
+
const pattern = await readFile(path.join(patternRoot, "PATTERN.md"), "utf8");
|
|
16
|
+
assert.match(pattern, /defineCrudJsonApiFeature\(\)/);
|
|
17
|
+
assert.match(pattern, /Workspace operations are scoped before repository access/);
|
|
18
|
+
assert.match(pattern, /null\/cleared fields/);
|
|
19
|
+
assert.doesNotMatch(pattern, /createCrudJsonApiModule/);
|
|
51
20
|
});
|
|
52
21
|
|
|
53
|
-
test("
|
|
54
|
-
const
|
|
55
|
-
"users
|
|
56
|
-
"users-core-profile-username-schema",
|
|
57
|
-
"users-core-profile-updated-at-schema"
|
|
58
|
-
];
|
|
59
|
-
|
|
60
|
-
for (const migrationId of expectedMigrationIds) {
|
|
61
|
-
const mutation = readFileMutationById(migrationId);
|
|
62
|
-
assert.ok(mutation, `Missing users-core migration mutation ${migrationId}.`);
|
|
63
|
-
assert.equal(mutation.op, "install-migration", `${migrationId} must install a migration.`);
|
|
64
|
-
assert.equal(mutation.toDir, "migrations", `${migrationId} must target app migrations.`);
|
|
65
|
-
assert.equal(mutation.category, "migration", `${migrationId} must be categorized as a migration.`);
|
|
66
|
-
assert.ok(mutation.from, `${migrationId} must define a migration template source.`);
|
|
67
|
-
assert.ok(mutation.reason, `${migrationId} must document why it exists.`);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const updatedAtMigrationSource = await readFile(
|
|
71
|
-
path.join(PACKAGE_ROOT, "templates/migrations/users_core_profile_updated_at.cjs"),
|
|
22
|
+
test("global user administration example is one declarative CRUD feature", async () => {
|
|
23
|
+
const packageManifest = JSON.parse(await readFile(
|
|
24
|
+
path.join(patternRoot, "example", "packages", "users", "package.json"),
|
|
72
25
|
"utf8"
|
|
73
|
-
);
|
|
74
|
-
const
|
|
75
|
-
path.join(
|
|
26
|
+
));
|
|
27
|
+
const feature = await readFile(
|
|
28
|
+
path.join(patternRoot, "example", "packages", "users", "src", "server", "UsersFeature.js"),
|
|
76
29
|
"utf8"
|
|
77
30
|
);
|
|
78
|
-
const usersTableSection = initialMigrationSource.split("const hasUserSettingsTable")[0] || "";
|
|
79
31
|
|
|
80
|
-
assert.
|
|
81
|
-
assert.
|
|
82
|
-
assert.
|
|
32
|
+
assert.equal(packageManifest.name, "@app/users");
|
|
33
|
+
assert.equal(packageManifest.jskit?.mutations, undefined);
|
|
34
|
+
assert.deepEqual(packageManifest.jskit?.capabilities?.provides, ["app.users"]);
|
|
35
|
+
assert.match(feature, /defineCrudJsonApiFeature/);
|
|
36
|
+
assert.match(feature, /surface: "home"/);
|
|
37
|
+
assert.match(feature, /ownershipFilter: "public"/);
|
|
38
|
+
assert.doesNotMatch(feature, /\.make\(|\.singleton\(|containerToken|repositoryToken|serviceToken/);
|
|
83
39
|
});
|
|
84
40
|
|
|
85
|
-
test("
|
|
86
|
-
const
|
|
87
|
-
path.join(
|
|
41
|
+
test("workspace user administration example makes route and action scope explicit", async () => {
|
|
42
|
+
const packageManifest = JSON.parse(await readFile(
|
|
43
|
+
path.join(patternRoot, "example", "packages", "users-workspace", "package.json"),
|
|
88
44
|
"utf8"
|
|
89
|
-
);
|
|
90
|
-
const
|
|
91
|
-
path.join(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
"utf8"
|
|
101
|
-
);
|
|
102
|
-
const serviceSource = await readFile(
|
|
103
|
-
path.join(PACKAGE_ROOT, "templates/packages/users/src/server/service.js"),
|
|
104
|
-
"utf8"
|
|
105
|
-
);
|
|
106
|
-
const routesSource = await readFile(
|
|
107
|
-
path.join(PACKAGE_ROOT, "templates/packages/users/src/server/registerRoutes.js"),
|
|
45
|
+
));
|
|
46
|
+
const feature = await readFile(
|
|
47
|
+
path.join(
|
|
48
|
+
patternRoot,
|
|
49
|
+
"example",
|
|
50
|
+
"packages",
|
|
51
|
+
"users-workspace",
|
|
52
|
+
"src",
|
|
53
|
+
"server",
|
|
54
|
+
"UsersWorkspaceFeature.js"
|
|
55
|
+
),
|
|
108
56
|
"utf8"
|
|
109
57
|
);
|
|
110
58
|
|
|
111
|
-
assert.
|
|
112
|
-
assert.
|
|
113
|
-
assert.
|
|
114
|
-
assert.match(
|
|
115
|
-
assert.match(
|
|
116
|
-
assert.match(
|
|
117
|
-
assert.
|
|
118
|
-
assert.match(
|
|
119
|
-
assert.doesNotMatch(
|
|
120
|
-
assert.doesNotMatch(providerSource, /createCrudLookup/);
|
|
121
|
-
assert.doesNotMatch(providerSource, /lookup\.users/);
|
|
122
|
-
assert.doesNotMatch(providerSource, /normalizeRecordId/);
|
|
123
|
-
assert.doesNotMatch(providerSource, /requires application singleton\(\)\/service\(\)\/actions\(\)\./);
|
|
124
|
-
assert.match(providerSource, /createJsonRestResourceScopeOptions/);
|
|
125
|
-
assert.match(providerSource, /addResourceIfMissing\(\s*api,\s*"users",\s*createJsonRestResourceScopeOptions\(resource,/s);
|
|
126
|
-
assert.match(repositorySource, /api\.resources\.users\.query\(/);
|
|
127
|
-
assert.match(repositorySource, /api\.resources\.users\.get\(/);
|
|
128
|
-
assert.match(repositorySource, /async function queryDocuments\(query = \{\}, options = \{\}\)/);
|
|
129
|
-
assert.match(repositorySource, /async function getDocumentById\(recordId, query = \{\}, options = \{\}\)/);
|
|
130
|
-
assert.match(repositorySource, /buildJsonRestQueryParams\(RESOURCE_TYPE, query\)/);
|
|
131
|
-
assert.match(repositorySource, /returnBadRequestWhenJsonRestFieldsetInvalid/);
|
|
132
|
-
assert.match(repositorySource, /returnNullWhenJsonRestResourceMissing/);
|
|
133
|
-
assert.doesNotMatch(actionsSource, /workspaceSlugParamsValidator/);
|
|
134
|
-
assert.doesNotMatch(actionsSource, /delete query\.workspaceSlug/);
|
|
135
|
-
assert.doesNotMatch(actionsSource, /requireActionSurface/);
|
|
136
|
-
assert.match(actionsSource, /createStandardCrudListQueryValidators\(\{ resource \}\)/);
|
|
137
|
-
assert.match(actionsSource, /createStandardCrudViewQueryValidators\(\)/);
|
|
138
|
-
assert.match(actionsSource, /const \{ recordId, \.\.\.query \} = input \|\| \{\};/);
|
|
139
|
-
assert.match(actionsSource, /usersService\.getDocumentById\(recordId, query,/);
|
|
140
|
-
assert.match(actionsSource, /output: null/);
|
|
141
|
-
assert.match(actionsSource, /usersService\.queryDocuments/);
|
|
142
|
-
assert.match(actionsSource, /usersService\.getDocumentById/);
|
|
143
|
-
assert.doesNotMatch(actionsSource, /from "\.\/actionIds\.js"/);
|
|
144
|
-
assert.match(actionsSource, /id: "crud\.users\.list"/);
|
|
145
|
-
assert.match(actionsSource, /id: "crud\.users\.view"/);
|
|
146
|
-
assert.doesNotMatch(serviceSource, /serviceEvents/);
|
|
147
|
-
assert.match(serviceSource, /throw new TypeError\("createService requires usersRepository\."\);/);
|
|
148
|
-
assert.match(serviceSource, /return404IfNotFound/);
|
|
149
|
-
assert.match(serviceSource, /throw new AppError\(404, "Document not found\."\);/);
|
|
150
|
-
assert.match(serviceSource, /returnJsonApiDocument/);
|
|
151
|
-
assert.doesNotMatch(routesSource, /workspaceRouteInput/);
|
|
152
|
-
assert.match(routesSource, /createJsonApiResourceRouteContract/);
|
|
153
|
-
assert.doesNotMatch(routesSource, /wrapResponse/);
|
|
154
|
-
assert.match(routesSource, /routeBase: "\/"/);
|
|
155
|
-
assert.match(routesSource, /orderBy: resource\.defaultSort/);
|
|
59
|
+
assert.equal(packageManifest.name, "@app/users-workspace");
|
|
60
|
+
assert.equal(packageManifest.jskit?.mutations, undefined);
|
|
61
|
+
assert.deepEqual(packageManifest.jskit?.capabilities?.provides, ["app.users-workspace"]);
|
|
62
|
+
assert.match(feature, /ownershipFilter: "workspace"/);
|
|
63
|
+
assert.match(feature, /routeBase: "\/w\/:workspaceSlug"/);
|
|
64
|
+
assert.match(feature, /actionInputValidator: workspaceSlugParamsValidator/);
|
|
65
|
+
assert.match(feature, /inputKeys: \["workspaceSlug"\]/);
|
|
66
|
+
assert.match(feature, /buildWorkspaceInputFromRouteParams/);
|
|
67
|
+
assert.doesNotMatch(feature, /\.make\(|\.singleton\(|containerToken|repositoryToken|serviceToken/);
|
|
156
68
|
});
|
|
157
69
|
|
|
158
|
-
test("
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
path.join(PACKAGE_ROOT, "templates/packages/users-workspace/src/server/UsersProvider.js"),
|
|
165
|
-
"utf8"
|
|
166
|
-
);
|
|
167
|
-
const actionsSource = await readFile(
|
|
168
|
-
path.join(PACKAGE_ROOT, "templates/packages/users-workspace/src/server/actions.js"),
|
|
169
|
-
"utf8"
|
|
170
|
-
);
|
|
171
|
-
const routesSource = await readFile(
|
|
172
|
-
path.join(PACKAGE_ROOT, "templates/packages/users-workspace/src/server/registerRoutes.js"),
|
|
173
|
-
"utf8"
|
|
174
|
-
);
|
|
175
|
-
const serviceSource = await readFile(
|
|
176
|
-
path.join(PACKAGE_ROOT, "templates/packages/users/src/server/service.js"),
|
|
177
|
-
"utf8"
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
assert.match(packageManifestSource, /@jskit-ai\/workspaces-core/);
|
|
181
|
-
assert.match(packageManifestSource, /@jskit-ai\/json-rest-api-core/);
|
|
182
|
-
assert.match(packageManifestSource, /json-rest-api\.core/);
|
|
183
|
-
assert.match(packageManifestSource, /"scaffoldShape": "users-core-crud-v1"/);
|
|
184
|
-
assert.match(packageManifestSource, /"tableName": "users"/);
|
|
185
|
-
assert.match(packageManifestSource, /"provenance": "users-core-template"/);
|
|
186
|
-
assert.doesNotMatch(packageManifestSource, /server\/actionIds/);
|
|
187
|
-
assert.match(providerSource, /surface: "admin"/);
|
|
188
|
-
assert.match(providerSource, /routeSurfaceRequiresWorkspace/);
|
|
189
|
-
assert.doesNotMatch(providerSource, /createCrudLookup/);
|
|
190
|
-
assert.doesNotMatch(providerSource, /lookup\.users/);
|
|
191
|
-
assert.doesNotMatch(providerSource, /normalizeRecordId/);
|
|
192
|
-
assert.doesNotMatch(providerSource, /requires application singleton\(\)\/service\(\)\/actions\(\)\./);
|
|
193
|
-
assert.match(providerSource, /createJsonRestResourceScopeOptions/);
|
|
194
|
-
assert.match(providerSource, /addResourceIfMissing\(\s*api,\s*"users",\s*createJsonRestResourceScopeOptions\(resource,/s);
|
|
195
|
-
assert.match(actionsSource, /workspaceSlugParamsValidator/);
|
|
196
|
-
assert.match(actionsSource, /delete query\.workspaceSlug/);
|
|
197
|
-
assert.doesNotMatch(actionsSource, /requireActionSurface/);
|
|
198
|
-
assert.match(actionsSource, /createStandardCrudListQueryValidators\(\{ resource \}\)/);
|
|
199
|
-
assert.match(actionsSource, /createStandardCrudViewQueryValidators\(\)/);
|
|
200
|
-
assert.match(actionsSource, /const \{ workspaceSlug, recordId, \.\.\.query \} = input \|\| \{\};/);
|
|
201
|
-
assert.match(actionsSource, /usersService\.getDocumentById\(recordId, query,/);
|
|
202
|
-
assert.match(actionsSource, /usersService\.queryDocuments/);
|
|
203
|
-
assert.match(actionsSource, /usersService\.getDocumentById/);
|
|
204
|
-
assert.doesNotMatch(actionsSource, /from "\.\/actionIds\.js"/);
|
|
205
|
-
assert.match(actionsSource, /id: "crud\.users\.list"/);
|
|
206
|
-
assert.match(actionsSource, /id: "crud\.users\.view"/);
|
|
207
|
-
assert.match(routesSource, /buildWorkspaceInputFromRouteParams/);
|
|
208
|
-
assert.match(routesSource, /createJsonApiResourceRouteContract/);
|
|
209
|
-
assert.doesNotMatch(routesSource, /wrapResponse/);
|
|
210
|
-
assert.match(routesSource, /routeBase: routeSurfaceRequiresWorkspace === true \? "\/w\/:workspaceSlug" : "\/"/);
|
|
211
|
-
assert.doesNotMatch(serviceSource, /serviceEvents/);
|
|
212
|
-
assert.match(serviceSource, /throw new TypeError\("createService requires usersRepository\."\);/);
|
|
213
|
-
assert.match(serviceSource, /returnJsonApiDocument/);
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
test("users-core local users resource scaffold stays read-only and canonical", async () => {
|
|
217
|
-
const resourceModule = await import(
|
|
218
|
-
pathToFileURL(path.join(PACKAGE_ROOT, "templates/packages/users/src/shared/userResource.js")).href
|
|
219
|
-
);
|
|
220
|
-
const resource = resourceModule?.resource;
|
|
70
|
+
test("both user administration variants stay read-only and canonical", async () => {
|
|
71
|
+
for (const packageName of ["users", "users-workspace"]) {
|
|
72
|
+
const resourceModule = await import(pathToFileURL(
|
|
73
|
+
path.join(patternRoot, "example", "packages", packageName, "src", "shared", "userResource.js")
|
|
74
|
+
).href);
|
|
75
|
+
const resource = resourceModule.resource;
|
|
221
76
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
assert.equal(resource.schema.updatedAt?.storage?.writeSerializer, "datetime-utc");
|
|
77
|
+
assert.deepEqual(Object.keys(resource.operations), ["list", "view"]);
|
|
78
|
+
assert.equal(Object.hasOwn(resource.operations, "create"), false);
|
|
79
|
+
assert.equal(resource.schema.updatedAt?.storage?.writeSerializer, "datetime-utc");
|
|
80
|
+
}
|
|
227
81
|
});
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test from "node:test";
|
|
3
|
-
import {
|
|
4
|
-
import { INTERNAL_JSON_REST_API } from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
|
|
3
|
+
import { readSingleMultipartFile } from "@jskit-ai/uploads-runtime/server/multipart/readSingleMultipartFile";
|
|
5
4
|
import { createRouter } from "../../kernel/server/http/lib/router.js";
|
|
6
5
|
import { validateOperationSection } from "../../http-runtime/src/shared/validators/operationValidation.js";
|
|
7
6
|
import { userProfileResource } from "../src/shared/resources/userProfileResource.js";
|
|
7
|
+
import { registerAccountProfileRoutes } from "../src/server/accountProfile/bootAccountProfileRoutes.js";
|
|
8
|
+
import { registerAccountPreferencesRoutes } from "../src/server/accountPreferences/bootAccountPreferencesRoutes.js";
|
|
9
|
+
import { registerAccountNotificationsRoutes } from "../src/server/accountNotifications/bootAccountNotificationsRoutes.js";
|
|
10
|
+
import { registerAccountSecurityRoutes } from "../src/server/accountSecurity/bootAccountSecurityRoutes.js";
|
|
8
11
|
|
|
9
12
|
function createReplyDouble() {
|
|
10
13
|
return {
|
|
@@ -34,49 +37,22 @@ async function registerRoutes({
|
|
|
34
37
|
authService = {}
|
|
35
38
|
} = {}) {
|
|
36
39
|
const router = createRouter();
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
const accountProfileService = {
|
|
41
|
+
async readAvatar() {
|
|
42
|
+
return {
|
|
43
|
+
mimeType: "image/png",
|
|
44
|
+
buffer: Buffer.from([])
|
|
45
|
+
};
|
|
41
46
|
}
|
|
42
47
|
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
async readAvatar() {
|
|
52
|
-
return {
|
|
53
|
-
mimeType: "image/png",
|
|
54
|
-
buffer: Buffer.from([])
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
],
|
|
59
|
-
["actionExecutor", {}]
|
|
60
|
-
]);
|
|
61
|
-
|
|
62
|
-
const app = {
|
|
63
|
-
has(token) {
|
|
64
|
-
return bindings.has(token);
|
|
65
|
-
},
|
|
66
|
-
instance(token, value) {
|
|
67
|
-
bindings.set(token, value);
|
|
68
|
-
return this;
|
|
69
|
-
},
|
|
70
|
-
make(token) {
|
|
71
|
-
if (!bindings.has(token)) {
|
|
72
|
-
throw new Error(`Missing test binding for token: ${String(token)}`);
|
|
73
|
-
}
|
|
74
|
-
return bindings.get(token);
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const provider = new UsersCoreServiceProvider();
|
|
79
|
-
await provider.boot(app);
|
|
48
|
+
registerAccountProfileRoutes(router, {
|
|
49
|
+
accountProfileService,
|
|
50
|
+
authService,
|
|
51
|
+
uploads: { readSingleMultipartFile }
|
|
52
|
+
});
|
|
53
|
+
registerAccountPreferencesRoutes(router);
|
|
54
|
+
registerAccountNotificationsRoutes(router);
|
|
55
|
+
registerAccountSecurityRoutes(router, { authService });
|
|
80
56
|
|
|
81
57
|
return router.list();
|
|
82
58
|
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { USERS_SHARED_API } from "../shared/index.js";
|
|
2
|
-
import {
|
|
3
|
-
INTERNAL_JSON_REST_API,
|
|
4
|
-
addResourceIfMissing,
|
|
5
|
-
createJsonRestResourceScopeOptions
|
|
6
|
-
} from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
|
|
7
|
-
import { toDatabaseDateTimeUtc } from "@jskit-ai/database-runtime/shared";
|
|
8
|
-
import { bootAccountProfileRoutes } from "./accountProfile/bootAccountProfileRoutes.js";
|
|
9
|
-
import { bootAccountPreferencesRoutes } from "./accountPreferences/bootAccountPreferencesRoutes.js";
|
|
10
|
-
import { bootAccountNotificationsRoutes } from "./accountNotifications/bootAccountNotificationsRoutes.js";
|
|
11
|
-
import { bootAccountSecurityRoutes } from "./accountSecurity/bootAccountSecurityRoutes.js";
|
|
12
|
-
import { registerSharedApi } from "./common/registerSharedApi.js";
|
|
13
|
-
import { registerCommonRepositories } from "./common/registerCommonRepositories.js";
|
|
14
|
-
import { registerUsersCore } from "./registerUsersCore.js";
|
|
15
|
-
import { registerUsersBootstrap } from "./registerUsersBootstrap.js";
|
|
16
|
-
import { registerAccountPreferences } from "./accountPreferences/registerAccountPreferences.js";
|
|
17
|
-
import { registerAccountNotifications } from "./accountNotifications/registerAccountNotifications.js";
|
|
18
|
-
import { registerAccountProfile } from "./accountProfile/registerAccountProfile.js";
|
|
19
|
-
import { registerAccountSecurity } from "./accountSecurity/registerAccountSecurity.js";
|
|
20
|
-
import { userProfileResource } from "../shared/resources/userProfileResource.js";
|
|
21
|
-
import { userSettingsResource } from "../shared/resources/userSettingsResource.js";
|
|
22
|
-
|
|
23
|
-
class UsersCoreServiceProvider {
|
|
24
|
-
static id = "users.core";
|
|
25
|
-
|
|
26
|
-
static startsAfter = ["json-rest-api.core", "runtime.actions"];
|
|
27
|
-
|
|
28
|
-
async register(app) {
|
|
29
|
-
registerSharedApi(app, USERS_SHARED_API);
|
|
30
|
-
registerCommonRepositories(app);
|
|
31
|
-
registerUsersCore(app);
|
|
32
|
-
registerUsersBootstrap(app);
|
|
33
|
-
|
|
34
|
-
registerAccountProfile(app);
|
|
35
|
-
registerAccountPreferences(app);
|
|
36
|
-
registerAccountNotifications(app);
|
|
37
|
-
registerAccountSecurity(app);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async boot(app) {
|
|
41
|
-
const api = app.make(INTERNAL_JSON_REST_API);
|
|
42
|
-
const scopeOptions = {
|
|
43
|
-
writeSerializers: {
|
|
44
|
-
"datetime-utc": toDatabaseDateTimeUtc
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
await addResourceIfMissing(api, "userProfiles", createJsonRestResourceScopeOptions(userProfileResource, scopeOptions));
|
|
48
|
-
await addResourceIfMissing(api, "userSettings", createJsonRestResourceScopeOptions(userSettingsResource, scopeOptions));
|
|
49
|
-
bootAccountProfileRoutes(app);
|
|
50
|
-
bootAccountPreferencesRoutes(app);
|
|
51
|
-
bootAccountNotificationsRoutes(app);
|
|
52
|
-
bootAccountSecurityRoutes(app);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export { UsersCoreServiceProvider };
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
|
|
2
|
-
import { createService as createAccountNotificationsService } from "./accountNotificationsService.js";
|
|
3
|
-
import { accountNotificationsActions } from "./accountNotificationsActions.js";
|
|
4
|
-
import { deepFreeze } from "../common/support/deepFreeze.js";
|
|
5
|
-
import { ACCOUNT_SETTINGS_AND_BOOTSTRAP_EVENTS } from "../common/support/realtimeServiceEvents.js";
|
|
6
|
-
|
|
7
|
-
function registerAccountNotifications(app) {
|
|
8
|
-
if (!app || typeof app.singleton !== "function" || typeof app.service !== "function" || typeof app.actions !== "function") {
|
|
9
|
-
throw new Error("registerAccountNotifications requires application singleton()/service()/actions().");
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
app.service(
|
|
13
|
-
"users.accountNotifications.service",
|
|
14
|
-
(scope) =>
|
|
15
|
-
createAccountNotificationsService({
|
|
16
|
-
userSettingsRepository: scope.make("internal.repository.user-settings"),
|
|
17
|
-
userProfilesRepository: scope.make("internal.repository.user-profiles"),
|
|
18
|
-
authService: scope.make("authService")
|
|
19
|
-
}),
|
|
20
|
-
{
|
|
21
|
-
events: deepFreeze({
|
|
22
|
-
updateNotifications: ACCOUNT_SETTINGS_AND_BOOTSTRAP_EVENTS
|
|
23
|
-
})
|
|
24
|
-
}
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
app.actions(
|
|
28
|
-
withActionDefaults(accountNotificationsActions, {
|
|
29
|
-
domain: "settings",
|
|
30
|
-
dependencies: {
|
|
31
|
-
accountNotificationsService: "users.accountNotifications.service"
|
|
32
|
-
}
|
|
33
|
-
})
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export { registerAccountNotifications };
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
|
|
2
|
-
import { createService as createAccountPreferencesService } from "./accountPreferencesService.js";
|
|
3
|
-
import { accountPreferencesActions } from "./accountPreferencesActions.js";
|
|
4
|
-
import { deepFreeze } from "../common/support/deepFreeze.js";
|
|
5
|
-
import { ACCOUNT_SETTINGS_AND_BOOTSTRAP_EVENTS } from "../common/support/realtimeServiceEvents.js";
|
|
6
|
-
|
|
7
|
-
function registerAccountPreferences(app) {
|
|
8
|
-
if (!app || typeof app.singleton !== "function" || typeof app.service !== "function" || typeof app.actions !== "function") {
|
|
9
|
-
throw new Error("registerAccountPreferences requires application singleton()/service()/actions().");
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
app.service(
|
|
13
|
-
"users.accountPreferences.service",
|
|
14
|
-
(scope) =>
|
|
15
|
-
createAccountPreferencesService({
|
|
16
|
-
userSettingsRepository: scope.make("internal.repository.user-settings"),
|
|
17
|
-
userProfilesRepository: scope.make("internal.repository.user-profiles"),
|
|
18
|
-
authService: scope.make("authService")
|
|
19
|
-
}),
|
|
20
|
-
{
|
|
21
|
-
events: deepFreeze({
|
|
22
|
-
updatePreferences: ACCOUNT_SETTINGS_AND_BOOTSTRAP_EVENTS
|
|
23
|
-
})
|
|
24
|
-
}
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
app.actions(
|
|
28
|
-
withActionDefaults(accountPreferencesActions, {
|
|
29
|
-
domain: "settings",
|
|
30
|
-
dependencies: {
|
|
31
|
-
accountPreferencesService: "users.accountPreferences.service"
|
|
32
|
-
}
|
|
33
|
-
})
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export { registerAccountPreferences };
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
|
|
2
|
-
import { createService as createAccountProfileService } from "./accountProfileService.js";
|
|
3
|
-
import { createService as createAvatarStorageService } from "./avatarStorageService.js";
|
|
4
|
-
import { createService as createAvatarService } from "./avatarService.js";
|
|
5
|
-
import { accountProfileActions } from "./accountProfileActions.js";
|
|
6
|
-
import { deepFreeze } from "../common/support/deepFreeze.js";
|
|
7
|
-
import { ACCOUNT_SETTINGS_AND_BOOTSTRAP_EVENTS } from "../common/support/realtimeServiceEvents.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
function registerAccountProfile(app) {
|
|
11
|
-
if (!app || typeof app.singleton !== "function" || typeof app.service !== "function" || typeof app.actions !== "function") {
|
|
12
|
-
throw new Error("registerAccountProfile requires application singleton()/service()/actions().");
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
app.singleton("users.avatar.storage.service", (scope) =>
|
|
16
|
-
createAvatarStorageService({
|
|
17
|
-
storage: scope.make("jskit.storage")
|
|
18
|
-
})
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
app.singleton("users.avatar.service", (scope) =>
|
|
22
|
-
createAvatarService({
|
|
23
|
-
userProfilesRepository: scope.make("internal.repository.user-profiles"),
|
|
24
|
-
avatarStorageService: scope.make("users.avatar.storage.service")
|
|
25
|
-
})
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
app.service(
|
|
29
|
-
"users.accountProfile.service",
|
|
30
|
-
(scope) =>
|
|
31
|
-
createAccountProfileService({
|
|
32
|
-
userSettingsRepository: scope.make("internal.repository.user-settings"),
|
|
33
|
-
userProfilesRepository: scope.make("internal.repository.user-profiles"),
|
|
34
|
-
authService: scope.make("authService"),
|
|
35
|
-
avatarService: scope.make("users.avatar.service")
|
|
36
|
-
}),
|
|
37
|
-
{
|
|
38
|
-
events: deepFreeze({
|
|
39
|
-
updateProfile: ACCOUNT_SETTINGS_AND_BOOTSTRAP_EVENTS,
|
|
40
|
-
uploadAvatar: ACCOUNT_SETTINGS_AND_BOOTSTRAP_EVENTS,
|
|
41
|
-
deleteAvatar: ACCOUNT_SETTINGS_AND_BOOTSTRAP_EVENTS
|
|
42
|
-
})
|
|
43
|
-
}
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
app.actions(
|
|
47
|
-
withActionDefaults(accountProfileActions, {
|
|
48
|
-
domain: "settings",
|
|
49
|
-
dependencies: {
|
|
50
|
-
accountProfileService: "users.accountProfile.service"
|
|
51
|
-
}
|
|
52
|
-
})
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export { registerAccountProfile };
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
|
|
2
|
-
import { createService as createAccountSecurityService } from "./accountSecurityService.js";
|
|
3
|
-
import { accountSecurityActions } from "./accountSecurityActions.js";
|
|
4
|
-
|
|
5
|
-
function registerAccountSecurity(app) {
|
|
6
|
-
if (!app || typeof app.singleton !== "function" || typeof app.actions !== "function") {
|
|
7
|
-
throw new Error("registerAccountSecurity requires application singleton()/actions().");
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
app.singleton("users.accountSecurity.service", (scope) => {
|
|
11
|
-
const authService = scope.has("authService") ? scope.make("authService") : null;
|
|
12
|
-
return createAccountSecurityService({
|
|
13
|
-
userSettingsRepository: scope.make("internal.repository.user-settings"),
|
|
14
|
-
userProfilesRepository: scope.make("internal.repository.user-profiles"),
|
|
15
|
-
authService
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
app.actions(
|
|
20
|
-
withActionDefaults(accountSecurityActions, {
|
|
21
|
-
domain: "settings",
|
|
22
|
-
dependencies: {
|
|
23
|
-
accountSecurityService: "users.accountSecurity.service"
|
|
24
|
-
}
|
|
25
|
-
})
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export { registerAccountSecurity };
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { INTERNAL_JSON_REST_API } from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
|
|
2
|
-
import { createRepository as createUserProfilesRepository } from "./repositories/userProfilesRepository.js";
|
|
3
|
-
import { createRepository as createUserSettingsRepository } from "./repositories/userSettingsRepository.js";
|
|
4
|
-
|
|
5
|
-
function registerCommonRepositories(app) {
|
|
6
|
-
if (!app || typeof app.singleton !== "function") {
|
|
7
|
-
throw new Error("registerCommonRepositories requires application singleton().");
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
app.singleton("internal.repository.user-settings", (scope) => {
|
|
11
|
-
const api = scope.make(INTERNAL_JSON_REST_API);
|
|
12
|
-
const knex = scope.make("jskit.database.knex");
|
|
13
|
-
return createUserSettingsRepository({ api, knex });
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
app.singleton("internal.repository.user-profiles", (scope) => {
|
|
17
|
-
const api = scope.make(INTERNAL_JSON_REST_API);
|
|
18
|
-
const knex = scope.make("jskit.database.knex");
|
|
19
|
-
return createUserProfilesRepository({ api, knex });
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export { registerCommonRepositories };
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
function registerSharedApi(app, usersCoreApi) {
|
|
2
|
-
if (!app || typeof app.singleton !== "function") {
|
|
3
|
-
throw new Error("registerSharedApi requires application singleton().");
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
app.singleton("users.core", () => usersCoreApi);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export { registerSharedApi };
|