flamerest 1.0.121 → 1.0.122
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/package.json +9 -3
- package/REST.d.ts +0 -260
- package/REST.js +0 -849
package/package.json
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flamerest",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.122",
|
|
4
4
|
"description": "",
|
|
5
|
-
"main": "REST.
|
|
6
|
-
"typings": "./REST.
|
|
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;
|