@aj-archipelago/cortex 1.3.57 → 1.3.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/config.js +22 -0
- package/helper-apps/cortex-file-handler/INTERFACE.md +20 -9
- package/helper-apps/cortex-file-handler/package-lock.json +2 -2
- package/helper-apps/cortex-file-handler/package.json +1 -1
- package/helper-apps/cortex-file-handler/scripts/setup-azure-container.js +17 -17
- package/helper-apps/cortex-file-handler/scripts/setup-test-containers.js +35 -35
- package/helper-apps/cortex-file-handler/src/blobHandler.js +1010 -909
- package/helper-apps/cortex-file-handler/src/constants.js +98 -98
- package/helper-apps/cortex-file-handler/src/docHelper.js +27 -27
- package/helper-apps/cortex-file-handler/src/fileChunker.js +224 -214
- package/helper-apps/cortex-file-handler/src/helper.js +93 -93
- package/helper-apps/cortex-file-handler/src/index.js +584 -550
- package/helper-apps/cortex-file-handler/src/localFileHandler.js +86 -86
- package/helper-apps/cortex-file-handler/src/redis.js +186 -90
- package/helper-apps/cortex-file-handler/src/services/ConversionService.js +301 -273
- package/helper-apps/cortex-file-handler/src/services/FileConversionService.js +55 -55
- package/helper-apps/cortex-file-handler/src/services/storage/AzureStorageProvider.js +174 -154
- package/helper-apps/cortex-file-handler/src/services/storage/GCSStorageProvider.js +239 -223
- package/helper-apps/cortex-file-handler/src/services/storage/LocalStorageProvider.js +161 -159
- package/helper-apps/cortex-file-handler/src/services/storage/StorageFactory.js +73 -71
- package/helper-apps/cortex-file-handler/src/services/storage/StorageProvider.js +46 -45
- package/helper-apps/cortex-file-handler/src/services/storage/StorageService.js +256 -213
- package/helper-apps/cortex-file-handler/src/start.js +4 -1
- package/helper-apps/cortex-file-handler/src/utils/filenameUtils.js +59 -25
- package/helper-apps/cortex-file-handler/tests/FileConversionService.test.js +119 -116
- package/helper-apps/cortex-file-handler/tests/blobHandler.test.js +257 -257
- package/helper-apps/cortex-file-handler/tests/cleanup.test.js +676 -0
- package/helper-apps/cortex-file-handler/tests/conversionResilience.test.js +124 -124
- package/helper-apps/cortex-file-handler/tests/fileChunker.test.js +249 -208
- package/helper-apps/cortex-file-handler/tests/fileUpload.test.js +439 -380
- package/helper-apps/cortex-file-handler/tests/getOperations.test.js +299 -263
- package/helper-apps/cortex-file-handler/tests/postOperations.test.js +265 -239
- package/helper-apps/cortex-file-handler/tests/start.test.js +1230 -1201
- package/helper-apps/cortex-file-handler/tests/storage/AzureStorageProvider.test.js +110 -105
- package/helper-apps/cortex-file-handler/tests/storage/GCSStorageProvider.test.js +201 -175
- package/helper-apps/cortex-file-handler/tests/storage/LocalStorageProvider.test.js +128 -125
- package/helper-apps/cortex-file-handler/tests/storage/StorageFactory.test.js +78 -73
- package/helper-apps/cortex-file-handler/tests/storage/StorageService.test.js +99 -99
- package/helper-apps/cortex-file-handler/tests/testUtils.helper.js +74 -70
- package/package.json +1 -1
- package/pathways/translate_apptek.js +33 -0
- package/pathways/translate_subtitle.js +15 -8
- package/server/plugins/apptekTranslatePlugin.js +46 -91
- package/tests/apptekTranslatePlugin.test.js +0 -2
- package/tests/integration/apptekTranslatePlugin.integration.test.js +159 -93
- package/tests/translate_apptek.test.js +16 -0
|
@@ -1,182 +1,184 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import path from
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { ipAddress, port } from '../../start.js';
|
|
6
|
-
import { sanitizeFilename } from '../../utils/filenameUtils.js';
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { generateShortId } from "../../utils/filenameUtils.js";
|
|
4
|
+
import { ipAddress, port } from "../../start.js";
|
|
7
5
|
|
|
8
|
-
import { StorageProvider } from
|
|
6
|
+
import { StorageProvider } from "./StorageProvider.js";
|
|
9
7
|
|
|
10
8
|
export class LocalStorageProvider extends StorageProvider {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
this.publicFolder = publicFolder;
|
|
17
|
-
this.ensurePublicFolder();
|
|
9
|
+
constructor(publicFolder) {
|
|
10
|
+
super();
|
|
11
|
+
if (!publicFolder) {
|
|
12
|
+
throw new Error("Missing public folder path");
|
|
18
13
|
}
|
|
14
|
+
this.publicFolder = publicFolder;
|
|
15
|
+
this.ensurePublicFolder();
|
|
16
|
+
}
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
18
|
+
ensurePublicFolder() {
|
|
19
|
+
if (!fs.existsSync(this.publicFolder)) {
|
|
20
|
+
fs.mkdirSync(this.publicFolder, { recursive: true });
|
|
24
21
|
}
|
|
22
|
+
}
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Generate unique filename
|
|
34
|
-
let baseName = sanitizeFilename(path.basename(filePath));
|
|
35
|
-
// Local storage URLs shouldn't be double-encoded, so leave the name raw in the FS path,
|
|
36
|
-
// but use encodeURIComponent when building the external URL so browsers read it fine.
|
|
37
|
-
const encodedForUrl = encodeURIComponent(baseName);
|
|
38
|
-
const uniqueFileName = `${uuidv4()}_${encodedForUrl}`;
|
|
39
|
-
const destinationPath = path.join(requestFolder, uniqueFileName);
|
|
40
|
-
|
|
41
|
-
// Copy file to public folder
|
|
42
|
-
await fs.promises.copyFile(filePath, destinationPath);
|
|
43
|
-
|
|
44
|
-
// Generate full URL
|
|
45
|
-
const url = `http://${ipAddress}:${port}/files/${requestId}/${uniqueFileName}`;
|
|
46
|
-
|
|
47
|
-
return {
|
|
48
|
-
url,
|
|
49
|
-
blobName: path.join(requestId, uniqueFileName)
|
|
50
|
-
};
|
|
24
|
+
async uploadFile(context, filePath, requestId, hash = null, filename = null) {
|
|
25
|
+
// Create request folder if it doesn't exist
|
|
26
|
+
const requestFolder = path.join(this.publicFolder, requestId);
|
|
27
|
+
if (!fs.existsSync(requestFolder)) {
|
|
28
|
+
fs.mkdirSync(requestFolder, { recursive: true });
|
|
51
29
|
}
|
|
52
30
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
for (const file of files) {
|
|
62
|
-
const filePath = path.join(requestFolder, file);
|
|
63
|
-
await fs.promises.unlink(filePath);
|
|
64
|
-
// Return the full path relative to the public folder
|
|
65
|
-
result.push(path.join(requestId, file));
|
|
66
|
-
}
|
|
67
|
-
await fs.promises.rmdir(requestFolder);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return result;
|
|
31
|
+
// Use provided filename or generate LLM-friendly naming
|
|
32
|
+
let uniqueFileName;
|
|
33
|
+
if (filename) {
|
|
34
|
+
uniqueFileName = filename;
|
|
35
|
+
} else {
|
|
36
|
+
const fileExtension = path.extname(filePath);
|
|
37
|
+
const shortId = generateShortId();
|
|
38
|
+
uniqueFileName = `${shortId}${fileExtension}`;
|
|
71
39
|
}
|
|
72
40
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
41
|
+
const destinationPath = path.join(requestFolder, uniqueFileName);
|
|
42
|
+
|
|
43
|
+
// Copy file to public folder
|
|
44
|
+
await fs.promises.copyFile(filePath, destinationPath);
|
|
45
|
+
|
|
46
|
+
// Generate full URL
|
|
47
|
+
const url = `http://${ipAddress}:${port}/files/${requestId}/${uniqueFileName}`;
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
url,
|
|
51
|
+
blobName: path.join(requestId, uniqueFileName),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async deleteFiles(requestId) {
|
|
56
|
+
if (!requestId) throw new Error("Missing requestId parameter");
|
|
57
|
+
|
|
58
|
+
const requestFolder = path.join(this.publicFolder, requestId);
|
|
59
|
+
const result = [];
|
|
60
|
+
|
|
61
|
+
if (fs.existsSync(requestFolder)) {
|
|
62
|
+
const files = await fs.promises.readdir(requestFolder);
|
|
63
|
+
for (const file of files) {
|
|
64
|
+
const filePath = path.join(requestFolder, file);
|
|
65
|
+
await fs.promises.unlink(filePath);
|
|
66
|
+
// Return the full path relative to the public folder
|
|
67
|
+
result.push(path.join(requestId, file));
|
|
68
|
+
}
|
|
69
|
+
await fs.promises.rmdir(requestFolder);
|
|
94
70
|
}
|
|
95
71
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Check if file exists and is accessible
|
|
108
|
-
try {
|
|
109
|
-
await fs.promises.access(filePath, fs.constants.R_OK);
|
|
110
|
-
return true;
|
|
111
|
-
} catch (error) {
|
|
112
|
-
return false;
|
|
113
|
-
}
|
|
114
|
-
} catch (error) {
|
|
115
|
-
console.error('Error checking if file exists:', error);
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async deleteFile(url) {
|
|
76
|
+
if (!url) throw new Error("Missing URL parameter");
|
|
77
|
+
|
|
78
|
+
const filePath = this.urlToFilePath(url);
|
|
79
|
+
if (!filePath) {
|
|
80
|
+
throw new Error("Invalid URL");
|
|
118
81
|
}
|
|
119
82
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
await fs.promises.
|
|
83
|
+
if (fs.existsSync(filePath)) {
|
|
84
|
+
await fs.promises.unlink(filePath);
|
|
85
|
+
// Try to remove the parent directory if it's empty
|
|
86
|
+
const parentDir = path.dirname(filePath);
|
|
87
|
+
try {
|
|
88
|
+
await fs.promises.rmdir(parentDir);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
// Ignore error if directory is not empty
|
|
91
|
+
}
|
|
92
|
+
return path.relative(this.publicFolder, filePath);
|
|
126
93
|
}
|
|
127
94
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async fileExists(url) {
|
|
99
|
+
try {
|
|
100
|
+
if (!url) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const filePath = this.urlToFilePath(url);
|
|
105
|
+
if (!filePath) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Check if file exists and is accessible
|
|
110
|
+
try {
|
|
111
|
+
await fs.promises.access(filePath, fs.constants.R_OK);
|
|
112
|
+
return true;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
} catch (error) {
|
|
117
|
+
console.error("Error checking if file exists:", error);
|
|
118
|
+
return false;
|
|
145
119
|
}
|
|
120
|
+
}
|
|
146
121
|
|
|
147
|
-
|
|
148
|
-
|
|
122
|
+
async downloadFile(url, destinationPath) {
|
|
123
|
+
const sourcePath = this.urlToFilePath(url);
|
|
124
|
+
if (!fs.existsSync(sourcePath)) {
|
|
125
|
+
throw new Error("File not found");
|
|
149
126
|
}
|
|
127
|
+
await fs.promises.copyFile(sourcePath, destinationPath);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async cleanup(urls) {
|
|
131
|
+
if (!urls || !urls.length) return;
|
|
150
132
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Remove leading slash if present
|
|
161
|
-
if (urlPath.startsWith('/')) {
|
|
162
|
-
urlPath = urlPath.substring(1);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Split into parts and decode each part
|
|
166
|
-
const parts = urlPath.split('/');
|
|
167
|
-
const decodedParts = parts.map(part => decodeURIComponent(part));
|
|
168
|
-
|
|
169
|
-
// If the URL path starts with 'files', remove that segment because
|
|
170
|
-
// our publicFolder already represents the root of '/files'.
|
|
171
|
-
if (decodedParts.length && decodedParts[0] === 'files') {
|
|
172
|
-
decodedParts.shift();
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Join with the public folder path
|
|
176
|
-
return path.join(this.publicFolder, ...decodedParts);
|
|
177
|
-
} catch (error) {
|
|
178
|
-
console.error('Error converting URL to file path:', error);
|
|
179
|
-
return null;
|
|
133
|
+
const result = [];
|
|
134
|
+
for (const url of urls) {
|
|
135
|
+
try {
|
|
136
|
+
const filePath = this.urlToFilePath(url);
|
|
137
|
+
if (fs.existsSync(filePath)) {
|
|
138
|
+
await fs.promises.unlink(filePath);
|
|
139
|
+
result.push(path.relative(this.publicFolder, filePath));
|
|
180
140
|
}
|
|
141
|
+
} catch (error) {
|
|
142
|
+
console.error(`Error cleaning up file ${url}:`, error);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
isEncoded(str) {
|
|
150
|
+
return /%[0-9A-Fa-f]{2}/.test(str);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
urlToFilePath(url) {
|
|
154
|
+
try {
|
|
155
|
+
// If it's a full URL, extract the pathname
|
|
156
|
+
let urlPath = url;
|
|
157
|
+
if (url.startsWith("http://") || url.startsWith("https://")) {
|
|
158
|
+
const urlObj = new URL(url);
|
|
159
|
+
urlPath = urlObj.pathname;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Remove leading slash if present
|
|
163
|
+
if (urlPath.startsWith("/")) {
|
|
164
|
+
urlPath = urlPath.substring(1);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Split into parts and decode each part
|
|
168
|
+
const parts = urlPath.split("/");
|
|
169
|
+
const decodedParts = parts.map((part) => decodeURIComponent(part));
|
|
170
|
+
|
|
171
|
+
// If the URL path starts with 'files', remove that segment because
|
|
172
|
+
// our publicFolder already represents the root of '/files'.
|
|
173
|
+
if (decodedParts.length && decodedParts[0] === "files") {
|
|
174
|
+
decodedParts.shift();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Join with the public folder path
|
|
178
|
+
return path.join(this.publicFolder, ...decodedParts);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.error("Error converting URL to file path:", error);
|
|
181
|
+
return null;
|
|
181
182
|
}
|
|
182
|
-
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -1,86 +1,88 @@
|
|
|
1
|
-
import { AzureStorageProvider } from
|
|
2
|
-
import { GCSStorageProvider } from
|
|
3
|
-
import { LocalStorageProvider } from
|
|
4
|
-
import path from
|
|
5
|
-
import { fileURLToPath } from
|
|
1
|
+
import { AzureStorageProvider } from "./AzureStorageProvider.js";
|
|
2
|
+
import { GCSStorageProvider } from "./GCSStorageProvider.js";
|
|
3
|
+
import { LocalStorageProvider } from "./LocalStorageProvider.js";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
6
|
|
|
7
7
|
export class StorageFactory {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
constructor() {
|
|
9
|
+
this.providers = new Map();
|
|
10
|
+
}
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
return this.getLocalProvider();
|
|
12
|
+
getPrimaryProvider() {
|
|
13
|
+
if (process.env.AZURE_STORAGE_CONNECTION_STRING) {
|
|
14
|
+
return this.getAzureProvider();
|
|
17
15
|
}
|
|
16
|
+
return this.getLocalProvider();
|
|
17
|
+
}
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
return this.providers.get(key);
|
|
19
|
+
getAzureProvider() {
|
|
20
|
+
const key = "azure";
|
|
21
|
+
if (!this.providers.has(key)) {
|
|
22
|
+
const provider = new AzureStorageProvider(
|
|
23
|
+
process.env.AZURE_STORAGE_CONNECTION_STRING,
|
|
24
|
+
process.env.AZURE_STORAGE_CONTAINER_NAME || "whispertempfiles",
|
|
25
|
+
);
|
|
26
|
+
this.providers.set(key, provider);
|
|
29
27
|
}
|
|
28
|
+
return this.providers.get(key);
|
|
29
|
+
}
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
return this.providers.get(key);
|
|
31
|
+
getGCSProvider() {
|
|
32
|
+
const key = "gcs";
|
|
33
|
+
if (!this.providers.has(key)) {
|
|
34
|
+
const credentials = this.parseGCSCredentials();
|
|
35
|
+
if (!credentials) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const provider = new GCSStorageProvider(
|
|
39
|
+
credentials,
|
|
40
|
+
process.env.GCS_BUCKETNAME || "cortextempfiles",
|
|
41
|
+
);
|
|
42
|
+
this.providers.set(key, provider);
|
|
45
43
|
}
|
|
44
|
+
return this.providers.get(key);
|
|
45
|
+
}
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
return this.providers.get(key);
|
|
47
|
+
getLocalProvider() {
|
|
48
|
+
const key = "local";
|
|
49
|
+
if (!this.providers.has(key)) {
|
|
50
|
+
let folder = process.env.PUBLIC_FOLDER;
|
|
51
|
+
if (!folder) {
|
|
52
|
+
// Compute src/files relative to current directory
|
|
53
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
54
|
+
folder = path.join(__dirname, "..", "..", "files");
|
|
55
|
+
}
|
|
56
|
+
const provider = new LocalStorageProvider(folder);
|
|
57
|
+
this.providers.set(key, provider);
|
|
60
58
|
}
|
|
59
|
+
return this.providers.get(key);
|
|
60
|
+
}
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
parseGCSCredentials() {
|
|
63
|
+
const key =
|
|
64
|
+
process.env.GCP_SERVICE_ACCOUNT_KEY_BASE64 ||
|
|
65
|
+
process.env.GCP_SERVICE_ACCOUNT_KEY;
|
|
66
|
+
if (!key) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
70
|
+
try {
|
|
71
|
+
if (this.isBase64(key)) {
|
|
72
|
+
return JSON.parse(Buffer.from(key, "base64").toString());
|
|
73
|
+
}
|
|
74
|
+
return JSON.parse(key);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error("Error parsing GCS credentials:", error);
|
|
77
|
+
return null;
|
|
77
78
|
}
|
|
79
|
+
}
|
|
78
80
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
81
|
+
isBase64(str) {
|
|
82
|
+
try {
|
|
83
|
+
return btoa(atob(str)) === str;
|
|
84
|
+
} catch (err) {
|
|
85
|
+
return false;
|
|
85
86
|
}
|
|
86
|
-
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -2,52 +2,53 @@
|
|
|
2
2
|
* Base interface for storage providers
|
|
3
3
|
*/
|
|
4
4
|
export class StorageProvider {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Upload a file to storage
|
|
7
|
+
* @param {Object} context - The context object
|
|
8
|
+
* @param {string} filePath - Path to the file to upload
|
|
9
|
+
* @param {string} requestId - Unique identifier for the request
|
|
10
|
+
* @param {string} [hash] - Optional hash of the file
|
|
11
|
+
* @param {string} [filename] - Optional filename to use (if not provided, provider will generate one)
|
|
12
|
+
* @returns {Promise<{url: string, blobName: string}>} The URL and blob name of the uploaded file
|
|
13
|
+
*/
|
|
14
|
+
async uploadFile(context, filePath, requestId, hash = null, filename = null) {
|
|
15
|
+
throw new Error("Method not implemented");
|
|
16
|
+
}
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Delete files associated with a request ID
|
|
20
|
+
* @param {string} requestId - The request ID to delete files for
|
|
21
|
+
* @returns {Promise<string[]>} Array of deleted file URLs
|
|
22
|
+
*/
|
|
23
|
+
async deleteFiles(requestId) {
|
|
24
|
+
throw new Error("Method not implemented");
|
|
25
|
+
}
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Check if a file exists at the given URL
|
|
29
|
+
* @param {string} url - The URL to check
|
|
30
|
+
* @returns {Promise<boolean>} Whether the file exists
|
|
31
|
+
*/
|
|
32
|
+
async fileExists(url) {
|
|
33
|
+
throw new Error("Method not implemented");
|
|
34
|
+
}
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Download a file from storage
|
|
38
|
+
* @param {string} url - The URL of the file to download
|
|
39
|
+
* @param {string} destinationPath - Where to save the downloaded file
|
|
40
|
+
* @returns {Promise<void>}
|
|
41
|
+
*/
|
|
42
|
+
async downloadFile(url, destinationPath) {
|
|
43
|
+
throw new Error("Method not implemented");
|
|
44
|
+
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
46
|
+
/**
|
|
47
|
+
* Clean up files by their URLs
|
|
48
|
+
* @param {string[]} urls - Array of URLs to clean up
|
|
49
|
+
* @returns {Promise<void>}
|
|
50
|
+
*/
|
|
51
|
+
async cleanup(urls) {
|
|
52
|
+
throw new Error("Method not implemented");
|
|
53
|
+
}
|
|
54
|
+
}
|