flamerest 1.0.121 → 1.0.123

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.
Files changed (4) hide show
  1. package/REST.ts +2 -2
  2. package/package.json +9 -3
  3. package/REST.d.ts +0 -260
  4. package/REST.js +0 -849
package/REST.ts CHANGED
@@ -489,7 +489,7 @@ export class FLAMEREST {
489
489
  * @param {number|string|null} insertAfter
490
490
  * @param {boolean|null} insertFirst
491
491
  */
492
- async create<T>(table: string, values: object, appendTo: number | string | null = null, insertAfter: number | string | null = null, insertFirst: boolean | null = null): Promise<SavedObject<T>> {
492
+ async create<T>(table: string, values: object, appendTo: number | string | null = null, insertAfter: number | string | null = null, insertFirst: number | string | null = null): Promise<SavedObject<T>> {
493
493
  // Нормализуем имена таблиц
494
494
  table = table.replace(/_/g, "");
495
495
  // Подготовить значения
@@ -529,7 +529,7 @@ export class FLAMEREST {
529
529
  * @param {number|string|null} insertAfter
530
530
  * @param {boolean|null} insertFirst
531
531
  */
532
- async edit<T>(table: string, ID: number | string, values: object, appendTo: number | string | null = null, insertAfter: number | string | null = null, insertFirst: boolean | null = null): Promise<SavedObject<T>> {
532
+ async edit<T>(table: string, ID: number | string, values: object, appendTo: number | string | null = null, insertAfter: number | string | null = null, insertFirst: number | string | null = null): Promise<SavedObject<T>> {
533
533
  // Нормализуем имена таблиц
534
534
  table = table.replace(/_/g, "");
535
535
  // Подготовить значения
package/package.json CHANGED
@@ -1,9 +1,15 @@
1
1
  {
2
2
  "name": "flamerest",
3
- "version": "1.0.121",
3
+ "version": "1.0.123",
4
4
  "description": "",
5
- "main": "REST.js",
6
- "typings": "./REST.d.ts",
5
+ "main": "./REST.ts",
6
+ "typings": "./REST.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./REST.ts",
10
+ "types": "./REST.ts"
11
+ }
12
+ },
7
13
  "scripts": {
8
14
  "publishx": "git push && npm --no-git-tag-version version patch && npm publish"
9
15
  },
package/REST.d.ts DELETED
@@ -1,260 +0,0 @@
1
- interface IREST {
2
-
3
- /**
4
- * Сервер, endpoint
5
- */
6
- SERVER: string,
7
-
8
- /**
9
- * Версия api
10
- */
11
- version: string,
12
-
13
- /**
14
- * Если авторизация по токену
15
- */
16
- isAuthByJWTQuery: boolean,
17
-
18
- /**
19
- * Токен авторизации
20
- */
21
- token: string | null,
22
-
23
- /**
24
- * Будет вызван, если любой из запросов вернут требование авторизоваться
25
- */
26
- unauthorized_callback: () => {},
27
-
28
- /**
29
- * Токен приложения конкретного клиента для отправки пуш уведомлений именно ему
30
- */
31
- pushNotificationToken: string | null;
32
-
33
-
34
- /**
35
- * Стандартное число запросов на страницу
36
- * @type {number}
37
- */
38
- perPageDefault: number,
39
-
40
- /**
41
- *
42
- * @param {string} url Адрес
43
- * @param {object|string} params Параметры, которые надо передать, могут быть в виде объекта или строки
44
- * @param {string} type Тип
45
- * @param {string} responseType Тип ответа: json или blob
46
- * @param {Object} customHeaders объект с доп заголовками, которые надо включить в запрос
47
- */
48
- request(url: string, params: object | string, type?: string, responseType?: 'json' | 'blob', isNeedToken?: boolean, customHeaders?: object): Promise<CustomAnyRequest | Rows<any> | Row<any>>,
49
-
50
- /**
51
- * Получить выборку из таблицы через REST
52
- * @param table
53
- * @param fields
54
- * @param where Позволяет делать выборку из связанных таблиц, надо только их указать через название таблицы sites.id=5, и указать колонку в expand
55
- * @param expand Устаревшая механика, оставлена для совместимости
56
- * @param sortfields
57
- * @param page
58
- * @param perPage
59
- * @param RemoveDuplicates
60
- * @param format
61
- * @param titles Это чтобы мы могли контроллить какие названия полей мы будет загружать при экспорте, чтобы они были как в таблице
62
- * @return Promise<object>
63
- */
64
- get<T>(table: string, where?: object | string | null, extfields?: object | Array<string>, fields?: object | Array<string> | string | null, sortfields?: object | Array<string> | string | null, page?: number, perPage?: number, RemoveDuplicates?, format?, titles?): Promise<Rows<T>>,
65
-
66
- /**
67
- * Получить все записи по запросу [постранично]
68
- * @param {string} table
69
- * @param {object} params
70
- * @returns
71
- */
72
- all<T>(table: string, params?: {
73
- where?: object,
74
- extfields?: object | Array<string>,
75
- fields?: object | Array<string>,
76
- sort?: Array<string>,
77
- page?: number,
78
- perPage?: number,
79
- tree?: number,
80
- params?: any,
81
- export?: {
82
- format?: 'xlsx' | 'csv',
83
- titles?: Array<string>,
84
- filename?: string,
85
- }
86
- }): Promise<Rows<T>>,
87
-
88
- /**
89
- * Получить одну запись по ID или по условию выборки [первая запись]
90
- * @param {string} table
91
- * @param {number|string|object} IDOrWhere
92
- * @param {object|Array} fields
93
- * @param {string} primaryKeyName если указан ID, то указать название первичного ключа, если от id он отличается
94
- */
95
- one<T>(table: string, IDOrWhere: number | string | object, extfields?: object | Array<string>, fields?: object | Array<string> | null, primaryKeyName?: string): Promise<T | null>,
96
-
97
- /**
98
- * Создать новую запись
99
- * @param table
100
- * @param values
101
- */
102
- create<T>(table: string, values: object, appendTo?: number | string | null, insertAfter?: number | string | null, insertFirst?: number | string | null): Promise<SavedObject<T>>,
103
-
104
- /**
105
- * Удалить запись
106
- * @param table
107
- * @param id
108
- * @param byFields Если указан, удаляет по этим параметрам
109
- */
110
- remove(table: string, id: number | string, byFields?: object): Promise<boolean | Array<any>>,
111
-
112
- /**
113
- * Редактировать значения
114
- * @param table
115
- * @param ID
116
- * @param values
117
- */
118
- edit<T>(table: string, ID: number | string, values: object, appendTo: number | string | null, insertAfter: number | string | null, insertFirst: number | string | null): Promise<SavedObject<T>>,
119
-
120
-
121
- /**
122
- * Получить схемы всех таблиц
123
- */
124
- getCRUDInfo(): object,
125
-
126
- /**
127
- * Авторизоваться
128
- * @param username
129
- * @param password
130
- */
131
- auth(username?: string, password?: string, pushNotificationToken?: object | null): Promise<Authorized>,
132
-
133
- /**
134
- * Зарегистрироваться с этим логином и паролем
135
- * @param email
136
- * @param username
137
- * @param password
138
- * @param name
139
- */
140
- signup(email: string | null, username: string | null, password: string, name: string | null, pushNotificationToken: object | null, data: any): Promise<Authorized>,
141
-
142
-
143
- /**
144
- * Выйти из системы
145
- */
146
- logout(): Promise<object>,
147
-
148
-
149
- /**
150
- * Восстановление пароля
151
- * Запрос на восстановление пароля
152
- * @param {*} email
153
- * @returns
154
- */
155
- ResetPasswordRequest(email: string): Promise<any>,
156
-
157
- /**
158
- * Восстановление пароля
159
- * Проверка токена восстановления
160
- * @param {*} token токен подтверждения
161
- * @returns
162
- */
163
- ResetPasswordTokenCheck(token: string): Promise<any>,
164
-
165
- /**
166
- * Восстановление пароля
167
- * Сохранение нового пароля
168
- * @param {*} token
169
- * @param {*} password
170
- * @returns
171
- */
172
- ResetPasswordSaveNewPassword(token: string, password: string): Promise<any>,
173
-
174
- /**
175
- * Подготовить объект под загрузку: загрузить данные из элементов [type=file]
176
- * Работает ссылочно, меняя основной массив
177
- * @param values
178
- */
179
- prepare(values: any): Promise<any>,
180
-
181
-
182
- /**
183
- * Заполнить объект данными, пришедшими из базы, сохраняя оригинальные классы и функции
184
- * @param object Заполняемый объект
185
- * @param values Аналогичный объект-источник данных
186
- */
187
- fillObject<T>(object: T, values: any): T,
188
-
189
- /**
190
- * Прочесть файл асинхронно
191
- * @param {File} file
192
- * @param {'data' | 'text'} readAs
193
- * @returns {Promise<string>}
194
- */
195
- readFileAsync(file: File, readAs: 'data' | 'text'): Promise<string>
196
-
197
- }
198
-
199
-
200
- /**
201
- * Базовый ответ от сервера
202
- */
203
- export type BaseResponse<T> = {
204
- type: string,
205
- status: number,
206
- ok: boolean,
207
- data?: T,
208
- errors: object | undefined,
209
- message: string | undefined,
210
- }
211
-
212
- /**
213
- * Часть ответа с пагинацией
214
- */
215
- export type Paginated = {
216
- pages: {
217
- page: number,
218
- perPage: number,
219
- count: number,
220
- total: number,
221
- }
222
- }
223
-
224
- /**
225
- * Стандартный ответ от Request с несколькими строками
226
- */
227
- export type Rows<T> = BaseResponse<Array<T>> & Paginated;
228
-
229
- export type Row<T> = BaseResponse<T> & Paginated;
230
-
231
- export type CustomRequest<T> = BaseResponse<T>;
232
-
233
- export type CustomAnyRequest = BaseResponse<any>;
234
-
235
-
236
- /**
237
- * Стандартный ответ от Request с изменённой строкой
238
- */
239
- export type SavedObject<T> = BaseResponse<T>;
240
-
241
- /**
242
- * Результат авторизации
243
- */
244
- export type Authorized = {
245
- isAuthorized: boolean;
246
- User: {
247
- avatar: string,
248
- country: string,
249
- id: number,
250
- lang: string,
251
- name: string,
252
- role: string
253
- };
254
- errors: { [key: string]: any };
255
- token: string;
256
- }
257
-
258
- declare const REST: IREST;
259
-
260
- export default REST;
package/REST.js DELETED
@@ -1,849 +0,0 @@
1
-
2
- class FLAMEREST {
3
-
4
- constructor(server_address, localhost_endpoint, unauthorized_callback, version) {
5
-
6
- /**
7
- * Адрес серва
8
- * @type {string}
9
- */
10
- if (typeof window === 'undefined' && server_address === undefined) this.SERVER = "http://localhost/";
11
- if (typeof window !== 'undefined' && server_address === undefined) this.SERVER = window.location.protocol + "//" + window.location.host;
12
- if (server_address !== undefined) this.SERVER = server_address;
13
- this.SERVER = this.SERVER.substr(this.SERVER.length - 2, 1) === '/' ? this.SERVER.substr(0, this.SERVER.length - 1) : this.SERVER;
14
-
15
- if (typeof window !== 'undefined' && window.location.hostname === 'localhost' && localhost_endpoint !== undefined) this.SERVER = localhost_endpoint;
16
-
17
- /**
18
- * Стандартное число запросов на страницу
19
- * @type {number}
20
- */
21
- this.perPageDefault = 20;
22
-
23
- /**
24
- * Будет вызван, если любой из запросов вернут требование авторизоваться
25
- */
26
- this.unauthorized_callback = unauthorized_callback;
27
-
28
- /**
29
- * Токен приложения конкретного клиента для отправки пуш уведомлений именно ему
30
- */
31
- this.pushNotificationToken = null;
32
-
33
- /**
34
- * Версия api
35
- */
36
- this.version = version ? version : 'v1';
37
-
38
- /**
39
- * Если авторизация по токену, то он сюда подставляется
40
- */
41
- this.isAuthByJWTQuery = true;
42
-
43
- /**
44
- * Режим авторизации: Bearer|Link
45
- */
46
- this.authMode = "Bearer";
47
-
48
- this.token = null;
49
-
50
- }
51
-
52
- install(Vue, options) {
53
- window.REST = this;
54
- }
55
-
56
-
57
- /**
58
- *
59
- * @param {string} url Адрес
60
- * @param {object|string|FormData} params Параметры, которые надо передать, могут быть в виде объекта или строки
61
- * @param {string} type Тип
62
- * @param {string} responseType Тип ответа: json или blob
63
- * @param {Object} customHeaders объект с доп заголовками, которые надо включить в запрос
64
- */
65
- request(url, params, type = 'GET', responseType = 'json', isNeedToken = true, customHeaders = {}) {
66
-
67
- // Нормализуем параметры, если они есть
68
- if (typeof params === "object" && params !== null) {
69
-
70
- if (!(params instanceof FormData))
71
- params = JSON.stringify(params);
72
-
73
- if (type === 'GET')
74
- type = 'POST';
75
-
76
- responseType = 'json';
77
-
78
- }
79
-
80
-
81
- // Подставляем сервер автоматом в запрос начинающийся с /
82
- if (url[0] === '/' && url[1] !== '/') url = this.SERVER + url;
83
-
84
- let that = this;
85
-
86
- // Фетч поддерживается - получаем через него, это быстрее
87
- if (typeof fetch === "function") {
88
-
89
- return new Promise(async (resolve, reject) => {
90
-
91
- try {
92
-
93
- // Авторизация
94
- if (this.isAuthByJWTQuery && isNeedToken === true && this.token !== null && this.token !== undefined && this.token !== 'undefined') {
95
- switch (this.authMode) {
96
- case 'Link':
97
- url = url + (url.indexOf("?") === -1 ? "?" : "&") + "access-token=" + this.token;
98
- default:
99
- Object.assign(customHeaders, { 'Authorization': 'Bearer ' + this.token })
100
- }
101
- }
102
-
103
- // Уникальный user_hash
104
- let user_hash = localStorage.getItem('user_hash');
105
- if (user_hash) {
106
- Object.assign(customHeaders, { udata: user_hash });
107
- }
108
-
109
- // Тело запроса
110
- let requestBody = {
111
- method: type,
112
- mode: 'cors',
113
- headers: Object.assign(
114
- (params instanceof FormData ? {} : {
115
- 'Content-type': 'application/json; charset=utf-8'
116
- }), customHeaders)
117
- };
118
-
119
- if (type !== 'GET')
120
- // Принимает и formData тоже и чистую json строку
121
- requestBody.body = params;
122
-
123
- // Создаём подпись каждого запроса
124
- let SIGN = "empty";
125
- try {
126
- SIGN = await this.hmac_sha256(requestBody.body, user_hash);
127
- }
128
- catch (ex) {
129
- // window.alert('Can`t create a request');
130
- // throw ex;
131
- }
132
-
133
- requestBody.headers['sign'] = SIGN;
134
-
135
- // Делаем запрос
136
- let response = await fetch(url, requestBody);
137
-
138
- // Тело ответа формируется в два этапа: сперва заголовки, затем ответ
139
- let ResolveBody = {
140
- status: response.status,
141
- ok: response.ok
142
- };
143
-
144
- // Ответ с ошибкой
145
- if (!response.ok) {
146
-
147
- // Тело ошибки
148
- ResolveBody.message = response.statusText;
149
-
150
- try {
151
-
152
- ResolveBody.errors = await response.json();
153
-
154
- // Ошибки
155
- switch (ResolveBody.status) {
156
-
157
- // Ошибка валидации: Собираем все ошибки полей
158
- case 422:
159
- let Errs = {};
160
- for (let err of ResolveBody.errors) {
161
- Errs[err['field']] = Errs[err['field']] === undefined ? err['message'] : Errs[err['field']] + ". " + err['message']
162
- }
163
- ResolveBody.errors = Errs;
164
- break;
165
- case 401:
166
- if (typeof this.unauthorized_callback === 'function') {
167
- this.unauthorized_callback();
168
- }
169
- break;
170
- }
171
-
172
- }
173
- catch (exjson) {
174
- ResolveBody.errors = await response.text();
175
- }
176
-
177
- // Рапортуем об ошибке
178
- console.error('Ошибка загрузки [' + response.status + '] ' + url + ": " + response.statusText, ResolveBody, ResolveBody.errors);
179
-
180
- // Возвращаем управление, которое работает без блока catch, достаточно проверить на if(response.errors)
181
- resolve(ResolveBody);
182
- return;
183
-
184
- }
185
-
186
- // Загрузка успешна
187
-
188
- // Если в заголовках указана паджинация
189
- let pages = undefined;
190
- if (response.headers.get('X-Pagination-Current-Page') !== null) {
191
- pages = {
192
- page: parseInt(response.headers.get('X-Pagination-Current-Page')),
193
- perPage: parseInt(response.headers.get('X-Pagination-Per-Page')),
194
- count: parseInt(response.headers.get('X-Pagination-Page-Count')),
195
- total: parseInt(response.headers.get('X-Pagination-Total-Count')),
196
- }
197
- }
198
-
199
-
200
-
201
- // Заполняем тело ответа заголовками
202
- ResolveBody.type = "json";
203
- ResolveBody.data = {};
204
- ResolveBody.pages = pages;
205
-
206
- // Получаем тело ответа
207
- switch (responseType) {
208
- case 'json': ResolveBody.data = await response.text(); break;
209
- case 'blob': {
210
- // Записываем имя файла и mime-тип
211
- ResolveBody.filename = 'file'; //response.headers.get('content-disposition').split('filename=')[1];
212
- ResolveBody.MimeType = response.headers.get('content-Type');
213
- ResolveBody.data = await response.blob();
214
-
215
- // Если ответ в виде блоба, сразу его отдаём без декодировки
216
- resolve(ResolveBody);
217
- return;
218
- }
219
- }
220
-
221
- // Декодируем тело ответа, если оно есть
222
- if (ResolveBody.data === undefined) {
223
- ResolveBody.errors = ["Принятый ответ пуст"];
224
- console.error("Принятый ответ пуст", ResolveBody);
225
- resolve(ResolveBody);
226
- return;
227
- };
228
-
229
- // Пустой ответ конвертируем в валидный пустой объект
230
- if (ResolveBody.data === "") ResolveBody.data = "{}";
231
-
232
- // Декодируем
233
- try {
234
- ResolveBody.data = JSON.parse(ResolveBody.data);
235
- } catch (ex) {
236
- ResolveBody.errors = ["Ошибка декодирования"];
237
- console.error("Ошибка декодирования", ResolveBody);
238
- reject(ResolveBody);
239
- return;
240
- }
241
-
242
- // Декодинг успешен
243
-
244
- // Если пришёл ответ: неавторизовано, и указан коллбек авторизации - запускаем его
245
- if (response.Auth === false) {
246
- if (that.unauthorized_callback !== undefined) { that.unauthorized_callback(); resolve(response); return; }
247
- }
248
-
249
-
250
- // Возвращаем успешную загрузку
251
- resolve(ResolveBody);
252
-
253
- return;
254
-
255
- }
256
-
257
- catch (err) {
258
-
259
- // Ошибка загрузки любого типа
260
- // TODO: на этом этапе стоит сделать, чтобы он пробовал повторить запрос, если это GET
261
-
262
- if (typeof err !== 'object' || err.message === undefined) {
263
- err = {
264
- status: 0,
265
- message: '',
266
- };
267
- }
268
-
269
- if (typeof err.body === 'object') {
270
- err.body = await err.body;
271
- }
272
-
273
- console.log('Ошибка загрузки [' + 0 + '] ' + url + ": " + err.message);
274
-
275
- reject(err);
276
-
277
- }
278
-
279
- });
280
-
281
- }
282
-
283
-
284
- // Фетч не поддерживается - возвращаем промисифицированный XHR
285
- else {
286
-
287
- return new Promise((resolve, reject) => {
288
-
289
- // Генерим новый запрос
290
- let xhr = new XMLHttpRequest();
291
- xhr.open(type, url, true);
292
-
293
- // Автоматический парсинг json ответа
294
- xhr.responseType = 'json';
295
-
296
- // Запрос тоже в json
297
- xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8');
298
-
299
- // Запрос между доменами свободный, если это dev-среда
300
- xhr.withCredentials = false;
301
-
302
- // Отправляем запрос
303
- xhr.send(params === undefined ? null : params);
304
-
305
- xhr.onload = function () {
306
-
307
- // Ошибка загрузки
308
- if (xhr.status !== 200) {
309
- console.log('Ошибка загрузки [' + xhr.status + '] ' + url + ": " + xhr.statusText);
310
- return reject({
311
- status: xhr.status,
312
- message: xhr.statusText
313
- });
314
- }
315
-
316
- // Загрузка успешна
317
-
318
- // Если в заголовках указана паджинация
319
- let pages = undefined;
320
- if (xhr.getResponseHeader('X-Pagination-Current-Page') !== null) {
321
- pages = {
322
- page: parseInt(xhr.getResponseHeader('X-Pagination-Current-Page')),
323
- perPage: parseInt(xhr.getResponseHeader('X-Pagination-Per-Page')),
324
- count: parseInt(xhr.getResponseHeader('X-Pagination-Page-Count')),
325
- total: parseInt(xhr.getResponseHeader('X-Pagination-Total-Count')),
326
- }
327
- }
328
-
329
-
330
- return resolve({
331
- status: xhr.status,
332
- type: xhr.responseType,
333
- data: xhr.response,
334
- pages: pages
335
- });
336
- };
337
-
338
- // Ошибка загрузки, не связанная с ХТТП, например обрыв соединения
339
- // TODO: на этом этапе стоит сделать, чтобы он пробовал повторить запрос, если это GET
340
- xhr.onerror = function () {
341
- console.log('Ошибка загрузки [' + 0 + '] ' + url + ": Нет соединения с сервером");
342
- return reject({
343
- status: 0,
344
- message: "Нет соединения с сервером"
345
- });
346
- };
347
-
348
- });
349
-
350
- }
351
-
352
- }
353
-
354
- /**
355
- * Получить выборку из таблицы через REST
356
- * @param table
357
- * @param fields
358
- * @param where Позволяет делать выборку из связанных таблиц, надо только их указать через название таблицы sites.id=5, и указать колонку в expand
359
- * @param expand
360
- * @param sortfields
361
- * @param page
362
- * @param perPage
363
- * @param RemoveDuplicates
364
- * @param format
365
- * @param titles Это чтобы мы могли контроллить какие названия полей мы будет загружать при экспорте, чтобы они были как в таблице
366
- * @param tree дерево
367
- * @param params Доп параметры для кастомизации запроса на беке
368
- * @param exportData имя файла для экспорта
369
- * @return Promise<>
370
- */
371
- get(table, where, extfields, fields, sortfields, page, perPage, RemoveDuplicates, format, titles, tree, params, exportData) {
372
-
373
- // Нормализуем имена таблиц
374
- table = table.replace(/_/g, "");
375
-
376
- let responseType = "json";
377
-
378
- // Генерим запрос
379
- let query = this.SERVER + '/api/' + this.version + '/' + table;
380
-
381
- let json = {};
382
-
383
- // Генерим условия
384
- if (where !== undefined && where !== null)
385
- json.where = where;
386
-
387
- // Генерим условия
388
- if (tree !== undefined && tree !== null)
389
- json.tree = tree;
390
-
391
- if (fields !== undefined && fields !== null)
392
- json.fields = fields;
393
-
394
- if (sortfields !== undefined && sortfields !== null)
395
- json.sort = sortfields;
396
-
397
- if (extfields !== undefined && extfields !== null)
398
- json.extfields = extfields;
399
-
400
- if (params !== undefined && params !== null)
401
- json.params = params;
402
-
403
- if (RemoveDuplicates !== undefined && RemoveDuplicates !== null)
404
- json.RemoveDuplicates = true;
405
-
406
- if (titles !== undefined && titles !== null)
407
- json.titles = titles;
408
-
409
- // экспорт
410
- if (exportData !== undefined && exportData !== null) {
411
- json.export = {
412
- format: exportData.format ?? 'xlsx',
413
- titles: exportData.titles ?? [],
414
- filename: exportData.filename ?? 'export_' + Date.now() + ".xlsx",
415
- };
416
- responseType = "blob";
417
- }
418
-
419
- // Страницы
420
- json['per-page'] = perPage === undefined ? this.perPageDefault : perPage;
421
- json['page'] = page === undefined ? 1 : page;
422
-
423
- return this.request(query, JSON.stringify(json), 'POST', responseType);
424
-
425
- }
426
-
427
- /**
428
- * Получить все записи по запросу [постранично]
429
- * @param {string} table
430
- * @param {object} params
431
- * @returns {object}
432
- */
433
- all(table, params) {
434
- return this.get(table, params?.where, params?.extfields, params?.fields, params?.sort, params?.page, params?.perPage, null, null, null, params?.tree, params?.params, params?.export);
435
- }
436
-
437
- /**
438
- * Получить одну запись по ID или по условию выборки [первая запись]
439
- * @param {string} table
440
- * @param {number|string|object} IDOrWhere
441
- * @param {object|Array} fields
442
- * @param {string} primaryKeyName
443
- * @returns {object|null}
444
- */
445
- async one(table, IDOrWhere, extfields = null, fields = null, primaryKeyName = 'id') {
446
-
447
- let where = {};
448
- if (typeof IDOrWhere === 'string' || typeof IDOrWhere === 'number') where = { [primaryKeyName]: IDOrWhere };
449
- else if (typeof IDOrWhere === 'object') where = IDOrWhere;
450
- else throw "Нужно передавать ID или объект";
451
-
452
- let resp = await this.get(table, where, extfields, fields, null, 1, 1);
453
- if (resp.errors) return resp;
454
- if (resp.data.length === 0) return null;
455
-
456
- return resp.data[0];
457
- }
458
-
459
- /**
460
- * Создать новую запись
461
- * @param table
462
- * @param values
463
- */
464
- async create(table, values, appendTo = null, insertAfter = null, insertFirst = null) {
465
-
466
- // Нормализуем имена таблиц
467
- table = table.replace(/_/g, "");
468
-
469
- // Подготовить значения
470
- if (!(values instanceof FormData))
471
- await this.prepare(values);
472
-
473
- if (appendTo === undefined) appendTo = null;
474
- if (insertAfter === undefined) insertAfter = null;
475
- if (insertFirst === undefined) insertFirst = null;
476
-
477
- return this.request(this.SERVER + '/api/' + this.version + '/' + table + '/create?'
478
- + (appendTo !== null ? '&appendTo=' + appendTo : '')
479
- + (insertAfter !== null ? '&insertAfter=' + insertAfter : '')
480
- + (insertFirst !== null ? '&insertFirst=' + insertFirst : '')
481
- , (values instanceof FormData ? values : JSON.stringify(values)), 'POST');
482
-
483
- }
484
-
485
- /**
486
- * Удалить запись
487
- * @param table
488
- * @param id
489
- * @param byFields
490
- */
491
- async remove(table, id = 0, byFields = null) {
492
-
493
- // Нормализуем имена таблиц
494
- table = table.replace(/_/g, "");
495
-
496
- let params = {};
497
- if (byFields instanceof Object) params = byFields;
498
-
499
- let resp = await this.request(this.SERVER + '/api/' + this.version + '/' + table + '/delete?id=' + id, JSON.stringify(params), 'DELETE');
500
-
501
- if (resp.status === 204) return true;
502
-
503
- return resp;
504
-
505
- }
506
-
507
- /**
508
- * Редактировать значения
509
- * @param table
510
- * @param ID
511
- * @param values
512
- */
513
- async edit(table, ID, values, appendTo = null, insertAfter = null, insertFirst = null) {
514
-
515
- // Нормализуем имена таблиц
516
- table = table.replace(/_/g, "");
517
-
518
- // Подготовить значения
519
- if (!(values instanceof FormData))
520
- await this.prepare(values);
521
-
522
- if (appendTo === undefined) appendTo = null;
523
- if (insertAfter === undefined) insertAfter = null;
524
- if (insertFirst === undefined) insertFirst = null;
525
-
526
- return this.request(this.SERVER + '/api/' + this.version + '/' + table + '/update?id=' + ID
527
- + (appendTo !== null ? '&appendTo=' + appendTo : '')
528
- + (insertAfter !== null ? '&insertAfter=' + insertAfter : '')
529
- + (insertFirst !== null ? '&insertFirst=' + insertFirst : '')
530
- , (values instanceof FormData ? values : JSON.stringify(values)), 'POST');
531
- }
532
-
533
- /**
534
- * Получить схемы всех таблиц
535
- */
536
- getCRUDInfo() {
537
- if (window.sessionStorage.getItem("crudschema") === null) {
538
- return this.request(this.SERVER + '/site/crudschema', {}, 'GET')
539
- .then(res => {
540
- // Кешируем схему в браузере на время текущей сессии (в пределах ОДНОЙ вкладки)
541
- window.sessionStorage.setItem("crudschema", JSON.stringify(res));
542
- return res;
543
- });
544
- }
545
- else
546
- return new Promise((resolve, reject) => { resolve(JSON.parse(window.sessionStorage.getItem("crudschema"))) });
547
- }
548
-
549
- async auth(username, password, pushNotificationToken) {
550
-
551
- let resp = null;
552
- if (this.token !== null && this.token !== undefined && this.token !== 'undefined' && (username === null || username === undefined)) {
553
- resp = await this.request(this.SERVER + '/auth/auth', JSON.stringify({}), 'POST', 'json', true);
554
- }
555
- else {
556
- resp = await this.request(this.SERVER + '/auth/auth', JSON.stringify({ login: username, password: password, pushNotificationToken: (pushNotificationToken ?? this.pushNotificationToken ?? null) }), 'POST', 'json', false);
557
- }
558
-
559
- if (resp.errors) return resp;
560
- if (resp.data.length === 0) { resp.errors = []; return resp; }
561
-
562
- // после успешной авторизации устанавливаем токен
563
- if (typeof resp.token === 'string') this.token = resp.token;
564
-
565
- // Сохраняем хеш юзера автоматически
566
- let user_hash = localStorage.getItem('user_hash');
567
-
568
- // Не трогаем хранилище при каждом запросе, только если нужно переустановить хеш [авторизация на др. устройстве]
569
- if (resp?.data?.User?.user_hash && resp.data.User.user_hash !== user_hash) {
570
- localStorage.setItem('user_hash', resp.data.User.user_hash);
571
- }
572
-
573
- return resp.data;
574
-
575
- }
576
-
577
- async signup(email, username, password, name, pushNotificationToken, data = null) {
578
-
579
- let resp = await this.request(this.SERVER + '/auth/signup', JSON.stringify({ login: username, email: email, password: password, name: name, data: data, pushNotificationToken: (pushNotificationToken ?? this.pushNotificationToken ?? null) }), 'POST', 'json', false);
580
-
581
- if (resp.errors) return resp;
582
- if (resp.data.length === 0) { resp.errors = []; return resp; }
583
-
584
- // после успешной авторизации устанавливаем токен
585
- if (typeof resp.token === 'string') this.token = resp.token;
586
-
587
- return resp.data;
588
-
589
- }
590
-
591
- logout() {
592
- return this.request(this.SERVER + '/auth/logout', '{}', 'POST');
593
- }
594
-
595
- /**
596
- * Восстановление пароля
597
- * Запрос на восстановление пароля
598
- * @param {*} email
599
- * @returns
600
- */
601
- async ResetPasswordRequest(email) {
602
-
603
- let resp = await this.request(this.SERVER + '/auth/resetpasswordrequest', JSON.stringify({ email: email }), 'POST', 'json', false);
604
-
605
- if (resp.errors) return resp;
606
- if (resp.data.length === 0) { resp.errors = []; return resp; }
607
-
608
- return resp.data;
609
-
610
- }
611
-
612
- /**
613
- * Восстановление пароля
614
- * Проверка токена восстановления
615
- * @param {*} token токен подтверждения
616
- * @returns
617
- */
618
- async ResetPasswordTokenCheck(token) {
619
-
620
- let resp = await this.request(this.SERVER + '/auth/resettokencheck', JSON.stringify({ token: token }), 'POST', 'json', false);
621
-
622
- if (resp.errors) return resp;
623
- if (resp.data.length === 0) { resp.errors = []; return resp; }
624
-
625
- return resp.data;
626
-
627
- }
628
-
629
- /**
630
- * Восстановление пароля
631
- * Сохранение нового пароля
632
- * @param {*} token
633
- * @param {*} password
634
- * @returns
635
- */
636
- async ResetPasswordSaveNewPassword(token, password) {
637
-
638
- let resp = await this.request(this.SERVER + '/auth/resetconfirm', JSON.stringify({ token: token, password: password }), 'POST', 'json', false);
639
-
640
- if (resp.errors) return resp;
641
- if (resp.data.length === 0) { resp.errors = []; return resp; }
642
-
643
- return resp.data;
644
-
645
- }
646
-
647
-
648
- /**
649
- * Подготовить объект под загрузку: загрузить данные из элементов Input [type=file] / Clipboard / DataTransfer [Drag&Drop/Clipboard]
650
- * @param {object} values
651
- */
652
- async prepare(values, asFormData = false) {
653
-
654
- // Если в один из параметров передан FileList или input[type=file], т.е. нужно загрузить файлы
655
-
656
- const formData = new FormData();
657
-
658
- for (let val in values) {
659
-
660
- // Пустые значения нам не нужны
661
- if (values[val] === undefined || values[val] === null) continue;
662
-
663
- // Любой вариант захода делаем массивом
664
- let valuesArr = [];
665
- if (values[val] instanceof Array) valuesArr = values[val];
666
- else valuesArr = [values[val]];
667
-
668
- // Идём по каждому значению в массиве
669
- for (let valueKey in valuesArr) {
670
-
671
- let value = valuesArr[valueKey];
672
-
673
- // Преобразуем
674
- let isRef = false;
675
- if (value instanceof Object
676
- && value.hasOwnProperty('_value')
677
- && (
678
- value._value instanceof Event ||
679
- value._value instanceof HTMLInputElement ||
680
- value._value instanceof ClipboardEvent ||
681
- value._value instanceof DataTransfer ||
682
- value._value instanceof FileList
683
- )
684
- ) { value = value.value; isRef = true }
685
-
686
- if (value instanceof Event && value.target instanceof HTMLInputElement && value.target.type === 'file') value = value.target.files;
687
- if (value instanceof HTMLInputElement && value.type === 'file') value = value.files;
688
- if (value instanceof ClipboardEvent) value = value.clipboardData.files;
689
- if (value instanceof DataTransfer) value = value.files;
690
- if (value instanceof FileList) {
691
- let newValues = [];
692
- value = Array.from(value);
693
- for (let index = 0; index < value.length; index++) {
694
- formData.append(val + "[]", value[index])
695
- newValues.push({
696
- 'name': value[index].name,
697
- 'data': asFormData ? null : await this.readFileAsync(value[index]),
698
- 'number': index, // Здесь индекс
699
- 'id': this.generateID(32)
700
- });
701
- }
702
-
703
- /*
704
- if (isRef)
705
- values[val].value = newValues;
706
- else
707
- values[val] = newValues;
708
- */
709
-
710
- if (values[val] instanceof Array) {
711
- values[val].splice(valueKey, 1);
712
- values[val].push(...newValues);
713
- }
714
- else {
715
- values[val] = newValues;
716
- }
717
-
718
- }
719
- }
720
- }
721
-
722
- formData.append("json", JSON.stringify(values));
723
-
724
- return asFormData ? formData : values;
725
-
726
- }
727
-
728
- /**
729
- * Прочесть файл асинхронно
730
- * @param {File} file
731
- * @param {'data' | 'text'} readAs
732
- * @returns {Promise<string>}
733
- */
734
- readFileAsync(file, readAs = 'data') {
735
- return new Promise((resolve, reject) => {
736
- let reader = new FileReader();
737
-
738
- reader.onloadend = () => {
739
- resolve(reader.result);
740
- };
741
-
742
- reader.onerror = reject;
743
-
744
- if (readAs === 'data')
745
- reader.readAsDataURL(file);
746
- if (readAs === 'text')
747
- reader.readAsText(file);
748
-
749
- })
750
- }
751
-
752
- /**
753
- * Заполнить существующий объект пришедшими из БД данными
754
- * сохраняя при этом оригинальные классы и функции
755
- * @param {*} object
756
- * @param {*} values
757
- */
758
- fillObject(object, values) {
759
- for (let prop in object) {
760
-
761
- // добавляем только те объекты, что уже созданы в сущности
762
- if (!values.hasOwnProperty(prop)) continue;
763
-
764
- // объекты проходим рекурсивно
765
- if (typeof values[prop] === 'object' && typeof object[prop] === 'object' && values[prop] !== null && object[prop] !== null) { this.fillObject(object[prop], values[prop]); continue }
766
-
767
- // тут остались все обычные типы - записываем
768
- object[prop] = values[prop];
769
-
770
- }
771
- return object;
772
- }
773
-
774
- generateID(length) {
775
- let result = '';
776
- const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
777
- const charactersLength = characters.length;
778
- let counter = 0;
779
- while (counter < length) {
780
- result += characters.charAt(Math.floor(Math.random() * charactersLength));
781
- counter += 1;
782
- }
783
- return result;
784
- }
785
-
786
- /**
787
- *
788
- * @param {string} data
789
- * @param {string} key
790
- * @returns
791
- */
792
- async hmac_sha256(message, secret_key) {
793
-
794
- if (!(typeof message === 'string') && !(typeof secret_key === 'string'))
795
- throw new TypeError('Expected string input data.')
796
-
797
- const enc = new TextEncoder();
798
- const encodedKey = enc.encode(secret_key);
799
- const encodedMessage = enc.encode(message);
800
-
801
- const data = encodedMessage;
802
- const key = encodedKey;
803
-
804
- if (!(data instanceof Uint8Array) && !(key instanceof Uint8Array))
805
- throw new TypeError('Expected Uint8Array input data.')
806
-
807
-
808
- if (typeof window === 'undefined' && typeof Deno === 'undefined') {
809
- const { createHmac } = await import('crypto')
810
- return Uint8Array.from([...createHmac('SHA256', key).update(data).digest()])
811
- } else {
812
-
813
- if (typeof window.crypto.subtle === 'undefined') throw "Can`t create hash";
814
-
815
- return window.crypto.subtle
816
- .importKey(
817
- 'raw',
818
- key,
819
- { name: 'HMAC', hash: { name: 'SHA-256' } },
820
- false,
821
- ['sign', 'verify']
822
- )
823
- .then(key => window.crypto.subtle.sign('HMAC', key, data))
824
- .then(signature => {
825
-
826
- // Преобразование ArrayBuffer в Uint8Array
827
- const signatureArray = new Uint8Array(signature);
828
-
829
- // Преобразование Uint8Array в строку в формате Hex
830
- const hexString = Array.from(signatureArray)
831
- .map(byte => byte.toString(16).padStart(2, '0'))
832
- .join('');
833
-
834
- return hexString;
835
-
836
- })
837
- }
838
- }
839
-
840
-
841
-
842
- }
843
-
844
-
845
- if (!window.REST) {
846
- window.REST = new FLAMEREST();
847
- }
848
-
849
- export default window.REST;