complexqa_frontend_core 1.21.4 → 1.21.6
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 +1 -1
- package/publish/api/file_app_api.js +90 -0
- package/publish/api/index.js +2 -0
- package/publish/index.js +10 -0
- package/publish/services/FileAppService.js +219 -0
- package/publish/types/element_translate/typeFile.i18n.js +54 -0
- package/publish/types/element_translate/typeFileScreenshot.i18n.js +7 -0
- package/publish/types/family_files/familyFile.js +23 -0
- package/publish/types/family_files/typeFile.js +135 -0
- package/publish/types/family_files/typeFileFactory.js +39 -0
- package/publish/types/family_files/typeFileHtml.js +10 -0
- package/publish/types/family_files/typeFileJson.js +10 -0
- package/publish/types/family_files/typeFilePostman.js +10 -0
- package/publish/types/family_files/typeFileScreenshot.js +39 -0
- package/publish/types/family_files/typeFileVideo.js +10 -0
- package/publish/types/family_utils/typeUtilExecution.js +10 -2
package/package.json
CHANGED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { ApiAbstractClass } from "./api_abstract_class.js";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Main App bridge → File App (tokens, access URLs, listing).
|
|
6
|
+
*
|
|
7
|
+
* @see docs/File App Layer/README.MD
|
|
8
|
+
*
|
|
9
|
+
* @version v.1.0 (28/06/2026)
|
|
10
|
+
*/
|
|
11
|
+
export class FileAppApi extends ApiAbstractClass
|
|
12
|
+
{
|
|
13
|
+
module_prefix = 'file_app';
|
|
14
|
+
api_prefix = 'web_api';
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* POST /web_api/file_app/issue_access_token
|
|
19
|
+
*
|
|
20
|
+
* @param {object} payload
|
|
21
|
+
*
|
|
22
|
+
* @returns {Promise<{payload: object, response: object}>}
|
|
23
|
+
*/
|
|
24
|
+
async issue_access_token(payload)
|
|
25
|
+
{
|
|
26
|
+
const url = `/${ this.api_prefix }/${ this.module_prefix }/issue_access_token`;
|
|
27
|
+
payload = this.handle_request_payload(payload);
|
|
28
|
+
let response = this.api_request(url, payload);
|
|
29
|
+
response = this.handle_response(response, false);
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
response: response,
|
|
33
|
+
payload : payload,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* GET /web_api/file_app/file_access_url?file_uuid=&disposition=
|
|
40
|
+
*
|
|
41
|
+
* @param {object} payload
|
|
42
|
+
*
|
|
43
|
+
* @returns {Promise<{payload: object, response: object}>}
|
|
44
|
+
*/
|
|
45
|
+
async file_access_url(payload)
|
|
46
|
+
{
|
|
47
|
+
const url = `/${ this.api_prefix }/${ this.module_prefix }/file_access_url`;
|
|
48
|
+
const prev_method = this.method;
|
|
49
|
+
|
|
50
|
+
this.method = 'get';
|
|
51
|
+
|
|
52
|
+
payload = this.handle_request_payload(payload);
|
|
53
|
+
let response = this.api_request(url, payload);
|
|
54
|
+
response = this.handle_response(response, false);
|
|
55
|
+
|
|
56
|
+
this.method = prev_method;
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
response: response,
|
|
60
|
+
payload : payload,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* GET /web_api/file_app/list_files?entity_type=&entity_id=&category=
|
|
67
|
+
*
|
|
68
|
+
* @param {object} payload
|
|
69
|
+
*
|
|
70
|
+
* @returns {Promise<{payload: object, response: object}>}
|
|
71
|
+
*/
|
|
72
|
+
async list_files(payload)
|
|
73
|
+
{
|
|
74
|
+
const url = `/${ this.api_prefix }/${ this.module_prefix }/list_files`;
|
|
75
|
+
const prev_method = this.method;
|
|
76
|
+
|
|
77
|
+
this.method = 'get';
|
|
78
|
+
|
|
79
|
+
payload = this.handle_request_payload(payload);
|
|
80
|
+
let response = this.api_request(url, payload);
|
|
81
|
+
response = this.handle_response(response, false);
|
|
82
|
+
|
|
83
|
+
this.method = prev_method;
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
response: response,
|
|
87
|
+
payload : payload,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
package/publish/api/index.js
CHANGED
|
@@ -22,6 +22,7 @@ import { ContextOsApi } from "./context/context_os_api.js";
|
|
|
22
22
|
import { ContextScreenResolutionApi } from "./context/context_screen_resolution_api.js";
|
|
23
23
|
import { BrowserToolsUtilExecutionApi } from "./util/browser_tools_util_execution_api.js";
|
|
24
24
|
import { BrowserToolsScreenshotMultiApi } from "./util/browser_tools_screenshot_multi_api.js";
|
|
25
|
+
import { FileAppApi } from "./file_app_api.js";
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* Обертка над axios
|
|
@@ -161,6 +162,7 @@ export class Api
|
|
|
161
162
|
this.execution_context = new ExecutionContextApi(payload);
|
|
162
163
|
this.browser_tools_util_execution = new BrowserToolsUtilExecutionApi(payload);
|
|
163
164
|
this.browser_tools_screenshot_multi = new BrowserToolsScreenshotMultiApi(payload);
|
|
165
|
+
this.file_app = new FileAppApi(payload);
|
|
164
166
|
|
|
165
167
|
this.reference = this.bootstrap_reference(payload)
|
|
166
168
|
}
|
package/publish/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { Api } from "./api/index.js";
|
|
2
2
|
export { UserService } from './services/UserService.js';
|
|
3
3
|
export { TeamService } from './services/TeamService.js';
|
|
4
|
+
export { FileAppService } from './services/FileAppService.js';
|
|
4
5
|
|
|
5
6
|
export { typeFOR } from './types/family_service/typeFOR.js';
|
|
6
7
|
export { typeFilter } from './types/family_service/typeFilter.js';
|
|
@@ -36,6 +37,15 @@ export { typeUtilExecutionScreenshotMultiScreenshot } from './types/family_utils
|
|
|
36
37
|
export { typeUtilExecutionScreenshotMultiCaptureError } from './types/family_utils/typeUtilExecutionScreenshotMultiCaptureError.js';
|
|
37
38
|
export { typeUtilExecutionEvent } from './types/family_utils/typeUtilExecutionEvent.js';
|
|
38
39
|
|
|
40
|
+
export { familyFile } from './types/family_files/familyFile.js';
|
|
41
|
+
export { typeFile } from './types/family_files/typeFile.js';
|
|
42
|
+
export { typeFileScreenshot } from './types/family_files/typeFileScreenshot.js';
|
|
43
|
+
export { typeFileHtml } from './types/family_files/typeFileHtml.js';
|
|
44
|
+
export { typeFilePostman } from './types/family_files/typeFilePostman.js';
|
|
45
|
+
export { typeFileVideo } from './types/family_files/typeFileVideo.js';
|
|
46
|
+
export { typeFileJson } from './types/family_files/typeFileJson.js';
|
|
47
|
+
export { create_type_file_from_wire } from './types/family_files/typeFileFactory.js';
|
|
48
|
+
|
|
39
49
|
export { typeUser } from './types/family_elements/typeUser.js';
|
|
40
50
|
export { typeTeam } from './types/family_elements/typeTeam.js';
|
|
41
51
|
export { typeTeamMember } from './types/family_elements/typeTeamMember.js';
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { abstractService } from "./abstractService.js";
|
|
2
|
+
import { typeFile } from "../types/family_files/typeFile.js";
|
|
3
|
+
import { create_type_file_from_wire } from "../types/family_files/typeFileFactory.js";
|
|
4
|
+
import { ApiException } from "../exceptions/ApiException.js";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* File App orchestration: bootstrap URL, upload, URL refresh.
|
|
9
|
+
*
|
|
10
|
+
* @see docs/File App Layer/README.MD
|
|
11
|
+
*
|
|
12
|
+
* @version v.1.0 (28/06/2026)
|
|
13
|
+
*/
|
|
14
|
+
export class FileAppService extends abstractService
|
|
15
|
+
{
|
|
16
|
+
static file_app_url = false;
|
|
17
|
+
static api_service = false;
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {object} options
|
|
22
|
+
* @param {string} options.file_app_url
|
|
23
|
+
* @param {import('../api/index.js').Api} [options.api_service]
|
|
24
|
+
*/
|
|
25
|
+
static bootstrap(options = {})
|
|
26
|
+
{
|
|
27
|
+
if (options.file_app_url)
|
|
28
|
+
{
|
|
29
|
+
FileAppService.file_app_url = String(options.file_app_url).replace(/\/$/, '');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (options.api_service)
|
|
33
|
+
{
|
|
34
|
+
FileAppService.api_service = options.api_service;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @returns {string|false}
|
|
41
|
+
*/
|
|
42
|
+
static get_file_app_url()
|
|
43
|
+
{
|
|
44
|
+
return FileAppService.file_app_url || false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @returns {import('../api/index.js').Api}
|
|
50
|
+
*/
|
|
51
|
+
static #get_api_service()
|
|
52
|
+
{
|
|
53
|
+
if (FileAppService.api_service)
|
|
54
|
+
{
|
|
55
|
+
return FileAppService.api_service;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (typeof window !== 'undefined' && window.ApiService)
|
|
59
|
+
{
|
|
60
|
+
return window.ApiService;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
throw new Error('FileAppService: ApiService is not bootstrapped');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Upload browser File/Blob to File App (token via Main App bridge).
|
|
69
|
+
*
|
|
70
|
+
* @param {object} payload
|
|
71
|
+
* @param {File|Blob} payload.file
|
|
72
|
+
* @param {string} payload.entity_type
|
|
73
|
+
* @param {number} payload.entity_id
|
|
74
|
+
* @param {string} payload.category
|
|
75
|
+
* @param {string} [payload.original_name]
|
|
76
|
+
*
|
|
77
|
+
* @returns {Promise<import('../types/family_files/typeFile.js').typeFile>}
|
|
78
|
+
*/
|
|
79
|
+
static async upload_file(payload)
|
|
80
|
+
{
|
|
81
|
+
const file = payload?.file;
|
|
82
|
+
|
|
83
|
+
if (!file)
|
|
84
|
+
{
|
|
85
|
+
throw new Error('FileAppService.upload_file: file is required');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const entity_type = String(payload.entity_type || '');
|
|
89
|
+
const entity_id = Number(payload.entity_id);
|
|
90
|
+
const category = String(payload.category || '');
|
|
91
|
+
const mime_type = file.type || 'application/octet-stream';
|
|
92
|
+
const size_bytes = Number(file.size || 0);
|
|
93
|
+
const original_name = String(payload.original_name || file.name || 'upload');
|
|
94
|
+
const file_app_url = FileAppService.get_file_app_url();
|
|
95
|
+
|
|
96
|
+
if (!file_app_url)
|
|
97
|
+
{
|
|
98
|
+
throw new Error('FileAppService.upload_file: file_app_url is not set');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const api = FileAppService.#get_api_service();
|
|
102
|
+
|
|
103
|
+
const token_wrap = await api.file_app.issue_access_token({
|
|
104
|
+
scopes : [ 'file:upload', 'file:read' ],
|
|
105
|
+
entity_type,
|
|
106
|
+
entity_id,
|
|
107
|
+
size_bytes,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const access_token = token_wrap?.response?.access_token;
|
|
111
|
+
|
|
112
|
+
if (!access_token)
|
|
113
|
+
{
|
|
114
|
+
throw new ApiException({ message: 'File App access token was not issued' });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const init_response = await axios.post(`${ file_app_url }/api/v1/uploads`, {
|
|
118
|
+
category,
|
|
119
|
+
mime_type,
|
|
120
|
+
size_bytes,
|
|
121
|
+
original_name,
|
|
122
|
+
entity_type,
|
|
123
|
+
entity_id,
|
|
124
|
+
}, {
|
|
125
|
+
headers: {
|
|
126
|
+
Authorization: `Bearer ${ access_token }`,
|
|
127
|
+
Accept : 'application/json',
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const init = init_response?.data?.api_data;
|
|
132
|
+
|
|
133
|
+
if (!init?.presigned_url || !init?.upload_id)
|
|
134
|
+
{
|
|
135
|
+
throw new ApiException({
|
|
136
|
+
message : 'File App upload init failed',
|
|
137
|
+
api_result: init_response?.data,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
await axios.put(init.presigned_url, file, {
|
|
142
|
+
headers: { 'Content-Type': mime_type },
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const confirm_response = await axios.post(
|
|
146
|
+
`${ file_app_url }/api/v1/uploads/${ encodeURIComponent(init.upload_id) }/confirm`,
|
|
147
|
+
{},
|
|
148
|
+
{
|
|
149
|
+
headers: {
|
|
150
|
+
Authorization: `Bearer ${ access_token }`,
|
|
151
|
+
Accept : 'application/json',
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
const confirmed = confirm_response?.data?.api_data;
|
|
157
|
+
|
|
158
|
+
if (!confirmed?.file_uuid)
|
|
159
|
+
{
|
|
160
|
+
throw new ApiException({
|
|
161
|
+
message : 'File App upload confirm failed',
|
|
162
|
+
api_result: confirm_response?.data,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const wire = {
|
|
167
|
+
...confirmed,
|
|
168
|
+
category : confirmed.category || category,
|
|
169
|
+
entity_type: confirmed.entity_type || entity_type,
|
|
170
|
+
entity_id : confirmed.entity_id ?? entity_id,
|
|
171
|
+
mime_type : confirmed.mime_type || mime_type,
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const typed = create_type_file_from_wire(wire);
|
|
175
|
+
|
|
176
|
+
if (!typed)
|
|
177
|
+
{
|
|
178
|
+
throw new ApiException({ message: 'File App upload: invalid wire after confirm' });
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return typed;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Refresh presigned URL via Main App bridge (lazy fallback when details wire expired).
|
|
187
|
+
*
|
|
188
|
+
* @param {string} file_uuid
|
|
189
|
+
* @param {'inline'|'attachment'} [disposition]
|
|
190
|
+
*
|
|
191
|
+
* @returns {Promise<object>}
|
|
192
|
+
*/
|
|
193
|
+
static async refresh_file_access_url(file_uuid, disposition = 'inline')
|
|
194
|
+
{
|
|
195
|
+
const api = FileAppService.#get_api_service();
|
|
196
|
+
const wrap = await api.file_app.file_access_url({ file_uuid, disposition });
|
|
197
|
+
const row = wrap?.response;
|
|
198
|
+
|
|
199
|
+
if (!row?.url)
|
|
200
|
+
{
|
|
201
|
+
throw new ApiException({ message: 'File access URL was not resolved' });
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (disposition === 'attachment')
|
|
205
|
+
{
|
|
206
|
+
return {
|
|
207
|
+
download_url : row.url,
|
|
208
|
+
download_url_expires_at : row.expires_at || false,
|
|
209
|
+
mime_type : row.mime_type || false,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
preview_url : row.url,
|
|
215
|
+
preview_url_expires_at : row.expires_at || false,
|
|
216
|
+
mime_type : row.mime_type || false,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Переводы для typeFile.
|
|
3
|
+
*/
|
|
4
|
+
export const typeFile_i18n = {
|
|
5
|
+
attribute_name_translate_matrix: {
|
|
6
|
+
en: {
|
|
7
|
+
file_uuid : 'File',
|
|
8
|
+
category : 'Category',
|
|
9
|
+
entity_type : 'Entity type',
|
|
10
|
+
entity_id : 'Entity ID',
|
|
11
|
+
mime_type : 'MIME type',
|
|
12
|
+
original_name : 'File name',
|
|
13
|
+
size_bytes : 'Size (bytes)',
|
|
14
|
+
preview_url : 'Preview URL',
|
|
15
|
+
preview_url_expires_at : 'Preview URL expires',
|
|
16
|
+
download_url : 'Download URL',
|
|
17
|
+
download_url_expires_at: 'Download URL expires',
|
|
18
|
+
created_at : 'Created at',
|
|
19
|
+
},
|
|
20
|
+
ru: {
|
|
21
|
+
file_uuid : 'Файл',
|
|
22
|
+
category : 'Категория',
|
|
23
|
+
entity_type : 'Тип сущности',
|
|
24
|
+
entity_id : 'ID сущности',
|
|
25
|
+
mime_type : 'MIME тип',
|
|
26
|
+
original_name : 'Имя файла',
|
|
27
|
+
size_bytes : 'Размер (байт)',
|
|
28
|
+
preview_url : 'URL превью',
|
|
29
|
+
preview_url_expires_at : 'Превью URL до',
|
|
30
|
+
download_url : 'URL скачивания',
|
|
31
|
+
download_url_expires_at : 'Скачивание URL до',
|
|
32
|
+
created_at : 'Создано',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
enum_value_translate_matrix: {
|
|
37
|
+
category: {
|
|
38
|
+
en: {
|
|
39
|
+
screenshot: 'Screenshot',
|
|
40
|
+
html : 'HTML',
|
|
41
|
+
postman : 'Postman',
|
|
42
|
+
video : 'Video',
|
|
43
|
+
json : 'JSON',
|
|
44
|
+
},
|
|
45
|
+
ru: {
|
|
46
|
+
screenshot: 'Скриншот',
|
|
47
|
+
html : 'HTML',
|
|
48
|
+
postman : 'Postman',
|
|
49
|
+
video : 'Видео',
|
|
50
|
+
json : 'JSON',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { familyGeneralElement } from "../core/0_familyGeneralElement.js";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Базовый family для File App сущностей.
|
|
6
|
+
*
|
|
7
|
+
* @see docs/File App Layer/README.MD
|
|
8
|
+
*
|
|
9
|
+
* @version v.1.0 (28/06/2026)
|
|
10
|
+
*/
|
|
11
|
+
export class familyFile extends familyGeneralElement
|
|
12
|
+
{
|
|
13
|
+
static get_category_values()
|
|
14
|
+
{
|
|
15
|
+
return [
|
|
16
|
+
'screenshot',
|
|
17
|
+
'html',
|
|
18
|
+
'postman',
|
|
19
|
+
'video',
|
|
20
|
+
'json',
|
|
21
|
+
];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { familyFile } from "./familyFile.js";
|
|
2
|
+
import { is_object } from "../../utils/utils.js";
|
|
3
|
+
import { typeFile_i18n } from "../element_translate/typeFile.i18n.js";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Базовый тип файла File App (wire + URL helpers).
|
|
8
|
+
*
|
|
9
|
+
* @version v.1.0 (28/06/2026)
|
|
10
|
+
*/
|
|
11
|
+
export class typeFile extends familyFile
|
|
12
|
+
{
|
|
13
|
+
static ENTITY_UTIL_EXECUTION = 'util_execution';
|
|
14
|
+
|
|
15
|
+
file_uuid;
|
|
16
|
+
category;
|
|
17
|
+
entity_type;
|
|
18
|
+
entity_id;
|
|
19
|
+
mime_type;
|
|
20
|
+
original_name;
|
|
21
|
+
size_bytes;
|
|
22
|
+
preview_url;
|
|
23
|
+
preview_url_expires_at;
|
|
24
|
+
download_url;
|
|
25
|
+
download_url_expires_at;
|
|
26
|
+
created_at;
|
|
27
|
+
|
|
28
|
+
primary_key = 'file_uuid';
|
|
29
|
+
api_key = 'file_app';
|
|
30
|
+
|
|
31
|
+
structure_scheme = {
|
|
32
|
+
file_uuid : 'string | max:64 | require',
|
|
33
|
+
category : 'string | enum | require',
|
|
34
|
+
entity_type : 'string | max:120 | optional',
|
|
35
|
+
entity_id : 'integer | optional',
|
|
36
|
+
mime_type : 'string | max:120 | optional',
|
|
37
|
+
original_name : 'string | max:255 | optional',
|
|
38
|
+
size_bytes : 'integer | optional',
|
|
39
|
+
preview_url : 'string | max:2048 | optional',
|
|
40
|
+
preview_url_expires_at : 'timestamp | optional',
|
|
41
|
+
download_url : 'string | max:2048 | optional',
|
|
42
|
+
download_url_expires_at : 'timestamp | optional',
|
|
43
|
+
created_at : 'timestamp | optional',
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
available_enum_values = {
|
|
47
|
+
category: familyFile.get_category_values(),
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
enum_value_translate_matrix = typeFile_i18n.enum_value_translate_matrix;
|
|
51
|
+
|
|
52
|
+
attribute_name_translate_matrix = typeFile_i18n.attribute_name_translate_matrix;
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @param {object|false|undefined} data
|
|
57
|
+
*/
|
|
58
|
+
constructor(data = false)
|
|
59
|
+
{
|
|
60
|
+
super();
|
|
61
|
+
|
|
62
|
+
if (data && is_object(data))
|
|
63
|
+
{
|
|
64
|
+
_.mapObject(data, (value, key) =>
|
|
65
|
+
{
|
|
66
|
+
if (this.hasOwnProperty(key))
|
|
67
|
+
{
|
|
68
|
+
this [ key ] = value;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @param {string|false|undefined} expires_at
|
|
79
|
+
*
|
|
80
|
+
* @returns {boolean}
|
|
81
|
+
*/
|
|
82
|
+
static is_url_expired(expires_at)
|
|
83
|
+
{
|
|
84
|
+
if (!expires_at)
|
|
85
|
+
{
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const expires_ms = Date.parse(expires_at);
|
|
90
|
+
|
|
91
|
+
if (Number.isNaN(expires_ms))
|
|
92
|
+
{
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return expires_ms <= Date.now();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @returns {string|false}
|
|
102
|
+
*/
|
|
103
|
+
get_preview_url()
|
|
104
|
+
{
|
|
105
|
+
if (!this.preview_url || typeFile.is_url_expired(this.preview_url_expires_at))
|
|
106
|
+
{
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return this.preview_url;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @returns {string|false}
|
|
116
|
+
*/
|
|
117
|
+
get_download_url()
|
|
118
|
+
{
|
|
119
|
+
if (!this.download_url || typeFile.is_url_expired(this.download_url_expires_at))
|
|
120
|
+
{
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return this.download_url;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @returns {boolean}
|
|
130
|
+
*/
|
|
131
|
+
needs_url_refresh()
|
|
132
|
+
{
|
|
133
|
+
return !this.get_preview_url();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { is_object } from "../../utils/utils.js";
|
|
2
|
+
import { typeFile } from "./typeFile.js";
|
|
3
|
+
import { typeFileScreenshot } from "./typeFileScreenshot.js";
|
|
4
|
+
import { typeFileHtml } from "./typeFileHtml.js";
|
|
5
|
+
import { typeFilePostman } from "./typeFilePostman.js";
|
|
6
|
+
import { typeFileVideo } from "./typeFileVideo.js";
|
|
7
|
+
import { typeFileJson } from "./typeFileJson.js";
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {object|false|undefined} data
|
|
12
|
+
*
|
|
13
|
+
* @returns {typeFile|false}
|
|
14
|
+
*/
|
|
15
|
+
export function create_type_file_from_wire(data)
|
|
16
|
+
{
|
|
17
|
+
if (!data || !is_object(data))
|
|
18
|
+
{
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const category = data.category;
|
|
23
|
+
|
|
24
|
+
switch (category)
|
|
25
|
+
{
|
|
26
|
+
case typeFileScreenshot.CATEGORY:
|
|
27
|
+
return new typeFileScreenshot(data);
|
|
28
|
+
case typeFileHtml.CATEGORY:
|
|
29
|
+
return new typeFileHtml(data);
|
|
30
|
+
case typeFilePostman.CATEGORY:
|
|
31
|
+
return new typeFilePostman(data);
|
|
32
|
+
case typeFileVideo.CATEGORY:
|
|
33
|
+
return new typeFileVideo(data);
|
|
34
|
+
case typeFileJson.CATEGORY:
|
|
35
|
+
return new typeFileJson(data);
|
|
36
|
+
default:
|
|
37
|
+
return new typeFile(data);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { typeFile } from "./typeFile.js";
|
|
2
|
+
import { typeFileScreenshot_i18n } from "../element_translate/typeFileScreenshot.i18n.js";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* File App — category screenshot.
|
|
7
|
+
*
|
|
8
|
+
* @version v.1.0 (28/06/2026)
|
|
9
|
+
*/
|
|
10
|
+
export class typeFileScreenshot extends typeFile
|
|
11
|
+
{
|
|
12
|
+
static CATEGORY = 'screenshot';
|
|
13
|
+
|
|
14
|
+
category = typeFileScreenshot.CATEGORY;
|
|
15
|
+
|
|
16
|
+
attribute_name_translate_matrix = typeFileScreenshot_i18n.attribute_name_translate_matrix;
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Upload browser File/Blob via FileAppService.
|
|
21
|
+
*
|
|
22
|
+
* @param {object} payload
|
|
23
|
+
* @param {File|Blob} payload.file
|
|
24
|
+
* @param {string} payload.entity_type
|
|
25
|
+
* @param {number} payload.entity_id
|
|
26
|
+
* @param {string} [payload.original_name]
|
|
27
|
+
*
|
|
28
|
+
* @returns {Promise<typeFileScreenshot>}
|
|
29
|
+
*/
|
|
30
|
+
async upload(payload)
|
|
31
|
+
{
|
|
32
|
+
const { FileAppService } = await import("../../services/FileAppService.js");
|
|
33
|
+
|
|
34
|
+
return FileAppService.upload_file({
|
|
35
|
+
...payload,
|
|
36
|
+
category: typeFileScreenshot.CATEGORY,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -6,6 +6,8 @@ import { typeUtilExecutionScreenshotMultiCapture } from "./typeUtilExecutionScre
|
|
|
6
6
|
import { typeUtilExecutionScreenshotMultiScreenshot } from "./typeUtilExecutionScreenshotMultiScreenshot.js";
|
|
7
7
|
import { typeUtilExecutionScreenshotMultiCaptureError } from "./typeUtilExecutionScreenshotMultiCaptureError.js";
|
|
8
8
|
import { typeUtilExecutionEvent } from "./typeUtilExecutionEvent.js";
|
|
9
|
+
import { typeFile } from "../family_files/typeFile.js";
|
|
10
|
+
import { create_type_file_from_wire } from "../family_files/typeFileFactory.js";
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
/**
|
|
@@ -114,7 +116,10 @@ export class typeUtilExecution extends familyUtil
|
|
|
114
116
|
* execution: typeUtilExecution,
|
|
115
117
|
* screenshot_multi: typeUtilExecutionScreenshotMulti|null,
|
|
116
118
|
* captures: typeUtilExecutionScreenshotMultiCapture[],
|
|
117
|
-
* screenshots:
|
|
119
|
+
* screenshots: Array<{
|
|
120
|
+
* shot: typeUtilExecutionScreenshotMultiScreenshot,
|
|
121
|
+
* file: import('../family_files/typeFile.js').typeFile|false,
|
|
122
|
+
* }>,
|
|
118
123
|
* capture_errors: typeUtilExecutionScreenshotMultiCaptureError[],
|
|
119
124
|
* events: typeUtilExecutionEvent[],
|
|
120
125
|
* }|false}
|
|
@@ -135,7 +140,10 @@ export class typeUtilExecution extends familyUtil
|
|
|
135
140
|
? data.captures.map((row) => new typeUtilExecutionScreenshotMultiCapture(row))
|
|
136
141
|
: [],
|
|
137
142
|
screenshots : Array.isArray(data.screenshots)
|
|
138
|
-
? data.screenshots.map((row) =>
|
|
143
|
+
? data.screenshots.map((row) => ({
|
|
144
|
+
shot: new typeUtilExecutionScreenshotMultiScreenshot(row),
|
|
145
|
+
file: row.file ? create_type_file_from_wire(row.file) : false,
|
|
146
|
+
}))
|
|
139
147
|
: [],
|
|
140
148
|
capture_errors : Array.isArray(data.capture_errors)
|
|
141
149
|
? data.capture_errors.map((row) => new typeUtilExecutionScreenshotMultiCaptureError(row))
|