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