@qidcloud/sdk 1.2.3 → 1.2.4
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 +1561 -158
- package/dist/index.js +730 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +730 -13
- package/dist/index.mjs.map +1 -1
- package/dist/{index.d.ts → src/index.d.ts} +8 -0
- package/dist/src/modules/auth.d.ts +155 -0
- package/dist/src/modules/billing.d.ts +57 -0
- package/dist/src/modules/db.d.ts +49 -0
- package/dist/src/modules/project.d.ts +112 -0
- package/dist/src/modules/resource.d.ts +25 -0
- package/dist/src/modules/sdk.d.ts +29 -0
- package/dist/{modules → src/modules}/vault.d.ts +26 -14
- package/dist/src/types.d.ts +126 -0
- package/package.json +17 -3
- package/dist/modules/auth.d.ts +0 -27
- package/dist/modules/db.d.ts +0 -21
- package/dist/types.d.ts +0 -46
- package/rollup.config.mjs +0 -26
- package/src/components/QidCloudLogin.tsx +0 -64
- package/src/components/QidSignInButton.tsx +0 -260
- package/src/hooks/useQidAuth.ts +0 -179
- package/src/index.ts +0 -58
- package/src/modules/auth.ts +0 -106
- package/src/modules/db.ts +0 -40
- package/src/modules/edge.ts +0 -98
- package/src/modules/logs.ts +0 -30
- package/src/modules/vault.ts +0 -124
- package/src/types.ts +0 -52
- package/tsconfig.json +0 -31
- /package/dist/{components → src/components}/QidCloudLogin.d.ts +0 -0
- /package/dist/{components → src/components}/QidSignInButton.d.ts +0 -0
- /package/dist/{hooks → src/hooks}/useQidAuth.d.ts +0 -0
- /package/dist/{modules → src/modules}/edge.d.ts +0 -0
- /package/dist/{modules → src/modules}/logs.d.ts +0 -0
package/dist/index.mjs
CHANGED
|
@@ -95,6 +95,138 @@ class AuthModule {
|
|
|
95
95
|
role: data.role
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
|
+
// --- Conventional Auth Methods ---
|
|
99
|
+
/**
|
|
100
|
+
* Login using username and password
|
|
101
|
+
*/
|
|
102
|
+
async login(credentials) {
|
|
103
|
+
const resp = await this.sdk.api.post('/api/login/conventional', credentials);
|
|
104
|
+
return resp.data;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Register a new user
|
|
108
|
+
*/
|
|
109
|
+
async register(data) {
|
|
110
|
+
const resp = await this.sdk.api.post('/api/register/conventional', data);
|
|
111
|
+
return resp.data;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Initiate registration (OTP based if configured)
|
|
115
|
+
*/
|
|
116
|
+
async initiateRegistration(email) {
|
|
117
|
+
const resp = await this.sdk.api.post('/api/register/initiate', { email });
|
|
118
|
+
return resp.data;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Verify OTP or mobile verification
|
|
122
|
+
*/
|
|
123
|
+
async verify(data) {
|
|
124
|
+
const resp = await this.sdk.api.post('/api/verify', data);
|
|
125
|
+
return resp.data;
|
|
126
|
+
}
|
|
127
|
+
// --- Password & Account Management ---
|
|
128
|
+
/**
|
|
129
|
+
* Request a password reset link/OTP
|
|
130
|
+
*/
|
|
131
|
+
async requestPasswordReset(email) {
|
|
132
|
+
const resp = await this.sdk.api.post('/api/password/reset/request', { email });
|
|
133
|
+
return resp.data;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Confirm password reset with OTP and new password
|
|
137
|
+
*/
|
|
138
|
+
async confirmPasswordReset(data) {
|
|
139
|
+
const resp = await this.sdk.api.post('/api/password/reset/confirm', data);
|
|
140
|
+
return resp.data;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Change current password (requires current token)
|
|
144
|
+
*/
|
|
145
|
+
async changePassword(token, data) {
|
|
146
|
+
const resp = await this.sdk.api.post('/api/password/change', data, {
|
|
147
|
+
headers: { 'Authorization': `Bearer ${token}` }
|
|
148
|
+
});
|
|
149
|
+
return resp.data;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Delete user account
|
|
153
|
+
*/
|
|
154
|
+
async deleteAccount(token) {
|
|
155
|
+
const resp = await this.sdk.api.delete('/api/auth/account', {
|
|
156
|
+
headers: { 'Authorization': `Bearer ${token}` }
|
|
157
|
+
});
|
|
158
|
+
return resp.data;
|
|
159
|
+
}
|
|
160
|
+
// --- MFA Management ---
|
|
161
|
+
/**
|
|
162
|
+
* Toggle MFA status (Enable/Disable)
|
|
163
|
+
*/
|
|
164
|
+
async toggleMFA(token, data) {
|
|
165
|
+
const resp = await this.sdk.api.post('/api/auth/mfa/toggle', data, {
|
|
166
|
+
headers: { 'Authorization': `Bearer ${token}` }
|
|
167
|
+
});
|
|
168
|
+
return resp.data;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Verify MFA toggle request
|
|
172
|
+
*/
|
|
173
|
+
async verifyMFA(token, otp) {
|
|
174
|
+
const resp = await this.sdk.api.post('/api/auth/mfa/verify', { otp }, {
|
|
175
|
+
headers: { 'Authorization': `Bearer ${token}` }
|
|
176
|
+
});
|
|
177
|
+
return resp.data;
|
|
178
|
+
}
|
|
179
|
+
// --- Session & Recovery ---
|
|
180
|
+
/**
|
|
181
|
+
* Refresh the current active session
|
|
182
|
+
*/
|
|
183
|
+
async refreshSession(token) {
|
|
184
|
+
const resp = await this.sdk.api.post('/api/session/refresh', {}, {
|
|
185
|
+
headers: { 'Authorization': `Bearer ${token}` }
|
|
186
|
+
});
|
|
187
|
+
return resp.data;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* List all active sessions for the user
|
|
191
|
+
*/
|
|
192
|
+
async getSessions(token) {
|
|
193
|
+
const resp = await this.sdk.api.get('/api/sessions', {
|
|
194
|
+
headers: { 'Authorization': `Bearer ${token}` }
|
|
195
|
+
});
|
|
196
|
+
return resp.data;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Revoke a specific session
|
|
200
|
+
*/
|
|
201
|
+
async revokeSession(token, sessionToken) {
|
|
202
|
+
const resp = await this.sdk.api.delete(`/api/sessions/${sessionToken}`, {
|
|
203
|
+
headers: { 'Authorization': `Bearer ${token}` }
|
|
204
|
+
});
|
|
205
|
+
return resp.data;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Initiate account recovery (password reset)
|
|
209
|
+
*/
|
|
210
|
+
async initiateRecovery(email) {
|
|
211
|
+
const resp = await this.sdk.api.post('/api/recover', { email });
|
|
212
|
+
return resp.data;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Verify recovery OTP and set new password
|
|
216
|
+
*/
|
|
217
|
+
async verifyRecovery(data) {
|
|
218
|
+
const resp = await this.sdk.api.post('/api/recover/verify', data);
|
|
219
|
+
return resp.data;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Export all user data (GDPR requirement)
|
|
223
|
+
*/
|
|
224
|
+
async exportUserData(token) {
|
|
225
|
+
const resp = await this.sdk.api.get('/api/auth/account/export', {
|
|
226
|
+
headers: { 'Authorization': `Bearer ${token}` }
|
|
227
|
+
});
|
|
228
|
+
return resp.data;
|
|
229
|
+
}
|
|
98
230
|
}
|
|
99
231
|
|
|
100
232
|
class DbModule {
|
|
@@ -128,6 +260,98 @@ class DbModule {
|
|
|
128
260
|
const resp = await this.sdk.api.post('/api/db/setup', {}, { headers });
|
|
129
261
|
return resp.data;
|
|
130
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* Run versioned migrations (Django-inspired).
|
|
265
|
+
* Only applies migrations that haven't been applied yet.
|
|
266
|
+
* Tracks applied migrations in a `_qid_migrations` table.
|
|
267
|
+
*
|
|
268
|
+
* @example
|
|
269
|
+
* ```ts
|
|
270
|
+
* await qid.db.migrate([
|
|
271
|
+
* { version: '001', name: 'create_users', up: async () => { await qid.db.query('CREATE TABLE ...') } },
|
|
272
|
+
* { version: '002', name: 'add_likes', up: async () => { await qid.db.query('ALTER TABLE ...') } },
|
|
273
|
+
* ]);
|
|
274
|
+
* ```
|
|
275
|
+
*
|
|
276
|
+
* @param migrations Ordered array of migrations to apply
|
|
277
|
+
* @param userToken Optional session token
|
|
278
|
+
* @returns Result with applied/skipped counts
|
|
279
|
+
*/
|
|
280
|
+
async migrate(migrations, userToken) {
|
|
281
|
+
const result = {
|
|
282
|
+
success: true,
|
|
283
|
+
applied: [],
|
|
284
|
+
skipped: [],
|
|
285
|
+
total: migrations.length,
|
|
286
|
+
};
|
|
287
|
+
try {
|
|
288
|
+
// 1. Ensure the migrations tracking table exists
|
|
289
|
+
await this.query(`CREATE TABLE IF NOT EXISTS _qid_migrations (
|
|
290
|
+
version VARCHAR(50) PRIMARY KEY,
|
|
291
|
+
name VARCHAR(255) NOT NULL,
|
|
292
|
+
applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
293
|
+
)`, [], userToken);
|
|
294
|
+
// 2. Get already-applied migrations
|
|
295
|
+
const appliedRes = await this.query('SELECT version FROM _qid_migrations ORDER BY version', [], userToken);
|
|
296
|
+
const appliedVersions = new Set((appliedRes.data || []).map((r) => r.version));
|
|
297
|
+
// 3. Run only new migrations, in order
|
|
298
|
+
for (const migration of migrations) {
|
|
299
|
+
if (appliedVersions.has(migration.version)) {
|
|
300
|
+
result.skipped.push(migration.version);
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
try {
|
|
304
|
+
await migration.up();
|
|
305
|
+
// Record as applied
|
|
306
|
+
await this.query('INSERT INTO _qid_migrations (version, name) VALUES ($1, $2) ON CONFLICT (version) DO NOTHING', [migration.version, migration.name], userToken);
|
|
307
|
+
result.applied.push(migration.version);
|
|
308
|
+
}
|
|
309
|
+
catch (migrationError) {
|
|
310
|
+
result.success = false;
|
|
311
|
+
result.failed = {
|
|
312
|
+
version: migration.version,
|
|
313
|
+
error: migrationError.message || 'Unknown error',
|
|
314
|
+
};
|
|
315
|
+
// Stop on first failure (like Django)
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return result;
|
|
320
|
+
}
|
|
321
|
+
catch (err) {
|
|
322
|
+
return {
|
|
323
|
+
success: false,
|
|
324
|
+
applied: result.applied,
|
|
325
|
+
skipped: result.skipped,
|
|
326
|
+
failed: { version: 'init', error: err.message || 'Migration system init failed' },
|
|
327
|
+
total: migrations.length,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Get the current migration status
|
|
333
|
+
* @param migrations The full list of registered migrations
|
|
334
|
+
* @param userToken Optional session token
|
|
335
|
+
*/
|
|
336
|
+
async migrateStatus(migrations, userToken) {
|
|
337
|
+
try {
|
|
338
|
+
const appliedRes = await this.query('SELECT version FROM _qid_migrations ORDER BY version', [], userToken);
|
|
339
|
+
const appliedVersions = new Set((appliedRes.data || []).map((r) => r.version));
|
|
340
|
+
return {
|
|
341
|
+
applied: migrations.filter(m => appliedVersions.has(m.version)).map(m => m.version),
|
|
342
|
+
pending: migrations.filter(m => !appliedVersions.has(m.version)).map(m => m.version),
|
|
343
|
+
total: migrations.length,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
// Table doesn't exist yet = all pending
|
|
348
|
+
return {
|
|
349
|
+
applied: [],
|
|
350
|
+
pending: migrations.map(m => m.version),
|
|
351
|
+
total: migrations.length,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
}
|
|
131
355
|
}
|
|
132
356
|
|
|
133
357
|
class EdgeModule {
|
|
@@ -221,19 +445,38 @@ class EdgeModule {
|
|
|
221
445
|
}
|
|
222
446
|
}
|
|
223
447
|
|
|
448
|
+
const DEFAULT_CHUNK_SIZE = 5 * 1024 * 1024; // 5MB
|
|
449
|
+
const CHUNKED_THRESHOLD = 10 * 1024 * 1024; // 10MB — above this, use chunked upload
|
|
224
450
|
class VaultModule {
|
|
225
451
|
sdk;
|
|
226
452
|
constructor(sdk) {
|
|
227
453
|
this.sdk = sdk;
|
|
228
454
|
}
|
|
229
455
|
/**
|
|
230
|
-
* Upload a file to the project's secure enclave storage
|
|
231
|
-
*
|
|
456
|
+
* Upload a file to the project's secure enclave storage.
|
|
457
|
+
*
|
|
458
|
+
* Files < 10MB use single-shot upload.
|
|
459
|
+
* Files >= 10MB automatically use chunked upload with progress tracking.
|
|
460
|
+
*
|
|
461
|
+
* @param file The file data (Buffer, Blob, or File)
|
|
232
462
|
* @param fileName Name of the file
|
|
233
463
|
* @param metadata Optional E2EE metadata or custom tags
|
|
234
464
|
* @param userToken Session token for user-scoped storage
|
|
465
|
+
* @param options Upload options (onProgress callback, custom chunk size)
|
|
235
466
|
*/
|
|
236
|
-
async upload(file, fileName, metadata = {}, userToken) {
|
|
467
|
+
async upload(file, fileName, metadata = {}, userToken, options) {
|
|
468
|
+
const fileSize = file.size || file.length || file.byteLength || 0;
|
|
469
|
+
// Auto-detect: use chunked upload for large files
|
|
470
|
+
if (fileSize >= CHUNKED_THRESHOLD) {
|
|
471
|
+
return this._chunkedUpload(file, fileName, fileSize, metadata, userToken, options);
|
|
472
|
+
}
|
|
473
|
+
// Small file — single-shot upload (existing behavior)
|
|
474
|
+
return this._singleUpload(file, fileName, metadata, userToken);
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Single-shot upload for small files (< 10MB)
|
|
478
|
+
*/
|
|
479
|
+
async _singleUpload(file, fileName, metadata, userToken) {
|
|
237
480
|
const formData = new FormData();
|
|
238
481
|
formData.append('file', file, fileName);
|
|
239
482
|
formData.append('metadata', JSON.stringify(metadata));
|
|
@@ -246,9 +489,149 @@ class VaultModule {
|
|
|
246
489
|
const resp = await this.sdk.api.post('/api/storage/upload', formData, { headers });
|
|
247
490
|
return resp.data;
|
|
248
491
|
}
|
|
492
|
+
/**
|
|
493
|
+
* Chunked upload for large files (>= 10MB)
|
|
494
|
+
* Flow: init → upload chunks → complete
|
|
495
|
+
*/
|
|
496
|
+
async _chunkedUpload(file, fileName, fileSize, metadata, userToken, options) {
|
|
497
|
+
const chunkSize = options?.chunkSize || DEFAULT_CHUNK_SIZE;
|
|
498
|
+
const totalChunks = Math.ceil(fileSize / chunkSize);
|
|
499
|
+
const onProgress = options?.onProgress;
|
|
500
|
+
const headers = {};
|
|
501
|
+
if (userToken) {
|
|
502
|
+
headers['Authorization'] = `Bearer ${userToken}`;
|
|
503
|
+
}
|
|
504
|
+
// 1. Initialize upload session
|
|
505
|
+
const initResp = await this.sdk.api.post('/api/storage/upload/init', {
|
|
506
|
+
fileName,
|
|
507
|
+
fileSize,
|
|
508
|
+
mimeType: file.type || 'application/octet-stream',
|
|
509
|
+
totalChunks,
|
|
510
|
+
metadata
|
|
511
|
+
}, { headers });
|
|
512
|
+
const { uploadId } = initResp.data;
|
|
513
|
+
let uploadedChunks = 0;
|
|
514
|
+
// 2. Upload chunks sequentially
|
|
515
|
+
for (let i = 0; i < totalChunks; i++) {
|
|
516
|
+
const start = i * chunkSize;
|
|
517
|
+
const end = Math.min(start + chunkSize, fileSize);
|
|
518
|
+
// Slice the chunk from the file
|
|
519
|
+
let chunkData;
|
|
520
|
+
if (file.slice) {
|
|
521
|
+
// Blob or File object
|
|
522
|
+
chunkData = file.slice(start, end);
|
|
523
|
+
}
|
|
524
|
+
else if (file.subarray) {
|
|
525
|
+
// Buffer or Uint8Array
|
|
526
|
+
chunkData = file.subarray(start, end);
|
|
527
|
+
}
|
|
528
|
+
else if (typeof file === 'object' && file.data) {
|
|
529
|
+
// express-fileupload style
|
|
530
|
+
chunkData = file.data.subarray(start, end);
|
|
531
|
+
}
|
|
532
|
+
// Build FormData for this chunk
|
|
533
|
+
const chunkForm = new FormData();
|
|
534
|
+
const chunkBlob = chunkData instanceof Blob ? chunkData : new Blob([chunkData]);
|
|
535
|
+
chunkForm.append('chunk', chunkBlob, `chunk_${i}`);
|
|
536
|
+
await this.sdk.api.put(`/api/storage/upload/${uploadId}/chunk/${i}`, chunkForm, {
|
|
537
|
+
headers: {
|
|
538
|
+
...headers,
|
|
539
|
+
'Content-Type': 'multipart/form-data'
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
uploadedChunks++;
|
|
543
|
+
// Report progress
|
|
544
|
+
if (onProgress) {
|
|
545
|
+
onProgress({
|
|
546
|
+
percent: Math.round((uploadedChunks / totalChunks) * 100),
|
|
547
|
+
uploadedChunks,
|
|
548
|
+
totalChunks,
|
|
549
|
+
uploadedBytes: Math.min(uploadedChunks * chunkSize, fileSize),
|
|
550
|
+
totalBytes: fileSize
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
// 3. Complete upload — server reassembles, encrypts, stores
|
|
555
|
+
const completeResp = await this.sdk.api.post(`/api/storage/upload/${uploadId}/complete`, { metadata }, { headers });
|
|
556
|
+
// Final 100% progress
|
|
557
|
+
if (onProgress) {
|
|
558
|
+
onProgress({
|
|
559
|
+
percent: 100,
|
|
560
|
+
uploadedChunks: totalChunks,
|
|
561
|
+
totalChunks,
|
|
562
|
+
uploadedBytes: fileSize,
|
|
563
|
+
totalBytes: fileSize
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
return completeResp.data;
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Get the status of a chunked upload (for resume support)
|
|
570
|
+
*/
|
|
571
|
+
async getUploadStatus(uploadId, userToken) {
|
|
572
|
+
const headers = {};
|
|
573
|
+
if (userToken) {
|
|
574
|
+
headers['Authorization'] = `Bearer ${userToken}`;
|
|
575
|
+
}
|
|
576
|
+
const resp = await this.sdk.api.get(`/api/storage/upload/${uploadId}/status`, { headers });
|
|
577
|
+
return resp.data;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Resume a partially completed chunked upload
|
|
581
|
+
*/
|
|
582
|
+
async resumeUpload(uploadId, file, userToken, options) {
|
|
583
|
+
const headers = {};
|
|
584
|
+
if (userToken) {
|
|
585
|
+
headers['Authorization'] = `Bearer ${userToken}`;
|
|
586
|
+
}
|
|
587
|
+
// Get current status to find missing chunks
|
|
588
|
+
const status = await this.getUploadStatus(uploadId, userToken);
|
|
589
|
+
if (status.status !== 'uploading') {
|
|
590
|
+
throw new Error(`Upload is in "${status.status}" state and cannot be resumed`);
|
|
591
|
+
}
|
|
592
|
+
const chunkSize = options?.chunkSize || status.chunkSize || DEFAULT_CHUNK_SIZE;
|
|
593
|
+
const onProgress = options?.onProgress;
|
|
594
|
+
const missingChunks = status.missingChunks;
|
|
595
|
+
const totalChunks = status.totalChunks;
|
|
596
|
+
const fileSize = status.fileSize;
|
|
597
|
+
let uploadedChunks = status.receivedChunks.length;
|
|
598
|
+
// Upload only the missing chunks
|
|
599
|
+
for (const i of missingChunks) {
|
|
600
|
+
const start = i * chunkSize;
|
|
601
|
+
const end = Math.min(start + chunkSize, fileSize);
|
|
602
|
+
let chunkData;
|
|
603
|
+
if (file.slice) {
|
|
604
|
+
chunkData = file.slice(start, end);
|
|
605
|
+
}
|
|
606
|
+
else if (file.subarray) {
|
|
607
|
+
chunkData = file.subarray(start, end);
|
|
608
|
+
}
|
|
609
|
+
const chunkForm = new FormData();
|
|
610
|
+
const chunkBlob = chunkData instanceof Blob ? chunkData : new Blob([chunkData]);
|
|
611
|
+
chunkForm.append('chunk', chunkBlob, `chunk_${i}`);
|
|
612
|
+
await this.sdk.api.put(`/api/storage/upload/${uploadId}/chunk/${i}`, chunkForm, {
|
|
613
|
+
headers: {
|
|
614
|
+
...headers,
|
|
615
|
+
'Content-Type': 'multipart/form-data'
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
uploadedChunks++;
|
|
619
|
+
if (onProgress) {
|
|
620
|
+
onProgress({
|
|
621
|
+
percent: Math.round((uploadedChunks / totalChunks) * 100),
|
|
622
|
+
uploadedChunks,
|
|
623
|
+
totalChunks,
|
|
624
|
+
uploadedBytes: Math.min(uploadedChunks * chunkSize, fileSize),
|
|
625
|
+
totalBytes: fileSize
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
// Complete
|
|
630
|
+
const completeResp = await this.sdk.api.post(`/api/storage/upload/${uploadId}/complete`, {}, { headers });
|
|
631
|
+
return completeResp.data;
|
|
632
|
+
}
|
|
249
633
|
/**
|
|
250
634
|
* List files in the project enclave
|
|
251
|
-
* @param userToken Session token for user-scoped storage
|
|
252
635
|
*/
|
|
253
636
|
async list(userToken) {
|
|
254
637
|
const headers = {};
|
|
@@ -260,8 +643,6 @@ class VaultModule {
|
|
|
260
643
|
}
|
|
261
644
|
/**
|
|
262
645
|
* Download a file from storage
|
|
263
|
-
* @param fileId Unique ID of the file
|
|
264
|
-
* @param userToken Session token for user-scoped storage
|
|
265
646
|
*/
|
|
266
647
|
async download(fileId, userToken) {
|
|
267
648
|
const headers = {};
|
|
@@ -276,8 +657,6 @@ class VaultModule {
|
|
|
276
657
|
}
|
|
277
658
|
/**
|
|
278
659
|
* Delete a file from storage (Soft Delete)
|
|
279
|
-
* @param fileId Unique ID of the file
|
|
280
|
-
* @param userToken Session token for user-scoped storage
|
|
281
660
|
*/
|
|
282
661
|
async delete(fileId, userToken) {
|
|
283
662
|
const headers = {};
|
|
@@ -289,7 +668,6 @@ class VaultModule {
|
|
|
289
668
|
}
|
|
290
669
|
/**
|
|
291
670
|
* List deleted files in the project enclave (Recycle Bin)
|
|
292
|
-
* @param userToken Session token for user-scoped storage
|
|
293
671
|
*/
|
|
294
672
|
async listDeleted(userToken) {
|
|
295
673
|
const headers = {};
|
|
@@ -301,8 +679,6 @@ class VaultModule {
|
|
|
301
679
|
}
|
|
302
680
|
/**
|
|
303
681
|
* Restore a deleted file from the recycle bin
|
|
304
|
-
* @param fileId Unique ID of the file
|
|
305
|
-
* @param userToken Session token for user-scoped storage
|
|
306
682
|
*/
|
|
307
683
|
async restore(fileId, userToken) {
|
|
308
684
|
const headers = {};
|
|
@@ -314,8 +690,6 @@ class VaultModule {
|
|
|
314
690
|
}
|
|
315
691
|
/**
|
|
316
692
|
* Permanently purge a deleted file
|
|
317
|
-
* @param fileId Unique ID of the file
|
|
318
|
-
* @param userToken Session token for user-scoped storage
|
|
319
693
|
*/
|
|
320
694
|
async purge(fileId, userToken) {
|
|
321
695
|
const headers = {};
|
|
@@ -353,6 +727,341 @@ class LogsModule {
|
|
|
353
727
|
}
|
|
354
728
|
}
|
|
355
729
|
|
|
730
|
+
class ProjectModule {
|
|
731
|
+
sdk;
|
|
732
|
+
constructor(sdk) {
|
|
733
|
+
this.sdk = sdk;
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Get all projects owned by the currently authenticated user
|
|
737
|
+
*/
|
|
738
|
+
async getMyProjects(userToken) {
|
|
739
|
+
const resp = await this.sdk.api.get('/api/projects/my-projects', {
|
|
740
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
741
|
+
});
|
|
742
|
+
return resp.data;
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Create a new project
|
|
746
|
+
*/
|
|
747
|
+
async createProject(userToken, data) {
|
|
748
|
+
const resp = await this.sdk.api.post('/api/projects', data, {
|
|
749
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
750
|
+
});
|
|
751
|
+
return resp.data;
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Update project basic details (e.g. rename)
|
|
755
|
+
*/
|
|
756
|
+
async updateProject(userToken, tenantId, data) {
|
|
757
|
+
const resp = await this.sdk.api.patch(`/api/projects/${tenantId}`, data, {
|
|
758
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
759
|
+
});
|
|
760
|
+
return resp.data;
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Rotate the API key for a project
|
|
764
|
+
*/
|
|
765
|
+
async rotateApiKey(userToken, tenantId) {
|
|
766
|
+
const resp = await this.sdk.api.post(`/api/projects/${tenantId}/rotate-key`, {}, {
|
|
767
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
768
|
+
});
|
|
769
|
+
return resp.data;
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Get project specific settings
|
|
773
|
+
*/
|
|
774
|
+
async getSettings(userToken, tenantId) {
|
|
775
|
+
const resp = await this.sdk.api.get(`/api/projects/${tenantId}/settings`, {
|
|
776
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
777
|
+
});
|
|
778
|
+
return resp.data;
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Update project settings
|
|
782
|
+
*/
|
|
783
|
+
async updateSettings(userToken, tenantId, settings) {
|
|
784
|
+
const resp = await this.sdk.api.patch(`/api/projects/${tenantId}/settings`, settings, {
|
|
785
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
786
|
+
});
|
|
787
|
+
return resp.data;
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Invite a new member to the project
|
|
791
|
+
*/
|
|
792
|
+
async inviteMember(userToken, tenantId, data) {
|
|
793
|
+
const resp = await this.sdk.api.post(`/api/projects/${tenantId}/invite`, data, {
|
|
794
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
795
|
+
});
|
|
796
|
+
return resp.data;
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Remove a member from the project
|
|
800
|
+
*/
|
|
801
|
+
async removeMember(userToken, tenantId, userId) {
|
|
802
|
+
const resp = await this.sdk.api.delete(`/api/projects/${tenantId}/members/${userId}`, {
|
|
803
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
804
|
+
});
|
|
805
|
+
return resp.data;
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Get all users/members associated with a project
|
|
809
|
+
*/
|
|
810
|
+
async getProjectUsers(userToken, tenantId) {
|
|
811
|
+
const resp = await this.sdk.api.get(`/api/projects/${tenantId}/users`, {
|
|
812
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
813
|
+
});
|
|
814
|
+
return resp.data;
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Get all invitations for the currently authenticated user
|
|
818
|
+
*/
|
|
819
|
+
async getMyInvitations(userToken) {
|
|
820
|
+
const resp = await this.sdk.api.get('/api/projects/invitations', {
|
|
821
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
822
|
+
});
|
|
823
|
+
return resp.data;
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* Accept or reject a project invitation
|
|
827
|
+
*/
|
|
828
|
+
async handleInvitationAction(userToken, tenantId, action) {
|
|
829
|
+
const resp = await this.sdk.api.post(`/api/projects/${tenantId}/invitations/${action}`, {}, {
|
|
830
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
831
|
+
});
|
|
832
|
+
return resp.data;
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* Update a member's permissions within a project
|
|
836
|
+
*/
|
|
837
|
+
async updateMemberPermissions(userToken, tenantId, userId, permissions) {
|
|
838
|
+
const resp = await this.sdk.api.patch(`/api/projects/${tenantId}/members/${userId}/permissions`, { permissions }, {
|
|
839
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
840
|
+
});
|
|
841
|
+
return resp.data;
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Update a project user's status (active/suspended)
|
|
845
|
+
*/
|
|
846
|
+
async updateProjectUserStatus(userToken, tenantId, regUserId, status) {
|
|
847
|
+
const resp = await this.sdk.api.patch(`/api/projects/${tenantId}/users/${regUserId}/status`, { status }, {
|
|
848
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
849
|
+
});
|
|
850
|
+
return resp.data;
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
853
|
+
* Unlink/Remove a user from the project identity enclave
|
|
854
|
+
*/
|
|
855
|
+
async unlinkProjectUser(userToken, tenantId, regUserId) {
|
|
856
|
+
const resp = await this.sdk.api.delete(`/api/projects/${tenantId}/users/${regUserId}`, {
|
|
857
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
858
|
+
});
|
|
859
|
+
return resp.data;
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Generate a new service key for the project
|
|
863
|
+
*/
|
|
864
|
+
async generateServiceKey(userToken, tenantId, data) {
|
|
865
|
+
const resp = await this.sdk.api.post(`/api/projects/${tenantId}/service-keys`, data, {
|
|
866
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
867
|
+
});
|
|
868
|
+
return resp.data;
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Revoke an existing service key
|
|
872
|
+
*/
|
|
873
|
+
async revokeServiceKey(userToken, tenantId, keyId) {
|
|
874
|
+
const resp = await this.sdk.api.delete(`/api/projects/${tenantId}/service-keys/${keyId}`, {
|
|
875
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
876
|
+
});
|
|
877
|
+
return resp.data;
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* Permanently wipe the project's identity enclave data
|
|
881
|
+
*/
|
|
882
|
+
async wipeProjectEnclave(userToken, tenantId) {
|
|
883
|
+
const resp = await this.sdk.api.post(`/api/projects/${tenantId}/wipe`, {}, {
|
|
884
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
885
|
+
});
|
|
886
|
+
return resp.data;
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* Retrieve security and audit logs for the project
|
|
890
|
+
*/
|
|
891
|
+
async getProjectSecurityLogs(userToken, tenantId) {
|
|
892
|
+
const resp = await this.sdk.api.get(`/api/projects/${tenantId}/logs`, {
|
|
893
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
894
|
+
});
|
|
895
|
+
return resp.data;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
class ResourceModule {
|
|
900
|
+
sdk;
|
|
901
|
+
constructor(sdk) {
|
|
902
|
+
this.sdk = sdk;
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Get the status of all resources for a specific tenant
|
|
906
|
+
*/
|
|
907
|
+
async getStatus(userToken, tenantId) {
|
|
908
|
+
const resp = await this.sdk.api.get(`/api/resources/${tenantId}`, {
|
|
909
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
910
|
+
});
|
|
911
|
+
return resp.data;
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Provision a new resource type for a project
|
|
915
|
+
* @param resourceType 'database' | 'storage' | 'edge'
|
|
916
|
+
*/
|
|
917
|
+
async provision(userToken, tenantId, resourceType) {
|
|
918
|
+
const resp = await this.sdk.api.post(`/api/resources/${tenantId}/provision/${resourceType}`, {}, {
|
|
919
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
920
|
+
});
|
|
921
|
+
return resp.data;
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* Dissolve (delete) an existing resource from a project
|
|
925
|
+
*/
|
|
926
|
+
async dissolve(userToken, tenantId, resourceType) {
|
|
927
|
+
const resp = await this.sdk.api.delete(`/api/resources/${tenantId}/provision/${resourceType}`, {
|
|
928
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
929
|
+
});
|
|
930
|
+
return resp.data;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
class SdkModule {
|
|
935
|
+
sdk;
|
|
936
|
+
constructor(sdk) {
|
|
937
|
+
this.sdk = sdk;
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* Get allowed origins for a project
|
|
941
|
+
*/
|
|
942
|
+
async getOrigins(userToken, tenantId) {
|
|
943
|
+
const resp = await this.sdk.api.get(`/api/sdk/${tenantId}/origins`, {
|
|
944
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
945
|
+
});
|
|
946
|
+
return resp.data;
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* Add an allowed origin for SDK requests
|
|
950
|
+
*/
|
|
951
|
+
async addOrigin(userToken, tenantId, domain) {
|
|
952
|
+
const resp = await this.sdk.api.post(`/api/sdk/${tenantId}/origins`, { domain }, {
|
|
953
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
954
|
+
});
|
|
955
|
+
return resp.data;
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* Remove an allowed origin
|
|
959
|
+
*/
|
|
960
|
+
async removeOrigin(userToken, tenantId, domain) {
|
|
961
|
+
const resp = await this.sdk.api.delete(`/api/sdk/${tenantId}/origins/${domain}`, {
|
|
962
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
963
|
+
});
|
|
964
|
+
return resp.data;
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Test if an origin is allowed
|
|
968
|
+
*/
|
|
969
|
+
async testOrigin(userToken, tenantId, domain) {
|
|
970
|
+
const resp = await this.sdk.api.post(`/api/sdk/${tenantId}/origins/test`, { domain }, {
|
|
971
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
972
|
+
});
|
|
973
|
+
return resp.data;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
class BillingModule {
|
|
978
|
+
sdk;
|
|
979
|
+
constructor(sdk) {
|
|
980
|
+
this.sdk = sdk;
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
983
|
+
* Retrieve all available plans and their pricing
|
|
984
|
+
*/
|
|
985
|
+
async getPlans() {
|
|
986
|
+
const resp = await this.sdk.api.get('/api/billing/plans');
|
|
987
|
+
return resp.data;
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* Get detailed billing and usage info for a project
|
|
991
|
+
*/
|
|
992
|
+
async getProjectBillingInfo(userToken, tenantId) {
|
|
993
|
+
const resp = await this.sdk.api.get(`/api/billing/${tenantId}`, {
|
|
994
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
995
|
+
});
|
|
996
|
+
return resp.data;
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* Sync payment status after a successful Razorpay transaction
|
|
1000
|
+
* @param paymentId The Razorpay payment ID
|
|
1001
|
+
*/
|
|
1002
|
+
async syncPaymentStatus(userToken, paymentId) {
|
|
1003
|
+
const resp = await this.sdk.api.post('/api/billing/sync', { paymentId }, {
|
|
1004
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
1005
|
+
});
|
|
1006
|
+
return resp.data;
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Get transaction history for the current user
|
|
1010
|
+
*/
|
|
1011
|
+
async getTransactions(userToken) {
|
|
1012
|
+
const resp = await this.sdk.api.get('/api/billing/history/transactions', {
|
|
1013
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
1014
|
+
});
|
|
1015
|
+
return resp.data;
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Get usage analytics and history
|
|
1019
|
+
*/
|
|
1020
|
+
async getUsageAnalytics(userToken) {
|
|
1021
|
+
const resp = await this.sdk.api.get('/api/billing/history/analytics', {
|
|
1022
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
1023
|
+
});
|
|
1024
|
+
return resp.data;
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Get billing alerts for a specific project
|
|
1028
|
+
*/
|
|
1029
|
+
async getBillingAlerts(userToken, tenantId) {
|
|
1030
|
+
const resp = await this.sdk.api.get(`/api/billing/${tenantId}/alerts`, {
|
|
1031
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
1032
|
+
});
|
|
1033
|
+
return resp.data;
|
|
1034
|
+
}
|
|
1035
|
+
/**
|
|
1036
|
+
* Create or update a billing alert
|
|
1037
|
+
*/
|
|
1038
|
+
async createBillingAlert(userToken, tenantId, data) {
|
|
1039
|
+
const resp = await this.sdk.api.post(`/api/billing/${tenantId}/alerts`, data, {
|
|
1040
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
1041
|
+
});
|
|
1042
|
+
return resp.data;
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Delete a billing alert
|
|
1046
|
+
*/
|
|
1047
|
+
async deleteBillingAlert(userToken, alertId) {
|
|
1048
|
+
const resp = await this.sdk.api.delete(`/api/billing/alerts/${alertId}`, {
|
|
1049
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
1050
|
+
});
|
|
1051
|
+
return resp.data;
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* Generate a consolidated financial report
|
|
1055
|
+
*/
|
|
1056
|
+
async getConsolidatedReport(userToken, query) {
|
|
1057
|
+
const resp = await this.sdk.api.get('/api/billing/report/consolidated', {
|
|
1058
|
+
params: query,
|
|
1059
|
+
headers: { 'Authorization': `Bearer ${userToken}` }
|
|
1060
|
+
});
|
|
1061
|
+
return resp.data;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
|
|
356
1065
|
/**
|
|
357
1066
|
* A React hook for managing QidCloud authentication lifecycle.
|
|
358
1067
|
* Handles handshake initialization, WebSocket listeners, and profile fetching.
|
|
@@ -701,6 +1410,10 @@ class QidCloud {
|
|
|
701
1410
|
edge;
|
|
702
1411
|
vault;
|
|
703
1412
|
logs;
|
|
1413
|
+
projects;
|
|
1414
|
+
resources;
|
|
1415
|
+
sdk;
|
|
1416
|
+
billing;
|
|
704
1417
|
constructor(config) {
|
|
705
1418
|
this.config = {
|
|
706
1419
|
baseUrl: 'https://api.qidcloud.com',
|
|
@@ -718,6 +1431,10 @@ class QidCloud {
|
|
|
718
1431
|
this.edge = new EdgeModule(this);
|
|
719
1432
|
this.vault = new VaultModule(this);
|
|
720
1433
|
this.logs = new LogsModule(this);
|
|
1434
|
+
this.projects = new ProjectModule(this);
|
|
1435
|
+
this.resources = new ResourceModule(this);
|
|
1436
|
+
this.sdk = new SdkModule(this);
|
|
1437
|
+
this.billing = new BillingModule(this);
|
|
721
1438
|
}
|
|
722
1439
|
/**
|
|
723
1440
|
* Get the current project configuration
|