@apito-io/js-admin-sdk 2.7.0 → 3.1.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 +23 -12
- package/dist/index.d.mts +76 -40
- package/dist/index.d.ts +76 -40
- package/dist/index.js +31 -27
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +32 -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 +218 -57
- package/src/index.ts +8 -3
- package/src/types.ts +55 -24
- package/src/version.ts +1 -1
package/src/client.ts
CHANGED
|
@@ -11,27 +11,42 @@ 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
|
+
File,
|
|
24
|
+
FilesListResponse,
|
|
25
|
+
UploadFileParams,
|
|
26
|
+
DeleteFilesResponse,
|
|
22
27
|
} from './types';
|
|
23
28
|
|
|
29
|
+
function deriveRestBaseURL(graphqlURL: string): string {
|
|
30
|
+
const u = graphqlURL.trim().replace(/\/$/, '');
|
|
31
|
+
if (u.endsWith('/graphql')) {
|
|
32
|
+
return u.slice(0, -'/graphql'.length);
|
|
33
|
+
}
|
|
34
|
+
return u;
|
|
35
|
+
}
|
|
36
|
+
|
|
24
37
|
/**
|
|
25
38
|
* Apito SDK Client - JavaScript implementation matching the Go SDK
|
|
26
39
|
*/
|
|
27
40
|
export class ApitoClient implements InjectedDBOperationInterface {
|
|
28
41
|
private httpClient: AxiosInstance;
|
|
29
42
|
private baseURL: string;
|
|
43
|
+
private restBaseURL: string;
|
|
30
44
|
private apiKey: string;
|
|
31
45
|
private tenantId?: string;
|
|
32
46
|
|
|
33
47
|
constructor(config: ClientConfig) {
|
|
34
48
|
this.baseURL = config.baseURL;
|
|
49
|
+
this.restBaseURL = (config.restBaseURL ?? '').trim() || deriveRestBaseURL(config.baseURL);
|
|
35
50
|
this.apiKey = config.apiKey;
|
|
36
51
|
this.tenantId = config.tenantId;
|
|
37
52
|
|
|
@@ -147,10 +162,73 @@ export class ApitoClient implements InjectedDBOperationInterface {
|
|
|
147
162
|
return data.token;
|
|
148
163
|
}
|
|
149
164
|
|
|
165
|
+
private authHeaders(tenantId?: string): Record<string, string> {
|
|
166
|
+
const headers: Record<string, string> = {
|
|
167
|
+
...(this.apiKey.startsWith('cli-') || this.apiKey.startsWith('sdk-')
|
|
168
|
+
? { 'X-Apito-Sync-Key': this.apiKey }
|
|
169
|
+
: { 'X-Apito-Key': this.apiKey }),
|
|
170
|
+
};
|
|
171
|
+
const tid = tenantId ?? this.tenantId;
|
|
172
|
+
if (tid) {
|
|
173
|
+
headers['X-Apito-Tenant-ID'] = tid;
|
|
174
|
+
}
|
|
175
|
+
return headers;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private async executeREST<T>(
|
|
179
|
+
method: 'GET' | 'POST',
|
|
180
|
+
path: string,
|
|
181
|
+
options?: {
|
|
182
|
+
query?: Record<string, string | number | undefined>;
|
|
183
|
+
jsonBody?: Record<string, unknown>;
|
|
184
|
+
formData?: FormData;
|
|
185
|
+
allowFailure?: boolean;
|
|
186
|
+
}
|
|
187
|
+
): Promise<T> {
|
|
188
|
+
const url = new URL(`${this.restBaseURL.replace(/\/$/, '')}${path}`);
|
|
189
|
+
if (options?.query) {
|
|
190
|
+
for (const [k, v] of Object.entries(options.query)) {
|
|
191
|
+
if (v !== undefined && v !== '') {
|
|
192
|
+
url.searchParams.set(k, String(v));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const headers = this.authHeaders();
|
|
197
|
+
let data: FormData | Record<string, unknown> | undefined;
|
|
198
|
+
if (options?.formData) {
|
|
199
|
+
data = options.formData;
|
|
200
|
+
} else if (options?.jsonBody) {
|
|
201
|
+
headers['Content-Type'] = 'application/json';
|
|
202
|
+
data = options.jsonBody;
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
const response = await this.httpClient.request({
|
|
206
|
+
method,
|
|
207
|
+
url: url.toString(),
|
|
208
|
+
headers,
|
|
209
|
+
data,
|
|
210
|
+
maxBodyLength: Infinity,
|
|
211
|
+
maxContentLength: Infinity,
|
|
212
|
+
});
|
|
213
|
+
const body = response.data as Record<string, unknown>;
|
|
214
|
+
if (body.success === false && !options?.allowFailure) {
|
|
215
|
+
throw new ValidationError(String(body.message ?? 'request failed'));
|
|
216
|
+
}
|
|
217
|
+
return body as T;
|
|
218
|
+
} catch (error) {
|
|
219
|
+
if (axios.isAxiosError(error)) {
|
|
220
|
+
const msg =
|
|
221
|
+
(error.response?.data as { message?: string })?.message ?? error.message;
|
|
222
|
+
throw new ApitoError(msg, 'HTTP_ERROR', error.response?.status, error.response?.data);
|
|
223
|
+
}
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
150
228
|
/**
|
|
151
|
-
*
|
|
229
|
+
* 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
230
|
*/
|
|
153
|
-
async
|
|
231
|
+
async loginUser(params: LoginUserParams): Promise<LoginUserResponse> {
|
|
154
232
|
const authMethod = (params.authMethod ?? 'general').trim().toLowerCase() || 'general';
|
|
155
233
|
const variables: Record<string, any> = {
|
|
156
234
|
project_id: params.projectId,
|
|
@@ -181,8 +259,8 @@ export class ApitoClient implements InjectedDBOperationInterface {
|
|
|
181
259
|
}
|
|
182
260
|
|
|
183
261
|
const query = `
|
|
184
|
-
query
|
|
185
|
-
|
|
262
|
+
query LoginUser($project_id: String!, $password: String, $auth_method: String, $email: String, $phone: String, $code: String, $state: String) {
|
|
263
|
+
loginUser(project_id: $project_id, password: $password, auth_method: $auth_method, email: $email, phone: $phone, code: $code, state: $state) {
|
|
186
264
|
token
|
|
187
265
|
user {
|
|
188
266
|
id
|
|
@@ -199,48 +277,48 @@ export class ApitoClient implements InjectedDBOperationInterface {
|
|
|
199
277
|
}
|
|
200
278
|
`;
|
|
201
279
|
const response = await this.executeGraphQL(query, variables);
|
|
202
|
-
const raw = response.data?.
|
|
280
|
+
const raw = response.data?.loginUser;
|
|
203
281
|
if (!raw?.token) {
|
|
204
|
-
throw new ValidationError('Invalid response format for
|
|
282
|
+
throw new ValidationError('Invalid response format for loginUser');
|
|
205
283
|
}
|
|
206
284
|
return {
|
|
207
285
|
token: raw.token as string,
|
|
208
|
-
user: raw.user as
|
|
286
|
+
user: raw.user as User | undefined,
|
|
209
287
|
};
|
|
210
288
|
}
|
|
211
289
|
|
|
212
290
|
/**
|
|
213
|
-
* Signed OAuth state for
|
|
291
|
+
* 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
292
|
*/
|
|
215
|
-
async
|
|
293
|
+
async googleOAuthState(projectId: string): Promise<GoogleOAuthStateResponse> {
|
|
216
294
|
const query = `
|
|
217
|
-
query
|
|
218
|
-
|
|
295
|
+
query GoogleOAuthState($project_id: String!) {
|
|
296
|
+
googleOAuthState(project_id: $project_id) {
|
|
219
297
|
state
|
|
220
298
|
}
|
|
221
299
|
}
|
|
222
300
|
`;
|
|
223
301
|
const variables = { project_id: projectId };
|
|
224
302
|
const response = await this.executeGraphQL(query, variables);
|
|
225
|
-
const raw = response.data?.
|
|
303
|
+
const raw = response.data?.googleOAuthState;
|
|
226
304
|
const state = typeof raw?.state === 'string' ? raw.state.trim() : '';
|
|
227
305
|
if (!state) {
|
|
228
|
-
throw new ValidationError('Invalid response format for
|
|
306
|
+
throw new ValidationError('Invalid response format for googleOAuthState');
|
|
229
307
|
}
|
|
230
308
|
return { state };
|
|
231
309
|
}
|
|
232
310
|
|
|
233
311
|
/**
|
|
234
|
-
* Search
|
|
312
|
+
* Search project end-users.
|
|
235
313
|
*/
|
|
236
|
-
async
|
|
314
|
+
async searchUsers(
|
|
237
315
|
projectId: string,
|
|
238
316
|
limit?: number,
|
|
239
317
|
offset?: number
|
|
240
|
-
): Promise<
|
|
318
|
+
): Promise<UsersResponse> {
|
|
241
319
|
const query = `
|
|
242
|
-
query
|
|
243
|
-
|
|
320
|
+
query SearchUsers($project_id: String!, $limit: Int, $offset: Int) {
|
|
321
|
+
searchUsers(project_id: $project_id, limit: $limit, offset: $offset) {
|
|
244
322
|
count
|
|
245
323
|
users {
|
|
246
324
|
id
|
|
@@ -260,15 +338,15 @@ export class ApitoClient implements InjectedDBOperationInterface {
|
|
|
260
338
|
if (limit !== undefined) variables.limit = limit;
|
|
261
339
|
if (offset !== undefined) variables.offset = offset;
|
|
262
340
|
const response = await this.executeGraphQL(query, variables);
|
|
263
|
-
const raw = response.data?.
|
|
341
|
+
const raw = response.data?.searchUsers;
|
|
264
342
|
if (!raw) {
|
|
265
|
-
throw new ValidationError('Invalid response format for
|
|
343
|
+
throw new ValidationError('Invalid response format for searchUsers');
|
|
266
344
|
}
|
|
267
345
|
let count = 0;
|
|
268
346
|
if (typeof raw.count === 'number') {
|
|
269
347
|
count = raw.count;
|
|
270
348
|
}
|
|
271
|
-
const users = (raw.users ?? []) as
|
|
349
|
+
const users = (raw.users ?? []) as User[];
|
|
272
350
|
return { users, count };
|
|
273
351
|
}
|
|
274
352
|
|
|
@@ -303,19 +381,16 @@ export class ApitoClient implements InjectedDBOperationInterface {
|
|
|
303
381
|
}
|
|
304
382
|
|
|
305
383
|
/**
|
|
306
|
-
* Create a
|
|
384
|
+
* Create a project user (local password). Use `email` and/or `phone` per engine validation for the project identifier mode.
|
|
307
385
|
*/
|
|
308
|
-
async
|
|
309
|
-
projectId: string,
|
|
310
|
-
params: CreateTenantUserParams
|
|
311
|
-
): Promise<TenantUser> {
|
|
386
|
+
async createUser(projectId: string, params: CreateUserParams): Promise<User> {
|
|
312
387
|
const password = (params.password ?? '').trim();
|
|
313
388
|
if (!password) {
|
|
314
389
|
throw new ValidationError('password is required');
|
|
315
390
|
}
|
|
316
391
|
const query = `
|
|
317
|
-
mutation
|
|
318
|
-
|
|
392
|
+
mutation CreateUser($project_id: String!, $password: String!, $role: String, $email: String, $phone: String) {
|
|
393
|
+
createUser(project_id: $project_id, password: $password, role: $role, email: $email, phone: $phone) {
|
|
319
394
|
id
|
|
320
395
|
email
|
|
321
396
|
phone
|
|
@@ -339,32 +414,27 @@ export class ApitoClient implements InjectedDBOperationInterface {
|
|
|
339
414
|
const phone = (params.phone ?? '').trim();
|
|
340
415
|
if (phone) variables.phone = phone;
|
|
341
416
|
const response = await this.executeGraphQL(query, variables);
|
|
342
|
-
const u = response.data?.
|
|
417
|
+
const u = response.data?.createUser;
|
|
343
418
|
if (!u?.id) {
|
|
344
|
-
throw new ValidationError('Invalid response format for
|
|
419
|
+
throw new ValidationError('Invalid response format for createUser');
|
|
345
420
|
}
|
|
346
|
-
return u as
|
|
421
|
+
return u as User;
|
|
347
422
|
}
|
|
348
423
|
|
|
349
424
|
/**
|
|
350
|
-
* Update a
|
|
425
|
+
* Update a project user. Project scope is implied by the API key. Only include fields to change.
|
|
351
426
|
*/
|
|
352
|
-
async
|
|
427
|
+
async updateUser(userId: string, params: UpdateUserParams): Promise<User> {
|
|
353
428
|
const uid = (userId ?? '').trim();
|
|
354
429
|
if (!uid) {
|
|
355
430
|
throw new ValidationError('userId is required');
|
|
356
431
|
}
|
|
357
|
-
if (
|
|
358
|
-
params.email === undefined &&
|
|
359
|
-
params.phone === undefined &&
|
|
360
|
-
params.password === undefined &&
|
|
361
|
-
params.role === undefined
|
|
362
|
-
) {
|
|
432
|
+
if (params.email === undefined && params.phone === undefined && params.role === undefined) {
|
|
363
433
|
throw new ValidationError('at least one field must be provided');
|
|
364
434
|
}
|
|
365
435
|
const query = `
|
|
366
|
-
mutation
|
|
367
|
-
|
|
436
|
+
mutation UpdateUser($user_id: String!, $email: String, $phone: String, $role: String) {
|
|
437
|
+
updateUser(user_id: $user_id, email: $email, phone: $phone, role: $role) {
|
|
368
438
|
id
|
|
369
439
|
email
|
|
370
440
|
phone
|
|
@@ -380,37 +450,128 @@ export class ApitoClient implements InjectedDBOperationInterface {
|
|
|
380
450
|
const variables: Record<string, any> = { user_id: uid };
|
|
381
451
|
if (params.email !== undefined) variables.email = params.email;
|
|
382
452
|
if (params.phone !== undefined) variables.phone = params.phone;
|
|
383
|
-
if (params.password !== undefined) variables.password = params.password;
|
|
384
453
|
if (params.role !== undefined) variables.role = params.role;
|
|
385
454
|
const response = await this.executeGraphQL(query, variables);
|
|
386
|
-
const u = response.data?.
|
|
455
|
+
const u = response.data?.updateUser;
|
|
387
456
|
if (!u?.id) {
|
|
388
|
-
throw new ValidationError('Invalid response format for
|
|
457
|
+
throw new ValidationError('Invalid response format for updateUser');
|
|
389
458
|
}
|
|
390
|
-
return u as
|
|
459
|
+
return u as User;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/** Set a new password for a project user (admin mutation resetUserPassword). */
|
|
463
|
+
async resetUserPassword(userId: string, password: string): Promise<boolean> {
|
|
464
|
+
const uid = (userId ?? '').trim();
|
|
465
|
+
if (!uid) {
|
|
466
|
+
throw new ValidationError('userId is required');
|
|
467
|
+
}
|
|
468
|
+
if (!(password ?? '').trim()) {
|
|
469
|
+
throw new ValidationError('password is required');
|
|
470
|
+
}
|
|
471
|
+
const query = `
|
|
472
|
+
mutation ResetUserPassword($user_id: String!, $password: String!) {
|
|
473
|
+
resetUserPassword(user_id: $user_id, password: $password)
|
|
474
|
+
}
|
|
475
|
+
`;
|
|
476
|
+
const response = await this.executeGraphQL(query, { user_id: uid, password });
|
|
477
|
+
const ok = response.data?.resetUserPassword;
|
|
478
|
+
if (typeof ok !== 'boolean') {
|
|
479
|
+
throw new ValidationError('Invalid response format for resetUserPassword');
|
|
480
|
+
}
|
|
481
|
+
return ok;
|
|
391
482
|
}
|
|
392
483
|
|
|
393
484
|
/**
|
|
394
|
-
* Delete a
|
|
485
|
+
* Delete a project user by id. Project scope is implied by the API key.
|
|
395
486
|
*/
|
|
396
|
-
async
|
|
487
|
+
async deleteUser(userId: string): Promise<boolean> {
|
|
397
488
|
const uid = (userId ?? '').trim();
|
|
398
489
|
if (!uid) {
|
|
399
490
|
throw new ValidationError('userId is required');
|
|
400
491
|
}
|
|
401
492
|
const query = `
|
|
402
|
-
mutation
|
|
403
|
-
|
|
493
|
+
mutation DeleteUser($user_id: String!) {
|
|
494
|
+
deleteUser(user_id: $user_id)
|
|
404
495
|
}
|
|
405
496
|
`;
|
|
406
497
|
const response = await this.executeGraphQL(query, { user_id: uid });
|
|
407
|
-
const ok = response.data?.
|
|
498
|
+
const ok = response.data?.deleteUser;
|
|
408
499
|
if (typeof ok !== 'boolean') {
|
|
409
|
-
throw new ValidationError('Invalid response format for
|
|
500
|
+
throw new ValidationError('Invalid response format for deleteUser');
|
|
410
501
|
}
|
|
411
502
|
return ok;
|
|
412
503
|
}
|
|
413
504
|
|
|
505
|
+
/** Upload a file via POST /system/files/upload. */
|
|
506
|
+
async uploadFile(params: UploadFileParams): Promise<File> {
|
|
507
|
+
const size =
|
|
508
|
+
params.content instanceof ArrayBuffer
|
|
509
|
+
? params.content.byteLength
|
|
510
|
+
: params.content.byteLength;
|
|
511
|
+
if (!params.content || size === 0) {
|
|
512
|
+
throw new ValidationError('file content is required');
|
|
513
|
+
}
|
|
514
|
+
const fileName = (params.fileName ?? '').trim() || 'upload';
|
|
515
|
+
const form = new FormData();
|
|
516
|
+
const bytes =
|
|
517
|
+
params.content instanceof ArrayBuffer ? new Uint8Array(params.content) : params.content;
|
|
518
|
+
const blob = new Blob([bytes as BlobPart]);
|
|
519
|
+
form.append('file', blob, fileName);
|
|
520
|
+
if (params.fileType?.trim()) {
|
|
521
|
+
form.append('file_type', params.fileType.trim());
|
|
522
|
+
}
|
|
523
|
+
const body = await this.executeREST<{ file: File }>('POST', '/files/upload', {
|
|
524
|
+
formData: form,
|
|
525
|
+
});
|
|
526
|
+
if (!body.file?.id) {
|
|
527
|
+
throw new ValidationError('Invalid response format for uploadFile');
|
|
528
|
+
}
|
|
529
|
+
return body.file;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/** List files via GET /system/files/list. */
|
|
533
|
+
async listFiles(
|
|
534
|
+
fileType?: string,
|
|
535
|
+
limit?: number,
|
|
536
|
+
offset?: number
|
|
537
|
+
): Promise<FilesListResponse> {
|
|
538
|
+
const body = await this.executeREST<{
|
|
539
|
+
files: File[];
|
|
540
|
+
total: number;
|
|
541
|
+
}>('GET', '/files/list', {
|
|
542
|
+
query: {
|
|
543
|
+
file_type: fileType,
|
|
544
|
+
limit,
|
|
545
|
+
offset,
|
|
546
|
+
},
|
|
547
|
+
});
|
|
548
|
+
return {
|
|
549
|
+
files: body.files ?? [],
|
|
550
|
+
total: body.total ?? 0,
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/** Delete files via POST /system/files/delete. */
|
|
555
|
+
async deleteFiles(ids: string[]): Promise<DeleteFilesResponse> {
|
|
556
|
+
if (!ids?.length) {
|
|
557
|
+
throw new ValidationError('ids are required');
|
|
558
|
+
}
|
|
559
|
+
const body = await this.executeREST<DeleteFilesResponse>('POST', '/files/delete', {
|
|
560
|
+
jsonBody: { ids },
|
|
561
|
+
allowFailure: true,
|
|
562
|
+
});
|
|
563
|
+
const result: DeleteFilesResponse = {
|
|
564
|
+
success: !!body.success,
|
|
565
|
+
deleted_ids: body.deleted_ids ?? [],
|
|
566
|
+
storage_failed: body.storage_failed,
|
|
567
|
+
message: body.message,
|
|
568
|
+
};
|
|
569
|
+
if (!result.success && result.message) {
|
|
570
|
+
throw new ValidationError(result.message);
|
|
571
|
+
}
|
|
572
|
+
return result;
|
|
573
|
+
}
|
|
574
|
+
|
|
414
575
|
/**
|
|
415
576
|
* Get a single resource by model and ID
|
|
416
577
|
*/
|
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
|
+
File,
|
|
32
|
+
UploadFileParams,
|
|
33
|
+
FilesListResponse,
|
|
34
|
+
DeleteFilesResponse,
|
|
30
35
|
} from './types';
|
|
31
36
|
|
|
32
37
|
// Default export for convenience
|
package/src/types.ts
CHANGED
|
@@ -86,63 +86,88 @@ 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 File {
|
|
142
|
+
id: string;
|
|
143
|
+
file_type: string;
|
|
144
|
+
file_name: string;
|
|
145
|
+
file_extension?: string;
|
|
146
|
+
content_type?: string;
|
|
147
|
+
size: number;
|
|
148
|
+
url: string;
|
|
149
|
+
created_by?: string;
|
|
150
|
+
created_at?: string;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface FilesListResponse {
|
|
154
|
+
files: File[];
|
|
155
|
+
total: number;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface UploadFileParams {
|
|
159
|
+
fileName: string;
|
|
160
|
+
content: Uint8Array | ArrayBuffer;
|
|
161
|
+
fileType?: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface DeleteFilesResponse {
|
|
165
|
+
success: boolean;
|
|
166
|
+
deleted_ids: string[];
|
|
167
|
+
storage_failed?: string[];
|
|
168
|
+
message?: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
146
171
|
/** One SaaS catalog tenant row from searchTenantsByDomain. */
|
|
147
172
|
export interface TenantCatalogSearchRow {
|
|
148
173
|
id: string;
|
|
@@ -158,6 +183,8 @@ export interface TenantByDomainResponse {
|
|
|
158
183
|
|
|
159
184
|
export interface ClientConfig {
|
|
160
185
|
baseURL: string;
|
|
186
|
+
/** REST base (e.g. http://host:5050/system); derived from baseURL when omitted */
|
|
187
|
+
restBaseURL?: string;
|
|
161
188
|
apiKey: string;
|
|
162
189
|
timeout?: number;
|
|
163
190
|
httpClient?: any;
|
|
@@ -192,13 +219,17 @@ export interface InjectedDBOperationInterface {
|
|
|
192
219
|
): Promise<GraphQLResponse>;
|
|
193
220
|
/** @param token Legacy; ignored. Auth uses client API key. */
|
|
194
221
|
generateTenantToken(tenantId: string, duration?: string, role?: string): Promise<string>;
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
222
|
+
loginUser(params: LoginUserParams): Promise<LoginUserResponse>;
|
|
223
|
+
googleOAuthState(projectId: string): Promise<GoogleOAuthStateResponse>;
|
|
224
|
+
searchUsers(projectId: string, limit?: number, offset?: number): Promise<UsersResponse>;
|
|
198
225
|
searchTenantsByDomain(projectId: string, domain: string): Promise<TenantByDomainResponse>;
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
226
|
+
createUser(projectId: string, params: CreateUserParams): Promise<User>;
|
|
227
|
+
updateUser(userId: string, params: UpdateUserParams): Promise<User>;
|
|
228
|
+
resetUserPassword(userId: string, password: string): Promise<boolean>;
|
|
229
|
+
deleteUser(userId: string): Promise<boolean>;
|
|
230
|
+
uploadFile(params: UploadFileParams): Promise<File>;
|
|
231
|
+
listFiles(fileType?: string, limit?: number, offset?: number): Promise<FilesListResponse>;
|
|
232
|
+
deleteFiles(ids: string[]): Promise<DeleteFilesResponse>;
|
|
202
233
|
getSingleResource(model: string, id: string, singlePageData?: boolean): Promise<DefaultDocumentStructure>;
|
|
203
234
|
searchResources(model: string, filter?: Record<string, any>, aggregate?: boolean): Promise<SearchResult>;
|
|
204
235
|
getRelationDocuments(id: string, connection: Record<string, any>): Promise<SearchResult>;
|