skapi-js 1.0.0-alpha.9 → 1.0.1
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 +52 -55
- package/dist/skapi.js +1 -1
- package/dist/skapi.js.map +1 -1
- package/dist/skapi.module.js +1 -1
- package/dist/skapi.module.js.map +1 -1
- package/js/Types.d.ts +53 -53
- package/js/main/error.d.ts +1 -0
- package/js/main/error.js +1 -1
- package/js/main/skapi.d.ts +43 -29
- package/js/main/skapi.js +117 -37
- package/js/methods/database.d.ts +13 -9
- package/js/methods/database.js +309 -137
- package/js/methods/request.d.ts +1 -0
- package/js/methods/request.js +47 -24
- package/js/methods/subscription.d.ts +1 -2
- package/js/methods/subscription.js +24 -30
- package/js/methods/user.d.ts +13 -8
- package/js/methods/user.js +36 -18
- package/js/utils/utils.d.ts +1 -0
- package/js/utils/utils.js +17 -17
- package/js/utils/validator.js +26 -20
- package/package.json +1 -1
package/js/methods/database.js
CHANGED
|
@@ -3,7 +3,15 @@ import { extractFormMeta, generateRandom } from '../utils/utils';
|
|
|
3
3
|
import validator from '../utils/validator';
|
|
4
4
|
import { request } from './request';
|
|
5
5
|
const __index_number_range = 4503599627370496;
|
|
6
|
-
function
|
|
6
|
+
function fromBase62(str) {
|
|
7
|
+
const base62Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
8
|
+
let result = 0;
|
|
9
|
+
for (let i = 0; i < str.length; i++) {
|
|
10
|
+
result = result * 62 + base62Chars.indexOf(str[i]);
|
|
11
|
+
}
|
|
12
|
+
return result;
|
|
13
|
+
}
|
|
14
|
+
export function normalizeRecord(record) {
|
|
7
15
|
function base_decode(chars) {
|
|
8
16
|
let charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
|
9
17
|
return chars.split('').reverse().reduce((prev, curr, i) => prev + (charset.indexOf(curr) * (62 ** i)), 0);
|
|
@@ -23,10 +31,17 @@ function normalizeRecord(record) {
|
|
|
23
31
|
referenced_count: 0
|
|
24
32
|
},
|
|
25
33
|
ip: '',
|
|
26
|
-
bin:
|
|
34
|
+
bin: {}
|
|
27
35
|
};
|
|
28
36
|
const keys = {
|
|
29
37
|
'ip': (r) => {
|
|
38
|
+
if (r.slice(-1) === 'R') {
|
|
39
|
+
output.readonly = true;
|
|
40
|
+
r = r.slice(0, -1);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
output.readonly = false;
|
|
44
|
+
}
|
|
30
45
|
output.ip = r;
|
|
31
46
|
},
|
|
32
47
|
'rec': (r) => {
|
|
@@ -47,10 +62,10 @@ function normalizeRecord(record) {
|
|
|
47
62
|
output.table.name = rSplit[0];
|
|
48
63
|
output.table.access_group = rSplit[2] == '**' ? 'private' : parseInt(rSplit[2]);
|
|
49
64
|
if (rSplit?.[3]) {
|
|
50
|
-
output.table.subscription =
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
65
|
+
output.table.subscription = true;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
output.table.subscription = false;
|
|
54
69
|
}
|
|
55
70
|
},
|
|
56
71
|
'usr_tbl': (r) => {
|
|
@@ -59,10 +74,10 @@ function normalizeRecord(record) {
|
|
|
59
74
|
output.table.name = rSplit[1];
|
|
60
75
|
output.table.access_group = rSplit[3] == '**' ? 'private' : parseInt(rSplit[3]);
|
|
61
76
|
if (rSplit?.[4]) {
|
|
62
|
-
output.table.subscription =
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
77
|
+
output.table.subscription = true;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
output.table.subscription = false;
|
|
66
81
|
}
|
|
67
82
|
},
|
|
68
83
|
'idx': (r) => {
|
|
@@ -97,7 +112,40 @@ function normalizeRecord(record) {
|
|
|
97
112
|
output.reference.referenced_count = r;
|
|
98
113
|
},
|
|
99
114
|
'bin': (r) => {
|
|
100
|
-
|
|
115
|
+
let binObj = {};
|
|
116
|
+
if (Array.isArray(r)) {
|
|
117
|
+
for (let url of r) {
|
|
118
|
+
let path = url.split('/').slice(3).join('/');
|
|
119
|
+
let splitPath = path.split('/');
|
|
120
|
+
let filename = decodeURIComponent(splitPath.slice(-1)[0]);
|
|
121
|
+
let pathKey = decodeURIComponent(splitPath[10]);
|
|
122
|
+
let size = splitPath[9];
|
|
123
|
+
let uploaded = splitPath[8];
|
|
124
|
+
let access_group = splitPath[6] == '**' ? 'private' : parseInt(splitPath[6]);
|
|
125
|
+
access_group = access_group == 0 ? 'public' : access_group == 1 ? 'authorized' : access_group;
|
|
126
|
+
let obj = {
|
|
127
|
+
access_group,
|
|
128
|
+
filename,
|
|
129
|
+
url,
|
|
130
|
+
path,
|
|
131
|
+
size: fromBase62(size),
|
|
132
|
+
uploaded: fromBase62(uploaded),
|
|
133
|
+
getFile: (dataType, progress) => {
|
|
134
|
+
let config = {
|
|
135
|
+
dataType: dataType || 'download',
|
|
136
|
+
progress
|
|
137
|
+
};
|
|
138
|
+
return getFile.bind(this)(url, config);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
if (binObj[pathKey]) {
|
|
142
|
+
binObj[pathKey].push(obj);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
binObj[pathKey] = [obj];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
output.bin = binObj;
|
|
101
149
|
},
|
|
102
150
|
'data': (r) => {
|
|
103
151
|
let data = r;
|
|
@@ -118,6 +166,9 @@ function normalizeRecord(record) {
|
|
|
118
166
|
keys[k](record[k]);
|
|
119
167
|
}
|
|
120
168
|
}
|
|
169
|
+
if (record.private_key) {
|
|
170
|
+
this.__private_access_key[output.record_id] = record.private_key;
|
|
171
|
+
}
|
|
121
172
|
return output;
|
|
122
173
|
}
|
|
123
174
|
function normalizeTypedString(v) {
|
|
@@ -155,15 +206,18 @@ export async function deleteFiles(params) {
|
|
|
155
206
|
if (!Array.isArray(endpoints)) {
|
|
156
207
|
throw new SkapiError('"endpoints" should be type: array | string.', { code: 'INVALID_PARAMETER' });
|
|
157
208
|
}
|
|
158
|
-
|
|
209
|
+
let updatedRec = request.bind(this)('del-files', {
|
|
159
210
|
endpoints,
|
|
160
211
|
storage: 'records'
|
|
161
212
|
}, { auth: true, method: 'post' });
|
|
213
|
+
return updatedRec.map(r => normalizeRecord.bind(this)(r));
|
|
162
214
|
}
|
|
163
215
|
export async function uploadFiles(fileList, params) {
|
|
164
216
|
await this.__connection;
|
|
165
|
-
let
|
|
166
|
-
|
|
217
|
+
let params_request = params?.request || 'post';
|
|
218
|
+
let nestKey = params?.nestKey || '';
|
|
219
|
+
let service = params?.service || this.service;
|
|
220
|
+
if (params_request === 'post') {
|
|
167
221
|
if (!params?.record_id) {
|
|
168
222
|
throw new SkapiError('"record_id" is required.', { code: 'INVALID_PARAMETER' });
|
|
169
223
|
}
|
|
@@ -172,7 +226,7 @@ export async function uploadFiles(fileList, params) {
|
|
|
172
226
|
if (service === this.service) {
|
|
173
227
|
throw new SkapiError('invalid service.', { code: 'INVALID_PARAMETER' });
|
|
174
228
|
}
|
|
175
|
-
if (
|
|
229
|
+
if (params_request !== 'host') {
|
|
176
230
|
throw new SkapiError('invalid request.', { code: 'INVALID_PARAMETER' });
|
|
177
231
|
}
|
|
178
232
|
}
|
|
@@ -182,11 +236,14 @@ export async function uploadFiles(fileList, params) {
|
|
|
182
236
|
if (fileList instanceof HTMLFormElement) {
|
|
183
237
|
fileList = new FormData(fileList);
|
|
184
238
|
}
|
|
239
|
+
let formDataKeys = [];
|
|
185
240
|
if (fileList instanceof FormData) {
|
|
186
241
|
let fileEntries = [];
|
|
187
242
|
for (let entry of fileList.entries()) {
|
|
188
243
|
let value = entry[1];
|
|
189
244
|
if (value instanceof File) {
|
|
245
|
+
let key = entry[0];
|
|
246
|
+
formDataKeys.push(key);
|
|
190
247
|
fileEntries.push(value);
|
|
191
248
|
}
|
|
192
249
|
}
|
|
@@ -199,7 +256,7 @@ export async function uploadFiles(fileList, params) {
|
|
|
199
256
|
let getSignedParams = {
|
|
200
257
|
reserved_key,
|
|
201
258
|
service,
|
|
202
|
-
request
|
|
259
|
+
request: params_request
|
|
203
260
|
};
|
|
204
261
|
if (params?.record_id) {
|
|
205
262
|
getSignedParams.id = params.record_id;
|
|
@@ -249,13 +306,17 @@ export async function uploadFiles(fileList, params) {
|
|
|
249
306
|
}
|
|
250
307
|
return result;
|
|
251
308
|
}
|
|
252
|
-
|
|
309
|
+
let bin_endpoints = [];
|
|
310
|
+
for (let i = 0; i < fileList.length; i++) {
|
|
311
|
+
let f = fileList[i];
|
|
312
|
+
let key = formDataKeys?.[i] || '';
|
|
253
313
|
let signedParams = Object.assign({
|
|
254
|
-
key: f.name,
|
|
314
|
+
key: params_request === 'host' ? (nestKey ? nestKey + '/' : '') + f.name : key ? key + '/' + f.name : f.name,
|
|
255
315
|
sizeKey: toBase62(f.size),
|
|
256
316
|
contentType: f.type || null
|
|
257
317
|
}, getSignedParams);
|
|
258
|
-
let { fields = null, url } = await request.bind(this)('get-signed-url', signedParams, { auth: true });
|
|
318
|
+
let { fields = null, url, cdn } = await request.bind(this)('get-signed-url', signedParams, { auth: true });
|
|
319
|
+
bin_endpoints.push(cdn);
|
|
259
320
|
let form = new FormData();
|
|
260
321
|
for (let name in fields) {
|
|
261
322
|
form.append(name, fields[name]);
|
|
@@ -263,6 +324,8 @@ export async function uploadFiles(fileList, params) {
|
|
|
263
324
|
form.append('file', f);
|
|
264
325
|
try {
|
|
265
326
|
await fetchProgress(url, form, (p) => {
|
|
327
|
+
if (typeof params.progress !== 'function')
|
|
328
|
+
return;
|
|
266
329
|
params.progress({
|
|
267
330
|
status: 'upload',
|
|
268
331
|
progress: p.loaded / p.total * 100,
|
|
@@ -280,15 +343,13 @@ export async function uploadFiles(fileList, params) {
|
|
|
280
343
|
failed.push(f);
|
|
281
344
|
}
|
|
282
345
|
}
|
|
283
|
-
return { completed, failed };
|
|
346
|
+
return { completed, failed, bin_endpoints };
|
|
284
347
|
}
|
|
285
348
|
export async function getFile(url, config) {
|
|
286
349
|
if (typeof url !== 'string') {
|
|
287
350
|
throw new SkapiError('"url" should be type: string.', { code: 'INVALID_PARAMETER' });
|
|
288
351
|
}
|
|
289
|
-
|
|
290
|
-
validator.Url(url);
|
|
291
|
-
}
|
|
352
|
+
validator.Url(url);
|
|
292
353
|
let isValidEndpoint = false;
|
|
293
354
|
let splitUrl = url.split('/');
|
|
294
355
|
let host = splitUrl[2];
|
|
@@ -312,24 +373,61 @@ export async function getFile(url, config) {
|
|
|
312
373
|
}
|
|
313
374
|
}
|
|
314
375
|
let service = subdomain ? null : target_key[1];
|
|
315
|
-
validator.Params(config, {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}
|
|
376
|
+
config = validator.Params(config, {
|
|
377
|
+
expires: 'number',
|
|
378
|
+
dataType: ['base64', 'blob', 'endpoint', 'download', () => 'download'],
|
|
379
|
+
progress: p => p
|
|
380
|
+
});
|
|
320
381
|
let needAuth = target_key[0] == 'auth';
|
|
321
382
|
let filename = url.split('/').slice(-1)[0];
|
|
322
|
-
|
|
383
|
+
let expires = config?.expires || 0;
|
|
384
|
+
if (expires) {
|
|
385
|
+
if (expires < 0) {
|
|
386
|
+
throw new SkapiError('"config.expires" should be > 0. (seconds)', { code: 'INVALID_PARAMETER' });
|
|
387
|
+
}
|
|
323
388
|
let params = {
|
|
324
389
|
request: subdomain ? 'get-host' : 'get',
|
|
325
390
|
id: subdomain || target_key[5],
|
|
326
|
-
key: url
|
|
391
|
+
key: url,
|
|
392
|
+
expires
|
|
327
393
|
};
|
|
328
394
|
if (service) {
|
|
329
395
|
params.service = service;
|
|
330
396
|
}
|
|
331
397
|
url = (await request.bind(this)('get-signed-url', params, { auth: true })).url;
|
|
332
398
|
}
|
|
399
|
+
else if (needAuth) {
|
|
400
|
+
let token = this.session?.idToken?.jwtToken;
|
|
401
|
+
let access_group = target_key[6] === '**' ? '**' : parseInt(target_key[6]);
|
|
402
|
+
if (!token) {
|
|
403
|
+
throw new SkapiError('User login is required.', { code: 'INVALID_REQUEST' });
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
let currTime = Date.now() / 1000;
|
|
407
|
+
if (this.session.idToken.payload.exp < currTime) {
|
|
408
|
+
try {
|
|
409
|
+
await this.authentication().getSession({ refreshToken: true });
|
|
410
|
+
token = this.session?.idToken?.jwtToken;
|
|
411
|
+
}
|
|
412
|
+
catch (err) {
|
|
413
|
+
this.logout();
|
|
414
|
+
throw new SkapiError('User login is required.', { code: 'INVALID_REQUEST' });
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (access_group === '**') {
|
|
419
|
+
if (this.__user.user_id !== target_key[3]) {
|
|
420
|
+
throw new SkapiError('User has no access.', { code: 'INVALID_REQUEST' });
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
else if (this.__user.access_group < access_group) {
|
|
424
|
+
throw new SkapiError('User has no access.', { code: 'INVALID_REQUEST' });
|
|
425
|
+
}
|
|
426
|
+
url += `?t=${token}`;
|
|
427
|
+
}
|
|
428
|
+
if (config?.dataType === 'endpoint') {
|
|
429
|
+
return url;
|
|
430
|
+
}
|
|
333
431
|
if (config?.dataType === 'download') {
|
|
334
432
|
let a = document.createElement('a');
|
|
335
433
|
a.href = url;
|
|
@@ -340,20 +438,19 @@ export async function getFile(url, config) {
|
|
|
340
438
|
document.body.removeChild(a);
|
|
341
439
|
return null;
|
|
342
440
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
if (config?.dataType === 'base64') {
|
|
348
|
-
function blobToBase64(blob) {
|
|
349
|
-
return new Promise((resolve, _) => {
|
|
441
|
+
let blob = new Promise(async (res, rej) => {
|
|
442
|
+
try {
|
|
443
|
+
let b = await request.bind(this)(url, { service: service || this.service }, { method: 'get', noParams: true, contentType: null, responseType: 'blob', fetchOptions: { progress: config?.progress } });
|
|
444
|
+
if (config?.dataType === 'base64') {
|
|
350
445
|
const reader = new FileReader();
|
|
351
|
-
reader.onloadend = () =>
|
|
352
|
-
reader.readAsDataURL(
|
|
353
|
-
}
|
|
446
|
+
reader.onloadend = () => res(reader.result);
|
|
447
|
+
reader.readAsDataURL(b);
|
|
448
|
+
}
|
|
354
449
|
}
|
|
355
|
-
|
|
356
|
-
|
|
450
|
+
catch (err) {
|
|
451
|
+
rej(err);
|
|
452
|
+
}
|
|
453
|
+
});
|
|
357
454
|
return blob;
|
|
358
455
|
}
|
|
359
456
|
export async function getRecords(query, fetchOptions) {
|
|
@@ -373,18 +470,7 @@ export async function getRecords(query, fetchOptions) {
|
|
|
373
470
|
table: {
|
|
374
471
|
name: 'string',
|
|
375
472
|
access_group: ['number', 'private', 'public', 'authorized'],
|
|
376
|
-
subscription:
|
|
377
|
-
user_id: (v) => validator.UserId(v, 'User ID in "subscription.user_id"'),
|
|
378
|
-
group: (v) => {
|
|
379
|
-
if (typeof v !== 'number') {
|
|
380
|
-
throw new SkapiError('"subscription.group" should be type: number.', { code: 'INVALID_PARAMETER' });
|
|
381
|
-
}
|
|
382
|
-
if (v > 99 || v < 0) {
|
|
383
|
-
throw new SkapiError('"subscription.group" should be within range: 0 ~ 99.', { code: 'INVALID_PARAMETER' });
|
|
384
|
-
}
|
|
385
|
-
return v;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
473
|
+
subscription: (v) => validator.UserId(v, 'User ID in "subscription"')
|
|
388
474
|
},
|
|
389
475
|
reference: 'string',
|
|
390
476
|
index: {
|
|
@@ -438,14 +524,17 @@ export async function getRecords(query, fetchOptions) {
|
|
|
438
524
|
throw new SkapiError('"index.range" type should match the type of "index.value".', { code: 'INVALID_PARAMETER' });
|
|
439
525
|
}
|
|
440
526
|
if (typeof v === 'string') {
|
|
441
|
-
return validator.specialChars(v, 'index.
|
|
527
|
+
return validator.specialChars(v, 'index.range', false, true);
|
|
442
528
|
}
|
|
443
529
|
return v;
|
|
444
530
|
}
|
|
445
531
|
},
|
|
446
532
|
tag: 'string',
|
|
447
|
-
|
|
533
|
+
private_key: 'string'
|
|
448
534
|
};
|
|
535
|
+
if (query?.tag) {
|
|
536
|
+
validator.specialChars(query.tag, 'tag', false, true);
|
|
537
|
+
}
|
|
449
538
|
if (query?.table) {
|
|
450
539
|
if (query.table.access_group === 'public') {
|
|
451
540
|
query.table.access_group = 0;
|
|
@@ -453,6 +542,9 @@ export async function getRecords(query, fetchOptions) {
|
|
|
453
542
|
else if (query.table.access_group === 'authorized') {
|
|
454
543
|
query.table.access_group = 1;
|
|
455
544
|
}
|
|
545
|
+
if (query.table?.name) {
|
|
546
|
+
validator.specialChars(query.table.name, 'table name', true, true);
|
|
547
|
+
}
|
|
456
548
|
if (typeof query.table.access_group === 'number') {
|
|
457
549
|
if (!this.__user) {
|
|
458
550
|
if (0 < query.table.access_group) {
|
|
@@ -467,6 +559,7 @@ export async function getRecords(query, fetchOptions) {
|
|
|
467
559
|
if (query?.index && !query.index?.name) {
|
|
468
560
|
throw new SkapiError('"index.name" is required when using "index" parameter.', { code: 'INVALID_REQUEST' });
|
|
469
561
|
}
|
|
562
|
+
let is_reference_fetch = '';
|
|
470
563
|
if (query?.record_id) {
|
|
471
564
|
validator.specialChars(query.record_id, 'record_id', false, false);
|
|
472
565
|
let outputObj = { record_id: query.record_id };
|
|
@@ -474,6 +567,9 @@ export async function getRecords(query, fetchOptions) {
|
|
|
474
567
|
outputObj.service = query.service;
|
|
475
568
|
}
|
|
476
569
|
query = outputObj;
|
|
570
|
+
if (this.__private_access_key[query.record_id]) {
|
|
571
|
+
query.private_key = this.__private_access_key[query.record_id];
|
|
572
|
+
}
|
|
477
573
|
}
|
|
478
574
|
else {
|
|
479
575
|
let ref_user;
|
|
@@ -482,26 +578,44 @@ export async function getRecords(query, fetchOptions) {
|
|
|
482
578
|
}
|
|
483
579
|
if (query.reference) {
|
|
484
580
|
try {
|
|
485
|
-
ref_user = validator.UserId(query
|
|
581
|
+
ref_user = validator.UserId(query.reference);
|
|
486
582
|
}
|
|
487
583
|
catch (err) {
|
|
584
|
+
validator.specialChars(query.reference, 'reference', false, false);
|
|
585
|
+
is_reference_fetch = query.reference;
|
|
586
|
+
if (this.__private_access_key[is_reference_fetch]) {
|
|
587
|
+
query.private_key = this.__private_access_key[is_reference_fetch];
|
|
588
|
+
}
|
|
488
589
|
}
|
|
489
590
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
591
|
+
let isAdmin = await this.checkAdmin();
|
|
592
|
+
let q = validator.Params(query || {}, struct, ref_user || isAdmin ? [] : ['table']);
|
|
593
|
+
if (typeof q.table !== 'string') {
|
|
594
|
+
if (q.table?.subscription) {
|
|
595
|
+
if (!this.session) {
|
|
596
|
+
throw new SkapiError('Unsigned users have no access to subscription records.', { code: 'INVALID_REQUEST' });
|
|
597
|
+
}
|
|
598
|
+
q.table.subscription = {
|
|
599
|
+
user_id: q.table.subscription,
|
|
600
|
+
group: 1
|
|
601
|
+
};
|
|
602
|
+
}
|
|
493
603
|
}
|
|
604
|
+
query = q;
|
|
494
605
|
}
|
|
495
|
-
let auth = query.hasOwnProperty('access_group') && query.table.access_group ? true : !!this.__user;
|
|
606
|
+
let auth = query.hasOwnProperty('access_group') && typeof query.table !== 'string' && query.table.access_group ? true : !!this.__user;
|
|
496
607
|
let result = await request.bind(this)('get-records', query, {
|
|
497
608
|
fetchOptions,
|
|
498
609
|
auth,
|
|
499
610
|
method: auth ? 'post' : 'get'
|
|
500
611
|
});
|
|
501
612
|
for (let i in result.list) {
|
|
502
|
-
result.list[i] = normalizeRecord(result.list[i]);
|
|
613
|
+
result.list[i] = normalizeRecord.bind(this)(result.list[i]);
|
|
503
614
|
}
|
|
504
615
|
;
|
|
616
|
+
if (is_reference_fetch && result?.reference_private_key) {
|
|
617
|
+
this.__private_access_key[is_reference_fetch] = result.reference_private_key;
|
|
618
|
+
}
|
|
505
619
|
return result;
|
|
506
620
|
}
|
|
507
621
|
export async function postRecord(form, config) {
|
|
@@ -525,16 +639,29 @@ export async function postRecord(form, config) {
|
|
|
525
639
|
config.table.access_group = 0;
|
|
526
640
|
}
|
|
527
641
|
}
|
|
642
|
+
if (typeof config.reference === 'string') {
|
|
643
|
+
config.reference = {
|
|
644
|
+
record_id: config.reference
|
|
645
|
+
};
|
|
646
|
+
}
|
|
528
647
|
let progress = config.progress || null;
|
|
529
|
-
|
|
648
|
+
let reference_private_key = null;
|
|
649
|
+
let config_chkd = validator.Params(config || {}, {
|
|
530
650
|
record_id: 'string',
|
|
651
|
+
readonly: 'boolean',
|
|
531
652
|
table: {
|
|
532
653
|
name: 'string',
|
|
533
|
-
|
|
654
|
+
subscription: 'boolean',
|
|
534
655
|
access_group: ['number', 'private', 'public', 'authorized']
|
|
535
656
|
},
|
|
536
657
|
reference: {
|
|
537
|
-
record_id:
|
|
658
|
+
record_id: (v) => {
|
|
659
|
+
validator.specialChars(v, '"reference.record_id"', false, false);
|
|
660
|
+
if (this.__private_access_key[v]) {
|
|
661
|
+
reference_private_key = this.__private_access_key[v];
|
|
662
|
+
}
|
|
663
|
+
return v;
|
|
664
|
+
},
|
|
538
665
|
reference_limit: (v) => {
|
|
539
666
|
if (v === null) {
|
|
540
667
|
return null;
|
|
@@ -573,41 +700,64 @@ export async function postRecord(form, config) {
|
|
|
573
700
|
return v;
|
|
574
701
|
}
|
|
575
702
|
throw new SkapiError(`"tags" should be type: <string | string[]>`, { code: 'INVALID_PARAMETER' });
|
|
703
|
+
},
|
|
704
|
+
remove_bin: (v) => {
|
|
705
|
+
if (!v) {
|
|
706
|
+
return null;
|
|
707
|
+
}
|
|
708
|
+
let arr = [];
|
|
709
|
+
if (Array.isArray(v)) {
|
|
710
|
+
for (let i of v) {
|
|
711
|
+
if (typeof i === 'string') {
|
|
712
|
+
arr.push(i);
|
|
713
|
+
}
|
|
714
|
+
else if (i.url && i.size && i.filename && typeof i.getFile === 'function') {
|
|
715
|
+
arr.push(i.url);
|
|
716
|
+
}
|
|
717
|
+
else {
|
|
718
|
+
throw new SkapiError(`"remove_bin" should be type: <string | BinaryFile[]>`, { code: 'INVALID_PARAMETER' });
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
return arr;
|
|
576
723
|
}
|
|
577
724
|
}, [], ['response', 'onerror', 'progress'], null);
|
|
578
|
-
if (!
|
|
725
|
+
if (!config_chkd?.table && !config_chkd?.record_id) {
|
|
579
726
|
throw new SkapiError('Either "record_id" or "table" should have a value.', { code: 'INVALID_PARAMETER' });
|
|
580
727
|
}
|
|
581
|
-
if (
|
|
582
|
-
if (
|
|
583
|
-
|
|
728
|
+
if (typeof config_chkd.table !== 'string' && config_chkd.table) {
|
|
729
|
+
if (config_chkd.table.access_group === 'public') {
|
|
730
|
+
config_chkd.table.access_group = 0;
|
|
584
731
|
}
|
|
585
|
-
else if (
|
|
586
|
-
|
|
732
|
+
else if (config_chkd.table.access_group === 'authorized') {
|
|
733
|
+
config_chkd.table.access_group = 1;
|
|
587
734
|
}
|
|
588
|
-
if (typeof
|
|
589
|
-
if (!isAdmin && this.user.access_group <
|
|
735
|
+
if (typeof config_chkd.table.access_group === 'number') {
|
|
736
|
+
if (!isAdmin && this.user.access_group < config_chkd.table.access_group) {
|
|
590
737
|
throw new SkapiError("User has no access", { code: 'INVALID_REQUEST' });
|
|
591
738
|
}
|
|
592
739
|
}
|
|
593
|
-
if (!
|
|
740
|
+
if (!config_chkd.table.name) {
|
|
594
741
|
throw new SkapiError('"table.name" cannot be empty string.', { code: 'INVALID_PARAMETER' });
|
|
595
742
|
}
|
|
743
|
+
validator.specialChars(config_chkd.table.name, 'table name', true, true);
|
|
596
744
|
if (isAdmin) {
|
|
597
|
-
if (
|
|
745
|
+
if (config_chkd.table.access_group === 'private') {
|
|
598
746
|
throw new SkapiError('Service owner cannot write private records.', { code: 'INVALID_REQUEST' });
|
|
599
747
|
}
|
|
600
|
-
if (config.table.hasOwnProperty('subscription_group')) {
|
|
601
|
-
throw new SkapiError('Service owner cannot write to subscription table.', { code: 'INVALID_REQUEST' });
|
|
602
|
-
}
|
|
603
748
|
}
|
|
604
|
-
if (
|
|
605
|
-
|
|
749
|
+
if (config_chkd.table?.subscription) {
|
|
750
|
+
config_chkd.table.subscription_group = 1;
|
|
751
|
+
delete config_chkd.table.subscription;
|
|
606
752
|
}
|
|
607
753
|
}
|
|
754
|
+
config = config_chkd;
|
|
608
755
|
delete config.response;
|
|
609
756
|
delete config.onerror;
|
|
610
757
|
delete config.progress;
|
|
758
|
+
if (reference_private_key) {
|
|
759
|
+
config.reference_private_key = reference_private_key;
|
|
760
|
+
}
|
|
611
761
|
if (config.index) {
|
|
612
762
|
if (!config.index.name || typeof config.index.name !== 'string') {
|
|
613
763
|
throw new SkapiError('"index.name" is required. type: string.', { code: 'INVALID_PARAMETER' });
|
|
@@ -629,43 +779,27 @@ export async function postRecord(form, config) {
|
|
|
629
779
|
}
|
|
630
780
|
let options = { auth: true };
|
|
631
781
|
let postData = null;
|
|
782
|
+
let to_bin = null;
|
|
632
783
|
if ((form instanceof HTMLFormElement) || (form instanceof FormData) || (form instanceof SubmitEvent)) {
|
|
633
|
-
|
|
634
|
-
let formData = !(form instanceof FormData) ? new FormData(toConvert) : form;
|
|
784
|
+
form = (form instanceof SubmitEvent) ? form.target : form;
|
|
635
785
|
let formMeta = extractFormMeta(form);
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
options.meta.data = formMeta.meta;
|
|
639
|
-
}
|
|
640
|
-
let formToRemove = {};
|
|
641
|
-
for (let [key, value] of formData.entries()) {
|
|
642
|
-
if (formMeta.meta.hasOwnProperty(key) && !(value instanceof Blob)) {
|
|
643
|
-
let f = formData.getAll(key);
|
|
644
|
-
let f_idx = f.indexOf(value);
|
|
645
|
-
if (formToRemove.hasOwnProperty(key)) {
|
|
646
|
-
formToRemove[key].push(f_idx);
|
|
647
|
-
}
|
|
648
|
-
else {
|
|
649
|
-
formToRemove[key] = [f_idx];
|
|
650
|
-
}
|
|
651
|
-
}
|
|
786
|
+
if (formMeta.to_bin.length) {
|
|
787
|
+
to_bin = formMeta.to_bin;
|
|
652
788
|
}
|
|
653
|
-
if (
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
}
|
|
662
|
-
formData.delete(key);
|
|
663
|
-
for (let dat of values) {
|
|
664
|
-
formData.append(key, dat, dat instanceof File ? dat.name : null);
|
|
665
|
-
}
|
|
789
|
+
if (formMeta.files.length) {
|
|
790
|
+
let formData = new FormData();
|
|
791
|
+
for (let f of formMeta.files) {
|
|
792
|
+
formData.append(f.name, f.file, f.file.name);
|
|
793
|
+
}
|
|
794
|
+
options.meta = config;
|
|
795
|
+
if (Object.keys(formMeta.meta).length) {
|
|
796
|
+
options.meta.data = formMeta.meta;
|
|
666
797
|
}
|
|
798
|
+
postData = formData;
|
|
799
|
+
}
|
|
800
|
+
else {
|
|
801
|
+
postData = Object.assign({ data: formMeta.meta }, config);
|
|
667
802
|
}
|
|
668
|
-
postData = formData;
|
|
669
803
|
}
|
|
670
804
|
else {
|
|
671
805
|
postData = Object.assign({ data: form }, config);
|
|
@@ -676,7 +810,28 @@ export async function postRecord(form, config) {
|
|
|
676
810
|
if (Object.keys(fetchOptions).length) {
|
|
677
811
|
Object.assign(options, { fetchOptions });
|
|
678
812
|
}
|
|
679
|
-
|
|
813
|
+
let rec = await request.bind(this)('post-record', postData, options);
|
|
814
|
+
if (to_bin) {
|
|
815
|
+
let bin_formData = new FormData();
|
|
816
|
+
for (let f of to_bin) {
|
|
817
|
+
bin_formData.append(f.name, f.file, f.file.name);
|
|
818
|
+
}
|
|
819
|
+
let uploadFileParams = {
|
|
820
|
+
record_id: rec.rec,
|
|
821
|
+
progress
|
|
822
|
+
};
|
|
823
|
+
if (config.hasOwnProperty('service')) {
|
|
824
|
+
uploadFileParams['service'] = config.service;
|
|
825
|
+
}
|
|
826
|
+
let { bin_endpoints } = await uploadFiles.bind(this)(bin_formData, uploadFileParams);
|
|
827
|
+
if (!rec.bin) {
|
|
828
|
+
rec.bin = bin_endpoints;
|
|
829
|
+
}
|
|
830
|
+
else {
|
|
831
|
+
rec.bin.push(...bin_endpoints);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
return normalizeRecord.bind(this)(rec);
|
|
680
835
|
}
|
|
681
836
|
export async function getTables(query, fetchOptions) {
|
|
682
837
|
let res = await request.bind(this)('get-table', validator.Params(query || {}, {
|
|
@@ -688,7 +843,7 @@ export async function getTables(query, fetchOptions) {
|
|
|
688
843
|
'tbl': 'table',
|
|
689
844
|
'srvc': 'service'
|
|
690
845
|
};
|
|
691
|
-
if (Array.isArray(res
|
|
846
|
+
if (Array.isArray(res?.list)) {
|
|
692
847
|
for (let t of res.list) {
|
|
693
848
|
for (let k in convert) {
|
|
694
849
|
if (t.hasOwnProperty(k)) {
|
|
@@ -701,6 +856,9 @@ export async function getTables(query, fetchOptions) {
|
|
|
701
856
|
return res;
|
|
702
857
|
}
|
|
703
858
|
export async function getIndexes(query, fetchOptions) {
|
|
859
|
+
if (!query?.table) {
|
|
860
|
+
throw new SkapiError('"table" is required.', { code: 'INVALID_PARAMETER' });
|
|
861
|
+
}
|
|
704
862
|
let p = validator.Params(query || {}, {
|
|
705
863
|
table: 'string',
|
|
706
864
|
index: (v) => validator.specialChars(v, 'index name', true, false),
|
|
@@ -743,7 +901,7 @@ export async function getIndexes(query, fetchOptions) {
|
|
|
743
901
|
'avrg_bool': 'average_bool',
|
|
744
902
|
'cnt_str': 'string_count'
|
|
745
903
|
};
|
|
746
|
-
if (Array.isArray(res
|
|
904
|
+
if (Array.isArray(res?.list)) {
|
|
747
905
|
res.list = res.list.map((i) => {
|
|
748
906
|
let iSplit = i.idx.split('/');
|
|
749
907
|
let resolved = {
|
|
@@ -767,7 +925,7 @@ export async function getTags(query, fetchOptions) {
|
|
|
767
925
|
tag: 'string',
|
|
768
926
|
condition: ['gt', 'gte', 'lt', 'lte', '>', '>=', '<', '<=', '=', 'eq', '!=', 'ne']
|
|
769
927
|
}), Object.assign({ auth: true }, { fetchOptions }));
|
|
770
|
-
if (Array.isArray(res
|
|
928
|
+
if (Array.isArray(res?.list)) {
|
|
771
929
|
for (let i in res.list) {
|
|
772
930
|
let item = res.list[i];
|
|
773
931
|
let tSplit = item.tag.split('/');
|
|
@@ -788,15 +946,17 @@ export async function deleteRecords(params) {
|
|
|
788
946
|
if (params?.record_id) {
|
|
789
947
|
return await request.bind(this)('del-records', {
|
|
790
948
|
service: params.service || this.service,
|
|
791
|
-
record_id: (
|
|
792
|
-
let id = validator.specialChars(v, 'record_id', false, false);
|
|
949
|
+
record_id: (id => {
|
|
793
950
|
if (typeof id === 'string') {
|
|
794
951
|
return [id];
|
|
795
952
|
}
|
|
953
|
+
if (!Array.isArray(id)) {
|
|
954
|
+
throw new SkapiError('"record_id" should be type: <string | string[]>', { code: 'INVALID_PARAMETER' });
|
|
955
|
+
}
|
|
796
956
|
if (id.length > 100) {
|
|
797
957
|
throw new SkapiError('"record_id" should not exceed 100 items.', { code: 'INVALID_PARAMETER' });
|
|
798
958
|
}
|
|
799
|
-
return id;
|
|
959
|
+
return validator.specialChars(id, 'record_id', false, false);
|
|
800
960
|
})(params.record_id)
|
|
801
961
|
}, { auth: true });
|
|
802
962
|
}
|
|
@@ -826,24 +986,28 @@ export async function deleteRecords(params) {
|
|
|
826
986
|
},
|
|
827
987
|
name: 'string',
|
|
828
988
|
subscription: (v) => {
|
|
829
|
-
if (isAdmin) {
|
|
989
|
+
if (isAdmin && typeof params?.table?.subscription === 'string') {
|
|
830
990
|
return validator.UserId(v, 'User ID in "table.subscription"');
|
|
831
991
|
}
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
if (typeof v === 'number') {
|
|
839
|
-
if (v >= 0 && v < 99) {
|
|
840
|
-
return v;
|
|
992
|
+
if (typeof v === 'boolean') {
|
|
993
|
+
if (v) {
|
|
994
|
+
return this.__user.user_id;
|
|
995
|
+
}
|
|
996
|
+
else {
|
|
997
|
+
return null;
|
|
841
998
|
}
|
|
842
999
|
}
|
|
843
|
-
throw new SkapiError('
|
|
844
|
-
}
|
|
1000
|
+
throw new SkapiError('"table.subscription" is an invalid parameter key.', { code: 'INVALID_PARAMETER' });
|
|
1001
|
+
},
|
|
845
1002
|
};
|
|
846
|
-
|
|
1003
|
+
let table_p = validator.Params(params.table || {}, struct, isAdmin ? [] : ['name']);
|
|
1004
|
+
if (table_p.subscription === null) {
|
|
1005
|
+
delete table_p.subscription;
|
|
1006
|
+
}
|
|
1007
|
+
else {
|
|
1008
|
+
table_p.subscription_group = 1;
|
|
1009
|
+
}
|
|
1010
|
+
params.table = table_p;
|
|
847
1011
|
}
|
|
848
1012
|
return await request.bind(this)('del-records', params, { auth: true });
|
|
849
1013
|
}
|
|
@@ -854,7 +1018,7 @@ export function grantPrivateRecordAccess(params) {
|
|
|
854
1018
|
if (!params.user_id || Array.isArray(params.user_id) && !params.user_id.length) {
|
|
855
1019
|
throw new SkapiError(`User ID is required.`, { code: 'INVALID_PARAMETER' });
|
|
856
1020
|
}
|
|
857
|
-
return recordAccess({
|
|
1021
|
+
return recordAccess.bind(this)({
|
|
858
1022
|
record_id: params.record_id,
|
|
859
1023
|
user_id: params.user_id || null,
|
|
860
1024
|
execute: 'add'
|
|
@@ -867,18 +1031,23 @@ export function removePrivateRecordAccess(params) {
|
|
|
867
1031
|
if (!params.user_id || Array.isArray(params.user_id) && !params.user_id.length) {
|
|
868
1032
|
throw new SkapiError(`User ID is required.`, { code: 'INVALID_PARAMETER' });
|
|
869
1033
|
}
|
|
870
|
-
return recordAccess({
|
|
1034
|
+
return recordAccess.bind(this)({
|
|
871
1035
|
record_id: params.record_id,
|
|
872
1036
|
user_id: params.user_id || null,
|
|
873
1037
|
execute: 'remove'
|
|
874
1038
|
});
|
|
875
1039
|
}
|
|
876
1040
|
export function listPrivateRecordAccess(params) {
|
|
877
|
-
|
|
1041
|
+
let list = recordAccess.bind(this)({
|
|
878
1042
|
record_id: params.record_id,
|
|
879
1043
|
user_id: params.user_id || null,
|
|
880
1044
|
execute: 'list'
|
|
881
1045
|
});
|
|
1046
|
+
list.list = list.list.map((i) => {
|
|
1047
|
+
i.record_id = i.rec_usr.split('/')[0];
|
|
1048
|
+
i.user_id = i.rec_usr.split('/')[1];
|
|
1049
|
+
return i;
|
|
1050
|
+
});
|
|
882
1051
|
}
|
|
883
1052
|
export function requestPrivateRecordAccessKey(record_id) {
|
|
884
1053
|
return request.bind(this)('request-private-access-key', { record_id }, { auth: true });
|
|
@@ -894,13 +1063,16 @@ function recordAccess(params) {
|
|
|
894
1063
|
}
|
|
895
1064
|
throw new SkapiError(`User ID is required.`, { code: 'INVALID_PARAMETER' });
|
|
896
1065
|
}
|
|
897
|
-
let id =
|
|
1066
|
+
let id = v;
|
|
898
1067
|
if (typeof id === 'string') {
|
|
899
|
-
|
|
1068
|
+
id = [id];
|
|
900
1069
|
}
|
|
901
1070
|
if (id.length > 100) {
|
|
902
1071
|
throw new SkapiError(`Cannot process more than 100 users at once.`, { code: 'INVALID_REQUEST' });
|
|
903
1072
|
}
|
|
1073
|
+
for (let i of id) {
|
|
1074
|
+
validator.UserId(i, 'User ID in "user_id"');
|
|
1075
|
+
}
|
|
904
1076
|
return id;
|
|
905
1077
|
},
|
|
906
1078
|
execute: ['add', 'remove', 'list']
|