@apito-io/js-admin-sdk 2.7.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -12
- package/dist/index.d.mts +102 -40
- package/dist/index.d.ts +102 -40
- package/dist/index.js +62 -28
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +62 -28
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/client.test.ts +9 -9
- package/src/client.ts +274 -57
- package/src/index.ts +8 -3
- package/src/types.ts +79 -24
- package/src/version.ts +1 -1
package/src/client.ts
CHANGED
|
@@ -11,27 +11,44 @@ import {
|
|
|
11
11
|
ApitoError,
|
|
12
12
|
ValidationError,
|
|
13
13
|
InjectedDBOperationInterface,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
LoginUserResponse,
|
|
15
|
+
User,
|
|
16
|
+
UsersResponse,
|
|
17
17
|
TenantByDomainResponse,
|
|
18
18
|
TenantCatalogSearchRow,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
LoginUserParams,
|
|
20
|
+
CreateUserParams,
|
|
21
|
+
UpdateUserParams,
|
|
22
|
+
GoogleOAuthStateResponse,
|
|
23
|
+
ProjectStorageSettings,
|
|
24
|
+
UpdateProjectStorageInput,
|
|
25
|
+
SystemFile,
|
|
26
|
+
SystemFilesListResponse,
|
|
27
|
+
SystemFileUploadParams,
|
|
28
|
+
DeleteSystemFilesResponse,
|
|
22
29
|
} from './types';
|
|
23
30
|
|
|
31
|
+
function deriveRestBaseURL(graphqlURL: string): string {
|
|
32
|
+
const u = graphqlURL.trim().replace(/\/$/, '');
|
|
33
|
+
if (u.endsWith('/graphql')) {
|
|
34
|
+
return u.slice(0, -'/graphql'.length);
|
|
35
|
+
}
|
|
36
|
+
return u;
|
|
37
|
+
}
|
|
38
|
+
|
|
24
39
|
/**
|
|
25
40
|
* Apito SDK Client - JavaScript implementation matching the Go SDK
|
|
26
41
|
*/
|
|
27
42
|
export class ApitoClient implements InjectedDBOperationInterface {
|
|
28
43
|
private httpClient: AxiosInstance;
|
|
29
44
|
private baseURL: string;
|
|
45
|
+
private restBaseURL: string;
|
|
30
46
|
private apiKey: string;
|
|
31
47
|
private tenantId?: string;
|
|
32
48
|
|
|
33
49
|
constructor(config: ClientConfig) {
|
|
34
50
|
this.baseURL = config.baseURL;
|
|
51
|
+
this.restBaseURL = (config.restBaseURL ?? '').trim() || deriveRestBaseURL(config.baseURL);
|
|
35
52
|
this.apiKey = config.apiKey;
|
|
36
53
|
this.tenantId = config.tenantId;
|
|
37
54
|
|
|
@@ -147,10 +164,73 @@ export class ApitoClient implements InjectedDBOperationInterface {
|
|
|
147
164
|
return data.token;
|
|
148
165
|
}
|
|
149
166
|
|
|
167
|
+
private authHeaders(tenantId?: string): Record<string, string> {
|
|
168
|
+
const headers: Record<string, string> = {
|
|
169
|
+
...(this.apiKey.startsWith('cli-') || this.apiKey.startsWith('sdk-')
|
|
170
|
+
? { 'X-Apito-Sync-Key': this.apiKey }
|
|
171
|
+
: { 'X-Apito-Key': this.apiKey }),
|
|
172
|
+
};
|
|
173
|
+
const tid = tenantId ?? this.tenantId;
|
|
174
|
+
if (tid) {
|
|
175
|
+
headers['X-Apito-Tenant-ID'] = tid;
|
|
176
|
+
}
|
|
177
|
+
return headers;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private async executeREST<T>(
|
|
181
|
+
method: 'GET' | 'POST',
|
|
182
|
+
path: string,
|
|
183
|
+
options?: {
|
|
184
|
+
query?: Record<string, string | number | undefined>;
|
|
185
|
+
jsonBody?: Record<string, unknown>;
|
|
186
|
+
formData?: FormData;
|
|
187
|
+
allowFailure?: boolean;
|
|
188
|
+
}
|
|
189
|
+
): Promise<T> {
|
|
190
|
+
const url = new URL(`${this.restBaseURL.replace(/\/$/, '')}${path}`);
|
|
191
|
+
if (options?.query) {
|
|
192
|
+
for (const [k, v] of Object.entries(options.query)) {
|
|
193
|
+
if (v !== undefined && v !== '') {
|
|
194
|
+
url.searchParams.set(k, String(v));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const headers = this.authHeaders();
|
|
199
|
+
let data: FormData | Record<string, unknown> | undefined;
|
|
200
|
+
if (options?.formData) {
|
|
201
|
+
data = options.formData;
|
|
202
|
+
} else if (options?.jsonBody) {
|
|
203
|
+
headers['Content-Type'] = 'application/json';
|
|
204
|
+
data = options.jsonBody;
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
const response = await this.httpClient.request({
|
|
208
|
+
method,
|
|
209
|
+
url: url.toString(),
|
|
210
|
+
headers,
|
|
211
|
+
data,
|
|
212
|
+
maxBodyLength: Infinity,
|
|
213
|
+
maxContentLength: Infinity,
|
|
214
|
+
});
|
|
215
|
+
const body = response.data as Record<string, unknown>;
|
|
216
|
+
if (body.success === false && !options?.allowFailure) {
|
|
217
|
+
throw new ValidationError(String(body.message ?? 'request failed'));
|
|
218
|
+
}
|
|
219
|
+
return body as T;
|
|
220
|
+
} catch (error) {
|
|
221
|
+
if (axios.isAxiosError(error)) {
|
|
222
|
+
const msg =
|
|
223
|
+
(error.response?.data as { message?: string })?.message ?? error.message;
|
|
224
|
+
throw new ApitoError(msg, 'HTTP_ERROR', error.response?.status, error.response?.data);
|
|
225
|
+
}
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
150
230
|
/**
|
|
151
|
-
*
|
|
231
|
+
* Project user login (system GraphQL `loginUser`). Password path: pass `password` and `email` or `phone` per project Authentication settings. Google OAuth path: `authMethod: 'google'` with `code` and `state` from the redirect; call `googleOAuthState(projectId)` before opening Google to obtain `state`.
|
|
152
232
|
*/
|
|
153
|
-
async
|
|
233
|
+
async loginUser(params: LoginUserParams): Promise<LoginUserResponse> {
|
|
154
234
|
const authMethod = (params.authMethod ?? 'general').trim().toLowerCase() || 'general';
|
|
155
235
|
const variables: Record<string, any> = {
|
|
156
236
|
project_id: params.projectId,
|
|
@@ -181,8 +261,8 @@ export class ApitoClient implements InjectedDBOperationInterface {
|
|
|
181
261
|
}
|
|
182
262
|
|
|
183
263
|
const query = `
|
|
184
|
-
query
|
|
185
|
-
|
|
264
|
+
query LoginUser($project_id: String!, $password: String, $auth_method: String, $email: String, $phone: String, $code: String, $state: String) {
|
|
265
|
+
loginUser(project_id: $project_id, password: $password, auth_method: $auth_method, email: $email, phone: $phone, code: $code, state: $state) {
|
|
186
266
|
token
|
|
187
267
|
user {
|
|
188
268
|
id
|
|
@@ -199,48 +279,48 @@ export class ApitoClient implements InjectedDBOperationInterface {
|
|
|
199
279
|
}
|
|
200
280
|
`;
|
|
201
281
|
const response = await this.executeGraphQL(query, variables);
|
|
202
|
-
const raw = response.data?.
|
|
282
|
+
const raw = response.data?.loginUser;
|
|
203
283
|
if (!raw?.token) {
|
|
204
|
-
throw new ValidationError('Invalid response format for
|
|
284
|
+
throw new ValidationError('Invalid response format for loginUser');
|
|
205
285
|
}
|
|
206
286
|
return {
|
|
207
287
|
token: raw.token as string,
|
|
208
|
-
user: raw.user as
|
|
288
|
+
user: raw.user as User | undefined,
|
|
209
289
|
};
|
|
210
290
|
}
|
|
211
291
|
|
|
212
292
|
/**
|
|
213
|
-
* Signed OAuth state for
|
|
293
|
+
* Signed OAuth state for Google login (system query `googleOAuthState`). Use in the authorize URL together with project `google_client_id` and the configured redirect URI.
|
|
214
294
|
*/
|
|
215
|
-
async
|
|
295
|
+
async googleOAuthState(projectId: string): Promise<GoogleOAuthStateResponse> {
|
|
216
296
|
const query = `
|
|
217
|
-
query
|
|
218
|
-
|
|
297
|
+
query GoogleOAuthState($project_id: String!) {
|
|
298
|
+
googleOAuthState(project_id: $project_id) {
|
|
219
299
|
state
|
|
220
300
|
}
|
|
221
301
|
}
|
|
222
302
|
`;
|
|
223
303
|
const variables = { project_id: projectId };
|
|
224
304
|
const response = await this.executeGraphQL(query, variables);
|
|
225
|
-
const raw = response.data?.
|
|
305
|
+
const raw = response.data?.googleOAuthState;
|
|
226
306
|
const state = typeof raw?.state === 'string' ? raw.state.trim() : '';
|
|
227
307
|
if (!state) {
|
|
228
|
-
throw new ValidationError('Invalid response format for
|
|
308
|
+
throw new ValidationError('Invalid response format for googleOAuthState');
|
|
229
309
|
}
|
|
230
310
|
return { state };
|
|
231
311
|
}
|
|
232
312
|
|
|
233
313
|
/**
|
|
234
|
-
* Search
|
|
314
|
+
* Search project end-users.
|
|
235
315
|
*/
|
|
236
|
-
async
|
|
316
|
+
async searchUsers(
|
|
237
317
|
projectId: string,
|
|
238
318
|
limit?: number,
|
|
239
319
|
offset?: number
|
|
240
|
-
): Promise<
|
|
320
|
+
): Promise<UsersResponse> {
|
|
241
321
|
const query = `
|
|
242
|
-
query
|
|
243
|
-
|
|
322
|
+
query SearchUsers($project_id: String!, $limit: Int, $offset: Int) {
|
|
323
|
+
searchUsers(project_id: $project_id, limit: $limit, offset: $offset) {
|
|
244
324
|
count
|
|
245
325
|
users {
|
|
246
326
|
id
|
|
@@ -260,15 +340,15 @@ export class ApitoClient implements InjectedDBOperationInterface {
|
|
|
260
340
|
if (limit !== undefined) variables.limit = limit;
|
|
261
341
|
if (offset !== undefined) variables.offset = offset;
|
|
262
342
|
const response = await this.executeGraphQL(query, variables);
|
|
263
|
-
const raw = response.data?.
|
|
343
|
+
const raw = response.data?.searchUsers;
|
|
264
344
|
if (!raw) {
|
|
265
|
-
throw new ValidationError('Invalid response format for
|
|
345
|
+
throw new ValidationError('Invalid response format for searchUsers');
|
|
266
346
|
}
|
|
267
347
|
let count = 0;
|
|
268
348
|
if (typeof raw.count === 'number') {
|
|
269
349
|
count = raw.count;
|
|
270
350
|
}
|
|
271
|
-
const users = (raw.users ?? []) as
|
|
351
|
+
const users = (raw.users ?? []) as User[];
|
|
272
352
|
return { users, count };
|
|
273
353
|
}
|
|
274
354
|
|
|
@@ -303,19 +383,16 @@ export class ApitoClient implements InjectedDBOperationInterface {
|
|
|
303
383
|
}
|
|
304
384
|
|
|
305
385
|
/**
|
|
306
|
-
* Create a
|
|
386
|
+
* Create a project user (local password). Use `email` and/or `phone` per engine validation for the project identifier mode.
|
|
307
387
|
*/
|
|
308
|
-
async
|
|
309
|
-
projectId: string,
|
|
310
|
-
params: CreateTenantUserParams
|
|
311
|
-
): Promise<TenantUser> {
|
|
388
|
+
async createUser(projectId: string, params: CreateUserParams): Promise<User> {
|
|
312
389
|
const password = (params.password ?? '').trim();
|
|
313
390
|
if (!password) {
|
|
314
391
|
throw new ValidationError('password is required');
|
|
315
392
|
}
|
|
316
393
|
const query = `
|
|
317
|
-
mutation
|
|
318
|
-
|
|
394
|
+
mutation CreateUser($project_id: String!, $password: String!, $role: String, $email: String, $phone: String) {
|
|
395
|
+
createUser(project_id: $project_id, password: $password, role: $role, email: $email, phone: $phone) {
|
|
319
396
|
id
|
|
320
397
|
email
|
|
321
398
|
phone
|
|
@@ -339,32 +416,27 @@ export class ApitoClient implements InjectedDBOperationInterface {
|
|
|
339
416
|
const phone = (params.phone ?? '').trim();
|
|
340
417
|
if (phone) variables.phone = phone;
|
|
341
418
|
const response = await this.executeGraphQL(query, variables);
|
|
342
|
-
const u = response.data?.
|
|
419
|
+
const u = response.data?.createUser;
|
|
343
420
|
if (!u?.id) {
|
|
344
|
-
throw new ValidationError('Invalid response format for
|
|
421
|
+
throw new ValidationError('Invalid response format for createUser');
|
|
345
422
|
}
|
|
346
|
-
return u as
|
|
423
|
+
return u as User;
|
|
347
424
|
}
|
|
348
425
|
|
|
349
426
|
/**
|
|
350
|
-
* Update a
|
|
427
|
+
* Update a project user. Project scope is implied by the API key. Only include fields to change.
|
|
351
428
|
*/
|
|
352
|
-
async
|
|
429
|
+
async updateUser(userId: string, params: UpdateUserParams): Promise<User> {
|
|
353
430
|
const uid = (userId ?? '').trim();
|
|
354
431
|
if (!uid) {
|
|
355
432
|
throw new ValidationError('userId is required');
|
|
356
433
|
}
|
|
357
|
-
if (
|
|
358
|
-
params.email === undefined &&
|
|
359
|
-
params.phone === undefined &&
|
|
360
|
-
params.password === undefined &&
|
|
361
|
-
params.role === undefined
|
|
362
|
-
) {
|
|
434
|
+
if (params.email === undefined && params.phone === undefined && params.role === undefined) {
|
|
363
435
|
throw new ValidationError('at least one field must be provided');
|
|
364
436
|
}
|
|
365
437
|
const query = `
|
|
366
|
-
mutation
|
|
367
|
-
|
|
438
|
+
mutation UpdateUser($user_id: String!, $email: String, $phone: String, $role: String) {
|
|
439
|
+
updateUser(user_id: $user_id, email: $email, phone: $phone, role: $role) {
|
|
368
440
|
id
|
|
369
441
|
email
|
|
370
442
|
phone
|
|
@@ -380,37 +452,182 @@ export class ApitoClient implements InjectedDBOperationInterface {
|
|
|
380
452
|
const variables: Record<string, any> = { user_id: uid };
|
|
381
453
|
if (params.email !== undefined) variables.email = params.email;
|
|
382
454
|
if (params.phone !== undefined) variables.phone = params.phone;
|
|
383
|
-
if (params.password !== undefined) variables.password = params.password;
|
|
384
455
|
if (params.role !== undefined) variables.role = params.role;
|
|
385
456
|
const response = await this.executeGraphQL(query, variables);
|
|
386
|
-
const u = response.data?.
|
|
457
|
+
const u = response.data?.updateUser;
|
|
387
458
|
if (!u?.id) {
|
|
388
|
-
throw new ValidationError('Invalid response format for
|
|
459
|
+
throw new ValidationError('Invalid response format for updateUser');
|
|
460
|
+
}
|
|
461
|
+
return u as User;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/** Set a new password for a project user (admin mutation resetUserPassword). */
|
|
465
|
+
async resetUserPassword(userId: string, password: string): Promise<boolean> {
|
|
466
|
+
const uid = (userId ?? '').trim();
|
|
467
|
+
if (!uid) {
|
|
468
|
+
throw new ValidationError('userId is required');
|
|
389
469
|
}
|
|
390
|
-
|
|
470
|
+
if (!(password ?? '').trim()) {
|
|
471
|
+
throw new ValidationError('password is required');
|
|
472
|
+
}
|
|
473
|
+
const query = `
|
|
474
|
+
mutation ResetUserPassword($user_id: String!, $password: String!) {
|
|
475
|
+
resetUserPassword(user_id: $user_id, password: $password)
|
|
476
|
+
}
|
|
477
|
+
`;
|
|
478
|
+
const response = await this.executeGraphQL(query, { user_id: uid, password });
|
|
479
|
+
const ok = response.data?.resetUserPassword;
|
|
480
|
+
if (typeof ok !== 'boolean') {
|
|
481
|
+
throw new ValidationError('Invalid response format for resetUserPassword');
|
|
482
|
+
}
|
|
483
|
+
return ok;
|
|
391
484
|
}
|
|
392
485
|
|
|
393
486
|
/**
|
|
394
|
-
* Delete a
|
|
487
|
+
* Delete a project user by id. Project scope is implied by the API key.
|
|
395
488
|
*/
|
|
396
|
-
async
|
|
489
|
+
async deleteUser(userId: string): Promise<boolean> {
|
|
397
490
|
const uid = (userId ?? '').trim();
|
|
398
491
|
if (!uid) {
|
|
399
492
|
throw new ValidationError('userId is required');
|
|
400
493
|
}
|
|
401
494
|
const query = `
|
|
402
|
-
mutation
|
|
403
|
-
|
|
495
|
+
mutation DeleteUser($user_id: String!) {
|
|
496
|
+
deleteUser(user_id: $user_id)
|
|
404
497
|
}
|
|
405
498
|
`;
|
|
406
499
|
const response = await this.executeGraphQL(query, { user_id: uid });
|
|
407
|
-
const ok = response.data?.
|
|
500
|
+
const ok = response.data?.deleteUser;
|
|
408
501
|
if (typeof ok !== 'boolean') {
|
|
409
|
-
throw new ValidationError('Invalid response format for
|
|
502
|
+
throw new ValidationError('Invalid response format for deleteUser');
|
|
410
503
|
}
|
|
411
504
|
return ok;
|
|
412
505
|
}
|
|
413
506
|
|
|
507
|
+
/** Read project storage settings via getProject. */
|
|
508
|
+
async getProjectStorageSettings(projectId: string): Promise<ProjectStorageSettings> {
|
|
509
|
+
const query = `
|
|
510
|
+
query GetProjectStorageSettings($_id: String!) {
|
|
511
|
+
getProject(_id: $_id) {
|
|
512
|
+
storage_settings {
|
|
513
|
+
use_free_cloud_storage
|
|
514
|
+
endpoint
|
|
515
|
+
region
|
|
516
|
+
bucket
|
|
517
|
+
access_key_id
|
|
518
|
+
has_secret_access_key
|
|
519
|
+
public_base_url
|
|
520
|
+
force_path_style
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
`;
|
|
525
|
+
const response = await this.executeGraphQL(query, { _id: projectId });
|
|
526
|
+
const settings = response.data?.getProject?.storage_settings;
|
|
527
|
+
if (!settings) {
|
|
528
|
+
throw new ValidationError('Invalid response format for getProjectStorageSettings');
|
|
529
|
+
}
|
|
530
|
+
return settings as ProjectStorageSettings;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/** Persist project storage settings. */
|
|
534
|
+
async updateProjectStorageSettings(
|
|
535
|
+
input: UpdateProjectStorageInput
|
|
536
|
+
): Promise<ProjectStorageSettings> {
|
|
537
|
+
const query = `
|
|
538
|
+
mutation UpdateProjectStorageSettings($input: UpdateProjectStorageInput!) {
|
|
539
|
+
updateProjectStorageSettings(input: $input) {
|
|
540
|
+
storage_settings {
|
|
541
|
+
use_free_cloud_storage
|
|
542
|
+
endpoint
|
|
543
|
+
region
|
|
544
|
+
bucket
|
|
545
|
+
access_key_id
|
|
546
|
+
has_secret_access_key
|
|
547
|
+
public_base_url
|
|
548
|
+
force_path_style
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
`;
|
|
553
|
+
const response = await this.executeGraphQL(query, { input });
|
|
554
|
+
const settings = response.data?.updateProjectStorageSettings?.storage_settings;
|
|
555
|
+
if (!settings) {
|
|
556
|
+
throw new ValidationError('Invalid response format for updateProjectStorageSettings');
|
|
557
|
+
}
|
|
558
|
+
return settings as ProjectStorageSettings;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/** Upload a file via POST /system/files/upload. */
|
|
562
|
+
async uploadSystemFile(params: SystemFileUploadParams): Promise<SystemFile> {
|
|
563
|
+
const size =
|
|
564
|
+
params.content instanceof ArrayBuffer
|
|
565
|
+
? params.content.byteLength
|
|
566
|
+
: params.content.byteLength;
|
|
567
|
+
if (!params.content || size === 0) {
|
|
568
|
+
throw new ValidationError('file content is required');
|
|
569
|
+
}
|
|
570
|
+
const fileName = (params.fileName ?? '').trim() || 'upload';
|
|
571
|
+
const form = new FormData();
|
|
572
|
+
const bytes =
|
|
573
|
+
params.content instanceof ArrayBuffer ? new Uint8Array(params.content) : params.content;
|
|
574
|
+
const blob = new Blob([bytes as BlobPart]);
|
|
575
|
+
form.append('file', blob, fileName);
|
|
576
|
+
if (params.fileType?.trim()) {
|
|
577
|
+
form.append('file_type', params.fileType.trim());
|
|
578
|
+
}
|
|
579
|
+
const body = await this.executeREST<{ file: SystemFile }>('POST', '/files/upload', {
|
|
580
|
+
formData: form,
|
|
581
|
+
});
|
|
582
|
+
if (!body.file?.id) {
|
|
583
|
+
throw new ValidationError('Invalid response format for uploadSystemFile');
|
|
584
|
+
}
|
|
585
|
+
return body.file;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/** List files via GET /system/files/list. */
|
|
589
|
+
async listSystemFiles(
|
|
590
|
+
fileType?: string,
|
|
591
|
+
limit?: number,
|
|
592
|
+
offset?: number
|
|
593
|
+
): Promise<SystemFilesListResponse> {
|
|
594
|
+
const body = await this.executeREST<{
|
|
595
|
+
files: SystemFile[];
|
|
596
|
+
total: number;
|
|
597
|
+
}>('GET', '/files/list', {
|
|
598
|
+
query: {
|
|
599
|
+
file_type: fileType,
|
|
600
|
+
limit,
|
|
601
|
+
offset,
|
|
602
|
+
},
|
|
603
|
+
});
|
|
604
|
+
return {
|
|
605
|
+
files: body.files ?? [],
|
|
606
|
+
total: body.total ?? 0,
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/** Delete files via POST /system/files/delete. */
|
|
611
|
+
async deleteSystemFiles(ids: string[]): Promise<DeleteSystemFilesResponse> {
|
|
612
|
+
if (!ids?.length) {
|
|
613
|
+
throw new ValidationError('ids are required');
|
|
614
|
+
}
|
|
615
|
+
const body = await this.executeREST<DeleteSystemFilesResponse>('POST', '/files/delete', {
|
|
616
|
+
jsonBody: { ids },
|
|
617
|
+
allowFailure: true,
|
|
618
|
+
});
|
|
619
|
+
const result: DeleteSystemFilesResponse = {
|
|
620
|
+
success: !!body.success,
|
|
621
|
+
deleted_ids: body.deleted_ids ?? [],
|
|
622
|
+
storage_failed: body.storage_failed,
|
|
623
|
+
message: body.message,
|
|
624
|
+
};
|
|
625
|
+
if (!result.success && result.message) {
|
|
626
|
+
throw new ValidationError(result.message);
|
|
627
|
+
}
|
|
628
|
+
return result;
|
|
629
|
+
}
|
|
630
|
+
|
|
414
631
|
/**
|
|
415
632
|
* Get a single resource by model and ID
|
|
416
633
|
*/
|
package/src/index.ts
CHANGED
|
@@ -24,9 +24,14 @@ export type {
|
|
|
24
24
|
ApitoError,
|
|
25
25
|
ValidationError,
|
|
26
26
|
InjectedDBOperationInterface,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
LoginUserParams,
|
|
28
|
+
CreateUserParams,
|
|
29
|
+
UpdateUserParams,
|
|
30
|
+
User,
|
|
31
|
+
ProjectStorageSettings,
|
|
32
|
+
UpdateProjectStorageInput,
|
|
33
|
+
SystemFile,
|
|
34
|
+
SystemFileUploadParams,
|
|
30
35
|
} from './types';
|
|
31
36
|
|
|
32
37
|
// Default export for convenience
|
package/src/types.ts
CHANGED
|
@@ -86,63 +86,110 @@ export interface CreateAndUpdateRequest {
|
|
|
86
86
|
forceUpdate?: boolean;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
/**
|
|
90
|
-
export interface
|
|
89
|
+
/** Project end-user from engine system DB (table project_users). */
|
|
90
|
+
export interface User {
|
|
91
91
|
id: string;
|
|
92
92
|
email?: string;
|
|
93
93
|
phone?: string;
|
|
94
94
|
role: string;
|
|
95
|
-
tenant_id
|
|
95
|
+
tenant_id?: string;
|
|
96
96
|
provider?: string;
|
|
97
97
|
status?: string;
|
|
98
98
|
created_at?: string;
|
|
99
99
|
updated_at?: string;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
/** Login via system GraphQL `
|
|
103
|
-
export interface
|
|
102
|
+
/** Login via system GraphQL `loginUser`. Password path: use `email` or `phone` per project settings. Google OAuth code path: `authMethod: 'google'`, `code`, `state` from redirect (get `state` first via `googleOAuthState`). */
|
|
103
|
+
export interface LoginUserParams {
|
|
104
104
|
projectId: string;
|
|
105
|
-
/** Required for general (password) login. */
|
|
106
105
|
password?: string;
|
|
107
106
|
email?: string;
|
|
108
107
|
phone?: string;
|
|
109
|
-
/** `general` (default) or `google`. */
|
|
110
108
|
authMethod?: string;
|
|
111
|
-
/** Google authorization code (with `authMethod: 'google'`). */
|
|
112
109
|
code?: string;
|
|
113
|
-
/** OAuth state from `tenantGoogleOAuthState` or callback (with `authMethod: 'google'`). */
|
|
114
110
|
state?: string;
|
|
115
111
|
}
|
|
116
112
|
|
|
117
|
-
export interface
|
|
113
|
+
export interface GoogleOAuthStateResponse {
|
|
118
114
|
state: string;
|
|
119
115
|
}
|
|
120
116
|
|
|
121
|
-
export interface
|
|
117
|
+
export interface CreateUserParams {
|
|
122
118
|
password: string;
|
|
123
119
|
role?: string;
|
|
124
120
|
email?: string;
|
|
125
121
|
phone?: string;
|
|
126
122
|
}
|
|
127
123
|
|
|
128
|
-
/** Optional fields for `
|
|
129
|
-
export interface
|
|
124
|
+
/** Optional fields for `updateUser`; omitted keys are not sent. */
|
|
125
|
+
export interface UpdateUserParams {
|
|
130
126
|
email?: string;
|
|
131
127
|
phone?: string;
|
|
132
|
-
password?: string;
|
|
133
128
|
role?: string;
|
|
134
129
|
}
|
|
135
130
|
|
|
136
|
-
export interface
|
|
131
|
+
export interface LoginUserResponse {
|
|
137
132
|
token: string;
|
|
138
|
-
user?:
|
|
133
|
+
user?: User;
|
|
139
134
|
}
|
|
140
135
|
|
|
141
|
-
export interface
|
|
142
|
-
users:
|
|
136
|
+
export interface UsersResponse {
|
|
137
|
+
users: User[];
|
|
143
138
|
count: number;
|
|
144
139
|
}
|
|
145
140
|
|
|
141
|
+
export interface ProjectStorageSettings {
|
|
142
|
+
use_free_cloud_storage: boolean;
|
|
143
|
+
endpoint?: string | null;
|
|
144
|
+
region?: string | null;
|
|
145
|
+
bucket?: string | null;
|
|
146
|
+
access_key_id?: string | null;
|
|
147
|
+
has_secret_access_key: boolean;
|
|
148
|
+
public_base_url?: string | null;
|
|
149
|
+
force_path_style?: boolean | null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface UpdateProjectStorageInput {
|
|
153
|
+
use_free_cloud_storage?: boolean;
|
|
154
|
+
endpoint?: string;
|
|
155
|
+
region?: string;
|
|
156
|
+
bucket?: string;
|
|
157
|
+
access_key_id?: string;
|
|
158
|
+
secret_access_key?: string;
|
|
159
|
+
public_base_url?: string;
|
|
160
|
+
force_path_style?: boolean;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export interface SystemFile {
|
|
164
|
+
id: string;
|
|
165
|
+
file_type: string;
|
|
166
|
+
file_name: string;
|
|
167
|
+
file_extension?: string;
|
|
168
|
+
content_type?: string;
|
|
169
|
+
size: number;
|
|
170
|
+
url: string;
|
|
171
|
+
created_by?: string;
|
|
172
|
+
created_at?: string;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface SystemFilesListResponse {
|
|
176
|
+
files: SystemFile[];
|
|
177
|
+
total: number;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export interface SystemFileUploadParams {
|
|
181
|
+
fileName: string;
|
|
182
|
+
content: Uint8Array | ArrayBuffer;
|
|
183
|
+
fileType?: string;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export interface DeleteSystemFilesResponse {
|
|
187
|
+
success: boolean;
|
|
188
|
+
deleted_ids: string[];
|
|
189
|
+
storage_failed?: string[];
|
|
190
|
+
message?: string;
|
|
191
|
+
}
|
|
192
|
+
|
|
146
193
|
/** One SaaS catalog tenant row from searchTenantsByDomain. */
|
|
147
194
|
export interface TenantCatalogSearchRow {
|
|
148
195
|
id: string;
|
|
@@ -158,6 +205,8 @@ export interface TenantByDomainResponse {
|
|
|
158
205
|
|
|
159
206
|
export interface ClientConfig {
|
|
160
207
|
baseURL: string;
|
|
208
|
+
/** REST base (e.g. http://host:5050/system); derived from baseURL when omitted */
|
|
209
|
+
restBaseURL?: string;
|
|
161
210
|
apiKey: string;
|
|
162
211
|
timeout?: number;
|
|
163
212
|
httpClient?: any;
|
|
@@ -192,13 +241,19 @@ export interface InjectedDBOperationInterface {
|
|
|
192
241
|
): Promise<GraphQLResponse>;
|
|
193
242
|
/** @param token Legacy; ignored. Auth uses client API key. */
|
|
194
243
|
generateTenantToken(tenantId: string, duration?: string, role?: string): Promise<string>;
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
244
|
+
loginUser(params: LoginUserParams): Promise<LoginUserResponse>;
|
|
245
|
+
googleOAuthState(projectId: string): Promise<GoogleOAuthStateResponse>;
|
|
246
|
+
searchUsers(projectId: string, limit?: number, offset?: number): Promise<UsersResponse>;
|
|
198
247
|
searchTenantsByDomain(projectId: string, domain: string): Promise<TenantByDomainResponse>;
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
248
|
+
createUser(projectId: string, params: CreateUserParams): Promise<User>;
|
|
249
|
+
updateUser(userId: string, params: UpdateUserParams): Promise<User>;
|
|
250
|
+
resetUserPassword(userId: string, password: string): Promise<boolean>;
|
|
251
|
+
deleteUser(userId: string): Promise<boolean>;
|
|
252
|
+
getProjectStorageSettings(projectId: string): Promise<ProjectStorageSettings>;
|
|
253
|
+
updateProjectStorageSettings(input: UpdateProjectStorageInput): Promise<ProjectStorageSettings>;
|
|
254
|
+
uploadSystemFile(params: SystemFileUploadParams): Promise<SystemFile>;
|
|
255
|
+
listSystemFiles(fileType?: string, limit?: number, offset?: number): Promise<SystemFilesListResponse>;
|
|
256
|
+
deleteSystemFiles(ids: string[]): Promise<DeleteSystemFilesResponse>;
|
|
202
257
|
getSingleResource(model: string, id: string, singlePageData?: boolean): Promise<DefaultDocumentStructure>;
|
|
203
258
|
searchResources(model: string, filter?: Record<string, any>, aggregate?: boolean): Promise<SearchResult>;
|
|
204
259
|
getRelationDocuments(id: string, connection: Record<string, any>): Promise<SearchResult>;
|