@apito-io/js-admin-sdk 2.5.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 +40 -12
- package/dist/index.d.mts +126 -22
- package/dist/index.d.ts +126 -22
- package/dist/index.js +78 -37
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +78 -37
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/client.test.ts +17 -11
- package/src/client.ts +370 -64
- package/src/index.ts +8 -0
- package/src/types.ts +103 -18
- package/src/version.ts +1 -1
package/src/client.ts
CHANGED
|
@@ -11,24 +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
|
+
LoginUserParams,
|
|
20
|
+
CreateUserParams,
|
|
21
|
+
UpdateUserParams,
|
|
22
|
+
GoogleOAuthStateResponse,
|
|
23
|
+
ProjectStorageSettings,
|
|
24
|
+
UpdateProjectStorageInput,
|
|
25
|
+
SystemFile,
|
|
26
|
+
SystemFilesListResponse,
|
|
27
|
+
SystemFileUploadParams,
|
|
28
|
+
DeleteSystemFilesResponse,
|
|
19
29
|
} from './types';
|
|
20
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
|
+
|
|
21
39
|
/**
|
|
22
40
|
* Apito SDK Client - JavaScript implementation matching the Go SDK
|
|
23
41
|
*/
|
|
24
42
|
export class ApitoClient implements InjectedDBOperationInterface {
|
|
25
43
|
private httpClient: AxiosInstance;
|
|
26
44
|
private baseURL: string;
|
|
45
|
+
private restBaseURL: string;
|
|
27
46
|
private apiKey: string;
|
|
28
47
|
private tenantId?: string;
|
|
29
48
|
|
|
30
49
|
constructor(config: ClientConfig) {
|
|
31
50
|
this.baseURL = config.baseURL;
|
|
51
|
+
this.restBaseURL = (config.restBaseURL ?? '').trim() || deriveRestBaseURL(config.baseURL);
|
|
32
52
|
this.apiKey = config.apiKey;
|
|
33
53
|
this.tenantId = config.tenantId;
|
|
34
54
|
|
|
@@ -144,22 +164,110 @@ export class ApitoClient implements InjectedDBOperationInterface {
|
|
|
144
164
|
return data.token;
|
|
145
165
|
}
|
|
146
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
|
+
|
|
147
230
|
/**
|
|
148
|
-
*
|
|
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`.
|
|
149
232
|
*/
|
|
150
|
-
async
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
233
|
+
async loginUser(params: LoginUserParams): Promise<LoginUserResponse> {
|
|
234
|
+
const authMethod = (params.authMethod ?? 'general').trim().toLowerCase() || 'general';
|
|
235
|
+
const variables: Record<string, any> = {
|
|
236
|
+
project_id: params.projectId,
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
if (authMethod === 'google') {
|
|
240
|
+
variables.auth_method = 'google';
|
|
241
|
+
const code = (params.code ?? '').trim();
|
|
242
|
+
const state = (params.state ?? '').trim();
|
|
243
|
+
if (!code || !state) {
|
|
244
|
+
throw new ValidationError('code and state are required for Google login');
|
|
245
|
+
}
|
|
246
|
+
variables.code = code;
|
|
247
|
+
variables.state = state;
|
|
248
|
+
} else {
|
|
249
|
+
const password = (params.password ?? '').trim();
|
|
250
|
+
if (!password) {
|
|
251
|
+
throw new ValidationError('password is required');
|
|
252
|
+
}
|
|
253
|
+
variables.password = password;
|
|
254
|
+
const email = (params.email ?? '').trim();
|
|
255
|
+
const phone = (params.phone ?? '').trim();
|
|
256
|
+
if (!email && !phone) {
|
|
257
|
+
throw new ValidationError('email or phone is required');
|
|
258
|
+
}
|
|
259
|
+
if (email) variables.email = email;
|
|
260
|
+
if (phone) variables.phone = phone;
|
|
261
|
+
}
|
|
262
|
+
|
|
155
263
|
const query = `
|
|
156
|
-
query
|
|
157
|
-
|
|
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) {
|
|
158
266
|
token
|
|
159
267
|
user {
|
|
160
268
|
id
|
|
161
|
-
username
|
|
162
269
|
email
|
|
270
|
+
phone
|
|
163
271
|
role
|
|
164
272
|
provider
|
|
165
273
|
tenant_id
|
|
@@ -170,68 +278,54 @@ export class ApitoClient implements InjectedDBOperationInterface {
|
|
|
170
278
|
}
|
|
171
279
|
}
|
|
172
280
|
`;
|
|
173
|
-
const variables: Record<string, any> = { project_id: projectId, username, password };
|
|
174
281
|
const response = await this.executeGraphQL(query, variables);
|
|
175
|
-
const raw = response.data?.
|
|
282
|
+
const raw = response.data?.loginUser;
|
|
176
283
|
if (!raw?.token) {
|
|
177
|
-
throw new ValidationError('Invalid response format for
|
|
284
|
+
throw new ValidationError('Invalid response format for loginUser');
|
|
178
285
|
}
|
|
179
286
|
return {
|
|
180
287
|
token: raw.token as string,
|
|
181
|
-
user: raw.user as
|
|
288
|
+
user: raw.user as User | undefined,
|
|
182
289
|
};
|
|
183
290
|
}
|
|
184
291
|
|
|
185
292
|
/**
|
|
186
|
-
*
|
|
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.
|
|
187
294
|
*/
|
|
188
|
-
async
|
|
295
|
+
async googleOAuthState(projectId: string): Promise<GoogleOAuthStateResponse> {
|
|
189
296
|
const query = `
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
user {
|
|
194
|
-
id
|
|
195
|
-
username
|
|
196
|
-
email
|
|
197
|
-
role
|
|
198
|
-
provider
|
|
199
|
-
tenant_id
|
|
200
|
-
status
|
|
201
|
-
created_at
|
|
202
|
-
updated_at
|
|
203
|
-
}
|
|
297
|
+
query GoogleOAuthState($project_id: String!) {
|
|
298
|
+
googleOAuthState(project_id: $project_id) {
|
|
299
|
+
state
|
|
204
300
|
}
|
|
205
301
|
}
|
|
206
302
|
`;
|
|
207
|
-
const variables = { project_id: projectId
|
|
303
|
+
const variables = { project_id: projectId };
|
|
208
304
|
const response = await this.executeGraphQL(query, variables);
|
|
209
|
-
const raw = response.data?.
|
|
210
|
-
|
|
211
|
-
|
|
305
|
+
const raw = response.data?.googleOAuthState;
|
|
306
|
+
const state = typeof raw?.state === 'string' ? raw.state.trim() : '';
|
|
307
|
+
if (!state) {
|
|
308
|
+
throw new ValidationError('Invalid response format for googleOAuthState');
|
|
212
309
|
}
|
|
213
|
-
return {
|
|
214
|
-
token: raw.token as string,
|
|
215
|
-
user: raw.user as TenantUser | undefined,
|
|
216
|
-
};
|
|
310
|
+
return { state };
|
|
217
311
|
}
|
|
218
312
|
|
|
219
313
|
/**
|
|
220
|
-
* Search
|
|
314
|
+
* Search project end-users.
|
|
221
315
|
*/
|
|
222
|
-
async
|
|
316
|
+
async searchUsers(
|
|
223
317
|
projectId: string,
|
|
224
318
|
limit?: number,
|
|
225
319
|
offset?: number
|
|
226
|
-
): Promise<
|
|
320
|
+
): Promise<UsersResponse> {
|
|
227
321
|
const query = `
|
|
228
|
-
query
|
|
229
|
-
|
|
322
|
+
query SearchUsers($project_id: String!, $limit: Int, $offset: Int) {
|
|
323
|
+
searchUsers(project_id: $project_id, limit: $limit, offset: $offset) {
|
|
230
324
|
count
|
|
231
325
|
users {
|
|
232
326
|
id
|
|
233
|
-
username
|
|
234
327
|
email
|
|
328
|
+
phone
|
|
235
329
|
role
|
|
236
330
|
provider
|
|
237
331
|
tenant_id
|
|
@@ -246,15 +340,15 @@ export class ApitoClient implements InjectedDBOperationInterface {
|
|
|
246
340
|
if (limit !== undefined) variables.limit = limit;
|
|
247
341
|
if (offset !== undefined) variables.offset = offset;
|
|
248
342
|
const response = await this.executeGraphQL(query, variables);
|
|
249
|
-
const raw = response.data?.
|
|
343
|
+
const raw = response.data?.searchUsers;
|
|
250
344
|
if (!raw) {
|
|
251
|
-
throw new ValidationError('Invalid response format for
|
|
345
|
+
throw new ValidationError('Invalid response format for searchUsers');
|
|
252
346
|
}
|
|
253
347
|
let count = 0;
|
|
254
348
|
if (typeof raw.count === 'number') {
|
|
255
349
|
count = raw.count;
|
|
256
350
|
}
|
|
257
|
-
const users = (raw.users ?? []) as
|
|
351
|
+
const users = (raw.users ?? []) as User[];
|
|
258
352
|
return { users, count };
|
|
259
353
|
}
|
|
260
354
|
|
|
@@ -289,21 +383,63 @@ export class ApitoClient implements InjectedDBOperationInterface {
|
|
|
289
383
|
}
|
|
290
384
|
|
|
291
385
|
/**
|
|
292
|
-
* Create a
|
|
386
|
+
* Create a project user (local password). Use `email` and/or `phone` per engine validation for the project identifier mode.
|
|
293
387
|
*/
|
|
294
|
-
async
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
388
|
+
async createUser(projectId: string, params: CreateUserParams): Promise<User> {
|
|
389
|
+
const password = (params.password ?? '').trim();
|
|
390
|
+
if (!password) {
|
|
391
|
+
throw new ValidationError('password is required');
|
|
392
|
+
}
|
|
393
|
+
const query = `
|
|
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) {
|
|
396
|
+
id
|
|
397
|
+
email
|
|
398
|
+
phone
|
|
399
|
+
role
|
|
400
|
+
provider
|
|
401
|
+
tenant_id
|
|
402
|
+
status
|
|
403
|
+
created_at
|
|
404
|
+
updated_at
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
`;
|
|
408
|
+
const variables: Record<string, any> = {
|
|
409
|
+
project_id: projectId,
|
|
410
|
+
password,
|
|
411
|
+
};
|
|
412
|
+
const role = (params.role ?? '').trim();
|
|
413
|
+
if (role) variables.role = role;
|
|
414
|
+
const email = (params.email ?? '').trim();
|
|
415
|
+
if (email) variables.email = email;
|
|
416
|
+
const phone = (params.phone ?? '').trim();
|
|
417
|
+
if (phone) variables.phone = phone;
|
|
418
|
+
const response = await this.executeGraphQL(query, variables);
|
|
419
|
+
const u = response.data?.createUser;
|
|
420
|
+
if (!u?.id) {
|
|
421
|
+
throw new ValidationError('Invalid response format for createUser');
|
|
422
|
+
}
|
|
423
|
+
return u as User;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Update a project user. Project scope is implied by the API key. Only include fields to change.
|
|
428
|
+
*/
|
|
429
|
+
async updateUser(userId: string, params: UpdateUserParams): Promise<User> {
|
|
430
|
+
const uid = (userId ?? '').trim();
|
|
431
|
+
if (!uid) {
|
|
432
|
+
throw new ValidationError('userId is required');
|
|
433
|
+
}
|
|
434
|
+
if (params.email === undefined && params.phone === undefined && params.role === undefined) {
|
|
435
|
+
throw new ValidationError('at least one field must be provided');
|
|
436
|
+
}
|
|
301
437
|
const query = `
|
|
302
|
-
mutation
|
|
303
|
-
|
|
438
|
+
mutation UpdateUser($user_id: String!, $email: String, $phone: String, $role: String) {
|
|
439
|
+
updateUser(user_id: $user_id, email: $email, phone: $phone, role: $role) {
|
|
304
440
|
id
|
|
305
|
-
username
|
|
306
441
|
email
|
|
442
|
+
phone
|
|
307
443
|
role
|
|
308
444
|
provider
|
|
309
445
|
tenant_id
|
|
@@ -313,13 +449,183 @@ export class ApitoClient implements InjectedDBOperationInterface {
|
|
|
313
449
|
}
|
|
314
450
|
}
|
|
315
451
|
`;
|
|
316
|
-
const variables = {
|
|
452
|
+
const variables: Record<string, any> = { user_id: uid };
|
|
453
|
+
if (params.email !== undefined) variables.email = params.email;
|
|
454
|
+
if (params.phone !== undefined) variables.phone = params.phone;
|
|
455
|
+
if (params.role !== undefined) variables.role = params.role;
|
|
317
456
|
const response = await this.executeGraphQL(query, variables);
|
|
318
|
-
const u = response.data?.
|
|
457
|
+
const u = response.data?.updateUser;
|
|
319
458
|
if (!u?.id) {
|
|
320
|
-
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');
|
|
469
|
+
}
|
|
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;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Delete a project user by id. Project scope is implied by the API key.
|
|
488
|
+
*/
|
|
489
|
+
async deleteUser(userId: string): Promise<boolean> {
|
|
490
|
+
const uid = (userId ?? '').trim();
|
|
491
|
+
if (!uid) {
|
|
492
|
+
throw new ValidationError('userId is required');
|
|
493
|
+
}
|
|
494
|
+
const query = `
|
|
495
|
+
mutation DeleteUser($user_id: String!) {
|
|
496
|
+
deleteUser(user_id: $user_id)
|
|
497
|
+
}
|
|
498
|
+
`;
|
|
499
|
+
const response = await this.executeGraphQL(query, { user_id: uid });
|
|
500
|
+
const ok = response.data?.deleteUser;
|
|
501
|
+
if (typeof ok !== 'boolean') {
|
|
502
|
+
throw new ValidationError('Invalid response format for deleteUser');
|
|
503
|
+
}
|
|
504
|
+
return ok;
|
|
505
|
+
}
|
|
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);
|
|
321
627
|
}
|
|
322
|
-
return
|
|
628
|
+
return result;
|
|
323
629
|
}
|
|
324
630
|
|
|
325
631
|
/**
|
package/src/index.ts
CHANGED
|
@@ -24,6 +24,14 @@ export type {
|
|
|
24
24
|
ApitoError,
|
|
25
25
|
ValidationError,
|
|
26
26
|
InjectedDBOperationInterface,
|
|
27
|
+
LoginUserParams,
|
|
28
|
+
CreateUserParams,
|
|
29
|
+
UpdateUserParams,
|
|
30
|
+
User,
|
|
31
|
+
ProjectStorageSettings,
|
|
32
|
+
UpdateProjectStorageInput,
|
|
33
|
+
SystemFile,
|
|
34
|
+
SystemFileUploadParams,
|
|
27
35
|
} from './types';
|
|
28
36
|
|
|
29
37
|
// Default export for convenience
|