@blocklet/uploader-server 0.1.49

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.
@@ -0,0 +1,107 @@
1
+ const { existsSync } = require("fs");
2
+ const { join } = require("path");
3
+ const config = require("@blocklet/sdk/lib/config");
4
+ const { getResources } = require("@blocklet/sdk/lib/component");
5
+ const logger = console;
6
+ const ImgResourceType = "imgpack";
7
+ const ImageBinDid = "z8ia1mAXo8ZE7ytGF36L5uBf9kD2kenhqFGp9";
8
+ let skipRunningCheck = false;
9
+ let mediaKitInfo = null;
10
+ let resourceTypes = [
11
+ {
12
+ type: ImgResourceType,
13
+ did: ImageBinDid,
14
+ folder: ""
15
+ // default is root, can be set to 'public' or 'assets' or any other folder
16
+ }
17
+ ];
18
+ let canUseResources = [];
19
+ export const mappingResource = async () => {
20
+ mediaKitInfo = config.components.find((item) => item.did === ImageBinDid);
21
+ if (mediaKitInfo) {
22
+ mediaKitInfo.uploadsDir = config.env.dataDir.replace(/\/[^/]*$/, "/image-bin/uploads");
23
+ }
24
+ try {
25
+ const resources = getResources({
26
+ types: resourceTypes,
27
+ skipRunningCheck
28
+ });
29
+ canUseResources = resources.map((resource) => {
30
+ const originDir = resource.path;
31
+ const resourceType = resourceTypes.find(({ type }) => originDir.endsWith(type));
32
+ if (!existsSync(originDir) || !resourceType) {
33
+ return false;
34
+ }
35
+ const { folder = "" } = resourceType;
36
+ return { originDir, dir: join(originDir, folder || ""), blockletInfo: resource };
37
+ }).filter(Boolean);
38
+ logger.info(
39
+ "Mapping can use resources count: ",
40
+ canUseResources.length
41
+ // canUseResources
42
+ );
43
+ return canUseResources;
44
+ } catch (error) {
45
+ console.error(error);
46
+ }
47
+ return false;
48
+ };
49
+ const { events, Events } = config;
50
+ events.on(Events.componentAdded, () => mappingResource());
51
+ events.on(Events.componentRemoved, () => mappingResource());
52
+ events.on(Events.componentStarted, () => mappingResource());
53
+ events.on(Events.componentStopped, () => mappingResource());
54
+ events.on(Events.componentUpdated, () => mappingResource());
55
+ export const initStaticResourceMiddleware = ({
56
+ options,
57
+ resourceTypes: _resourceTypes = resourceTypes,
58
+ express,
59
+ skipRunningCheck: _skipRunningCheck
60
+ } = {}) => {
61
+ skipRunningCheck = !!_skipRunningCheck;
62
+ if (_resourceTypes?.length > 0) {
63
+ resourceTypes = _resourceTypes.map((item) => {
64
+ if (typeof item === "string") {
65
+ return {
66
+ type: item,
67
+ did: ImageBinDid,
68
+ // not set did, default is ImageBinDid
69
+ folder: ""
70
+ // not set folder, default is root
71
+ };
72
+ }
73
+ return item;
74
+ });
75
+ }
76
+ mappingResource();
77
+ return (req, res, next) => {
78
+ const urlPath = new URL(`http://localhost${req.url}`).pathname;
79
+ const matchCanUseResourceItem = canUseResources.find((item) => existsSync(join(item.dir, urlPath)));
80
+ if (matchCanUseResourceItem) {
81
+ express.static(matchCanUseResourceItem.dir, {
82
+ maxAge: "365d",
83
+ immutable: true,
84
+ index: false,
85
+ // fallthrough: false,
86
+ ...options
87
+ })(req, res, next);
88
+ } else {
89
+ res.status(404).end();
90
+ }
91
+ };
92
+ };
93
+ export const getCanUseResources = () => canUseResources;
94
+ export const initProxyToMediaKitUploadsMiddleware = ({ options, express } = {}) => {
95
+ return (req, res, next) => {
96
+ if (!mediaKitInfo?.uploadsDir) {
97
+ return next();
98
+ }
99
+ return express.static(mediaKitInfo.uploadsDir, {
100
+ maxAge: "365d",
101
+ immutable: true,
102
+ index: false,
103
+ // fallthrough: false,
104
+ ...options
105
+ })(req, res, next);
106
+ };
107
+ };
@@ -0,0 +1,3 @@
1
+ export * from './middlewares/local-storage';
2
+ export * from './middlewares/companion';
3
+ export * from './middlewares/static-resource';
@@ -0,0 +1,3 @@
1
+ export * from "./middlewares/local-storage.js";
2
+ export * from "./middlewares/companion.js";
3
+ export * from "./middlewares/static-resource.js";
package/es/types.d.ts ADDED
File without changes
package/es/types.js ADDED
File without changes
package/es/utils.d.ts ADDED
File without changes
package/es/utils.js ADDED
File without changes
package/lib/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './middlewares';
package/lib/index.js ADDED
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ var _middlewares = require("./middlewares");
7
+ Object.keys(_middlewares).forEach(function (key) {
8
+ if (key === "default" || key === "__esModule") return;
9
+ if (key in exports && exports[key] === _middlewares[key]) return;
10
+ Object.defineProperty(exports, key, {
11
+ enumerable: true,
12
+ get: function () {
13
+ return _middlewares[key];
14
+ }
15
+ });
16
+ });
@@ -0,0 +1,6 @@
1
+ export declare function initCompanion({ path, express, providerOptions, ...restProps }: {
2
+ path: string;
3
+ express: Function;
4
+ providerOptions?: Object;
5
+ }): any;
6
+ export declare function proxyImageDownload(req: any, res: any, next?: Function): Promise<void>;
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.initCompanion = initCompanion;
7
+ exports.proxyImageDownload = proxyImageDownload;
8
+ const companion = require("@uppy/companion");
9
+ const bodyParser = require("body-parser");
10
+ const session = require("express-session");
11
+ const axios = require("axios");
12
+ const crypto = require("crypto");
13
+ const secret = crypto.randomBytes(32).toString("hex");
14
+ function initCompanion({
15
+ path,
16
+ express,
17
+ providerOptions,
18
+ ...restProps
19
+ }) {
20
+ const app = express();
21
+ app.use(bodyParser.json());
22
+ app.use(session({
23
+ secret
24
+ }));
25
+ app.use("/proxy", proxyImageDownload);
26
+ let dynamicProviderOptions = providerOptions;
27
+ const companionOptions = {
28
+ secret,
29
+ providerOptions,
30
+ // unused
31
+ server: {
32
+ protocol: "https",
33
+ host: "UNUSED_HOST",
34
+ // unused
35
+ path: "UNUSED_PATH"
36
+ // unused
37
+ },
38
+ filePath: path,
39
+ streamingUpload: true,
40
+ metrics: false,
41
+ ...restProps
42
+ };
43
+ const newCompanion = companion.app(companionOptions);
44
+ const {
45
+ app: companionApp
46
+ } = newCompanion;
47
+ app.all("*", (req, res, next) => {
48
+ let hackerCompanion = {};
49
+ Object.defineProperty(req, "companion", {
50
+ get() {
51
+ return hackerCompanion;
52
+ },
53
+ set(value) {
54
+ hackerCompanion = value;
55
+ hackerCompanion.options.providerOptions = dynamicProviderOptions;
56
+ }
57
+ });
58
+ next();
59
+ }, companionApp);
60
+ newCompanion.handle = app;
61
+ newCompanion.setProviderOptions = options => {
62
+ dynamicProviderOptions = options;
63
+ };
64
+ return newCompanion;
65
+ }
66
+ async function proxyImageDownload(req, res, next) {
67
+ let {
68
+ url,
69
+ responseType = "stream"
70
+ } = {
71
+ ...req.query,
72
+ ...req.body
73
+ };
74
+ if (url) {
75
+ url = encodeURI(url);
76
+ try {
77
+ const {
78
+ headers,
79
+ data,
80
+ status
81
+ } = await axios.get(url, {
82
+ responseType
83
+ });
84
+ if (data && status >= 200 && status < 302) {
85
+ res.setHeader("Content-Type", headers["content-type"]);
86
+ try {} catch (error) {}
87
+ if (responseType === "stream") {
88
+ data.pipe(res);
89
+ } else if (responseType === "arraybuffer") {
90
+ res.end(data?.toString?.("binary"), "binary");
91
+ } else {
92
+ res.send(data);
93
+ }
94
+ } else {
95
+ throw new Error("download image error");
96
+ }
97
+ } catch (err) {
98
+ console.error("Proxy url failed: ", err);
99
+ res.status(500).send("Proxy url failed");
100
+ }
101
+ } else {
102
+ res.status(500).send('Parameter "url" is required');
103
+ }
104
+ }
@@ -0,0 +1,18 @@
1
+ import { type ServerOptions } from '@tus/server';
2
+ export declare function initLocalStorageServer({ path: _path, onUploadFinish: _onUploadFinish, onUploadCreate: _onUploadCreate, express, expiredUploadTime, // default 3 days expire
3
+ ...restProps }: ServerOptions & {
4
+ path: string;
5
+ onUploadFinish?: Function;
6
+ onUploadCreate?: Function;
7
+ express: Function;
8
+ expiredUploadTime?: Number;
9
+ }): any;
10
+ export declare const getFileName: (req: any) => any;
11
+ export declare function getFileNameParam(req: any, res: any, { isRequired }?: {
12
+ isRequired: boolean;
13
+ }): any;
14
+ export declare function getLocalStorageFile({ server }: any): (req: any, res: any, next: any) => Promise<void>;
15
+ export declare function setHeaders(req: any, res: any, next?: Function): void;
16
+ export declare function fileExistBeforeUpload(req: any, res: any, next?: Function): Promise<void>;
17
+ export declare function getMetaDataByFilePath(filePath: string): Promise<any>;
18
+ export declare function joinUrl(...args: string[]): any;
@@ -0,0 +1,423 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.fileExistBeforeUpload = fileExistBeforeUpload;
7
+ exports.getFileName = void 0;
8
+ exports.getFileNameParam = getFileNameParam;
9
+ exports.getLocalStorageFile = getLocalStorageFile;
10
+ exports.getMetaDataByFilePath = getMetaDataByFilePath;
11
+ exports.initLocalStorageServer = initLocalStorageServer;
12
+ exports.joinUrl = joinUrl;
13
+ exports.setHeaders = setHeaders;
14
+ const {
15
+ Server,
16
+ EVENTS
17
+ } = require("@tus/server");
18
+ const {
19
+ FileStore
20
+ } = require("@tus/file-store");
21
+ const cron = require("@abtnode/cron");
22
+ const fs = require("fs").promises;
23
+ const path = require("path");
24
+ const crypto = require("crypto");
25
+ const mime = require("mime-types");
26
+ const joinUrlLib = require("url-join");
27
+ const {
28
+ default: queue
29
+ } = require("p-queue");
30
+ const validFilePathInDirPath = (dirPath, filePath) => {
31
+ const fileName = path.basename(filePath);
32
+ if (!filePath.startsWith(dirPath) || path.join(dirPath, fileName) !== filePath) {
33
+ console.error("Invalid file path: ", filePath);
34
+ throw new Error("Invalid file path");
35
+ }
36
+ return true;
37
+ };
38
+ function initLocalStorageServer({
39
+ path: _path,
40
+ onUploadFinish: _onUploadFinish,
41
+ onUploadCreate: _onUploadCreate,
42
+ express,
43
+ expiredUploadTime = 1e3 * 60 * 60 * 24 * 3,
44
+ // default 3 days expire
45
+ ...restProps
46
+ }) {
47
+ const app = express();
48
+ const configstore = new RewriteFileConfigstore(_path);
49
+ const datastore = new RewriteFileStore({
50
+ directory: _path,
51
+ expirationPeriodInMilliseconds: expiredUploadTime,
52
+ configstore
53
+ });
54
+ const formatMetadata = uploadMetadata => {
55
+ const cloneUploadMetadata = {
56
+ ...uploadMetadata
57
+ };
58
+ if (cloneUploadMetadata.metadata?.name?.indexOf("/") > -1 && cloneUploadMetadata.metadata?.relativePath?.indexOf("/") > -1) {
59
+ cloneUploadMetadata.metadata.name = cloneUploadMetadata.metadata.name.split("/").pop();
60
+ cloneUploadMetadata.metadata.filename = cloneUploadMetadata.metadata.name;
61
+ }
62
+ if (cloneUploadMetadata.id && !cloneUploadMetadata.runtime) {
63
+ const {
64
+ id,
65
+ metadata,
66
+ size
67
+ } = cloneUploadMetadata;
68
+ cloneUploadMetadata.runtime = {
69
+ relativePath: metadata?.relativePath,
70
+ absolutePath: path.join(_path, id),
71
+ size,
72
+ hashFileName: id,
73
+ originFileName: metadata?.filename,
74
+ type: metadata?.type,
75
+ fileType: metadata?.filetype
76
+ };
77
+ }
78
+ return cloneUploadMetadata;
79
+ };
80
+ const rewriteMetaDataFile = async uploadMetadata => {
81
+ uploadMetadata = formatMetadata(uploadMetadata);
82
+ const {
83
+ id
84
+ } = uploadMetadata;
85
+ if (!id) {
86
+ return;
87
+ }
88
+ const oldMetadata = formatMetadata(await configstore.get(id));
89
+ if (JSON.stringify(oldMetadata) !== JSON.stringify(uploadMetadata)) {
90
+ await configstore.set(id, uploadMetadata);
91
+ }
92
+ };
93
+ const onUploadCreate = async (req, res, uploadMetadata) => {
94
+ uploadMetadata = formatMetadata(uploadMetadata);
95
+ await rewriteMetaDataFile(uploadMetadata);
96
+ if (uploadMetadata.offset === 0 && uploadMetadata.size === 0) {
97
+ res.status(200);
98
+ res.setHeader("Location", joinUrl(req.headers["x-uploader-base-url"], uploadMetadata.id));
99
+ res.setHeader("Upload-Offset", 0);
100
+ res.setHeader("Upload-Length", 0);
101
+ res.setHeader("x-uploader-file-exist", true);
102
+ }
103
+ if (_onUploadCreate) {
104
+ const result = await _onUploadCreate(req, res, uploadMetadata);
105
+ return result;
106
+ }
107
+ return res;
108
+ };
109
+ const onUploadFinish = async (req, res, uploadMetadata) => {
110
+ res.setHeader("x-uploader-file-exist", true);
111
+ uploadMetadata = formatMetadata(uploadMetadata);
112
+ await rewriteMetaDataFile(uploadMetadata);
113
+ if (_onUploadFinish) {
114
+ try {
115
+ const result = await _onUploadFinish(req, res, uploadMetadata);
116
+ return result;
117
+ } catch (err) {
118
+ console.error("@blocklet/uploader: onUploadFinish error: ", err);
119
+ newServer.delete(uploadMetadata.id);
120
+ res.setHeader("x-uploader-file-exist", false);
121
+ throw err;
122
+ }
123
+ }
124
+ return res;
125
+ };
126
+ const newServer = new Server({
127
+ path: "/",
128
+ // UNUSED
129
+ relativeLocation: true,
130
+ // respectForwardedHeaders: true,
131
+ namingFunction: req => {
132
+ const fileName = getFileName(req);
133
+ const filePath = path.join(_path, fileName);
134
+ validFilePathInDirPath(_path, filePath);
135
+ return fileName;
136
+ },
137
+ datastore,
138
+ onUploadFinish: async (req, res, uploadMetadata) => {
139
+ uploadMetadata = formatMetadata(uploadMetadata);
140
+ const result = await onUploadFinish(req, res, uploadMetadata);
141
+ if (result && !result.send) {
142
+ const body = typeof result === "string" ? result : JSON.stringify(result);
143
+ throw {
144
+ body,
145
+ status_code: 200
146
+ };
147
+ } else {
148
+ return result;
149
+ }
150
+ },
151
+ onUploadCreate,
152
+ ...restProps
153
+ });
154
+ app.use((req, res, next) => {
155
+ req.uploaderProps = {
156
+ server: newServer,
157
+ onUploadFinish,
158
+ onUploadCreate
159
+ };
160
+ next();
161
+ });
162
+ cron.init({
163
+ context: {},
164
+ jobs: [{
165
+ name: "auto-cleanup-expired-uploads",
166
+ time: "0 0 * * * *",
167
+ // each hour
168
+ fn: () => {
169
+ try {
170
+ newServer.cleanUpExpiredUploads().then(count => {
171
+ console.info(`@blocklet/uploader: cleanup expired uploads done: ${count}`);
172
+ }).catch(err => {
173
+ console.error(`@blocklet/uploader: cleanup expired uploads error`, err);
174
+ });
175
+ } catch (err) {
176
+ console.error(`@blocklet/uploader: cleanup expired uploads error`, err);
177
+ }
178
+ },
179
+ options: {
180
+ runOnInit: false
181
+ }
182
+ }],
183
+ onError: err => {
184
+ console.error("@blocklet/uploader: cleanup job failed", err);
185
+ }
186
+ });
187
+ newServer.delete = async key => {
188
+ try {
189
+ await configstore.delete(key);
190
+ await configstore.delete(key, false);
191
+ } catch (err) {
192
+ console.error("@blocklet/uploader: delete error: ", err);
193
+ }
194
+ };
195
+ newServer.on(EVENTS.POST_RECEIVE, async (req, res, uploadMetadata) => {
196
+ uploadMetadata = formatMetadata(uploadMetadata);
197
+ await rewriteMetaDataFile(uploadMetadata);
198
+ });
199
+ app.all("*", setHeaders, fileExistBeforeUpload, newServer.handle.bind(newServer));
200
+ newServer.handle = app;
201
+ return newServer;
202
+ }
203
+ const getFileName = req => {
204
+ const ext = req.headers["x-uploader-file-ext"];
205
+ const randomName = `${crypto.randomBytes(16).toString("hex")}${ext ? `.${ext}` : ""}`;
206
+ return req.headers["x-uploader-file-name"] || randomName;
207
+ };
208
+ exports.getFileName = getFileName;
209
+ function getFileNameParam(req, res, {
210
+ isRequired = true
211
+ } = {}) {
212
+ let {
213
+ fileName
214
+ } = req.params;
215
+ if (!fileName) {
216
+ fileName = req.originalUrl.replace(req.baseUrl, "");
217
+ }
218
+ if (!fileName && isRequired) {
219
+ res.status(400).json({
220
+ error: 'Parameter "fileName" is required'
221
+ });
222
+ return;
223
+ }
224
+ return fileName;
225
+ }
226
+ function getLocalStorageFile({
227
+ server
228
+ }) {
229
+ return async (req, res, next) => {
230
+ const fileName = getFileNameParam(req, res);
231
+ const filePath = path.join(server.datastore.directory, fileName);
232
+ validFilePathInDirPath(server.datastore.directory, filePath);
233
+ const fileExists = await fs.stat(filePath).catch(() => false);
234
+ if (!fileExists) {
235
+ res.status(404).json({
236
+ error: "file not found"
237
+ });
238
+ return;
239
+ }
240
+ setHeaders(req, res);
241
+ const file = await fs.readFile(filePath);
242
+ res.send(file);
243
+ next?.();
244
+ };
245
+ }
246
+ function setHeaders(req, res, next) {
247
+ let {
248
+ method
249
+ } = req;
250
+ method = method.toUpperCase();
251
+ const fileName = getFileNameParam(req, res, {
252
+ isRequired: false
253
+ });
254
+ if (req.headers["x-uploader-endpoint-url"]) {
255
+ const query = new URL(req.headers["x-uploader-endpoint-url"]).searchParams;
256
+ req.query = {
257
+ ...Object.fromEntries(query),
258
+ // query params convert to object
259
+ ...req.query
260
+ };
261
+ }
262
+ if (method === "POST" && req.headers["x-uploader-base-url"]) {
263
+ req.baseUrl = req.headers["x-uploader-base-url"];
264
+ }
265
+ if (method === "GET" && fileName) {
266
+ const contentType = mime.lookup(fileName);
267
+ if (contentType) {
268
+ res.setHeader("Content-Type", contentType);
269
+ }
270
+ }
271
+ next?.();
272
+ }
273
+ async function fileExistBeforeUpload(req, res, next) {
274
+ let {
275
+ method,
276
+ uploaderProps
277
+ } = req;
278
+ method = method.toUpperCase();
279
+ if (["PATCH", "POST"].includes(method)) {
280
+ const _path = uploaderProps.server.datastore.directory;
281
+ const fileName = getFileName(req);
282
+ const filePath = path.join(_path, fileName);
283
+ validFilePathInDirPath(_path, filePath);
284
+ const isExist = await fs.stat(filePath).catch(() => false);
285
+ if (isExist) {
286
+ const metaData = await getMetaDataByFilePath(filePath);
287
+ if (isExist?.size >= 0 && isExist?.size === metaData?.size) {
288
+ const prepareUpload = method === "POST";
289
+ if (prepareUpload) {
290
+ res.status(200);
291
+ res.setHeader("Location", joinUrl(req.headers["x-uploader-base-url"], fileName));
292
+ res.setHeader("Upload-Offset", +metaData.offset);
293
+ res.setHeader("Upload-Length", +metaData.size);
294
+ }
295
+ if (req.headers["x-uploader-metadata"]) {
296
+ try {
297
+ const realMetaData = JSON.parse(req.headers["x-uploader-metadata"], (key, value) => {
298
+ if (typeof value === "string") {
299
+ return decodeURIComponent(value);
300
+ }
301
+ return value;
302
+ });
303
+ metaData.metadata = {
304
+ ...metaData.metadata,
305
+ ...realMetaData
306
+ };
307
+ } catch (err) {
308
+ console.error("@blocklet/uploader: parse metadata error: ", err);
309
+ }
310
+ }
311
+ const uploadResult = await uploaderProps.onUploadFinish(req, res, metaData);
312
+ res.json(uploadResult);
313
+ return;
314
+ }
315
+ }
316
+ }
317
+ next?.();
318
+ }
319
+ async function getMetaDataByFilePath(filePath) {
320
+ const metaDataPath = `${filePath}.json`;
321
+ const isExist = await fs.stat(filePath).catch(() => false);
322
+ if (isExist) {
323
+ try {
324
+ const metaData = await fs.readFile(metaDataPath, "utf-8");
325
+ const metaDataJson = JSON.parse(metaData);
326
+ return metaDataJson;
327
+ } catch (err) {
328
+ console.error("@blocklet/uploader: getMetaDataByPath error: ", err);
329
+ }
330
+ }
331
+ return null;
332
+ }
333
+ function joinUrl(...args) {
334
+ const realArgs = args.filter(Boolean).map(item => {
335
+ if (item === "/") {
336
+ return "";
337
+ }
338
+ return item;
339
+ });
340
+ return joinUrlLib(...realArgs);
341
+ }
342
+ class RewriteFileConfigstore {
343
+ directory;
344
+ queue;
345
+ constructor(path2) {
346
+ this.directory = path2;
347
+ this.queue = new queue({
348
+ concurrency: 1
349
+ });
350
+ }
351
+ async get(key) {
352
+ try {
353
+ const buffer = await this.queue.add(() => fs.readFile(this.resolve(key), "utf8"));
354
+ const metadata = JSON.parse(buffer);
355
+ if (metadata.offset !== metadata.size) {
356
+ const info = await fs.stat(this.resolve(key, false)).catch(() => false);
357
+ if (info?.size !== metadata?.offset) {
358
+ metadata.offset = info.size;
359
+ this.set(key, metadata);
360
+ }
361
+ }
362
+ return metadata;
363
+ } catch {
364
+ return void 0;
365
+ }
366
+ }
367
+ async set(key, value) {
368
+ if (value?.runtime) {
369
+ delete value.runtime;
370
+ }
371
+ if (value?.metadata?.runtime) {
372
+ delete value.metadata.runtime;
373
+ }
374
+ await this.queue.add(() => fs.writeFile(this.resolve(key), JSON.stringify(value)));
375
+ }
376
+ async safeDeleteFile(filePath) {
377
+ validFilePathInDirPath(this.directory, filePath);
378
+ try {
379
+ const isExist = await fs.stat(filePath).catch(() => false);
380
+ if (isExist) {
381
+ await fs.rm(filePath);
382
+ } else {
383
+ console.log("Can not remove file, the file not exist: ", filePath);
384
+ }
385
+ } catch (err) {
386
+ console.error("@blocklet/uploader: safeDeleteFile error: ", err);
387
+ }
388
+ }
389
+ async delete(key, isMetadata = true) {
390
+ try {
391
+ await this.queue.add(() => this.safeDeleteFile(this.resolve(key, isMetadata)));
392
+ } catch (err) {
393
+ console.error("@blocklet/uploader: delete error: ", err);
394
+ }
395
+ }
396
+ async list() {
397
+ return this.queue.add(async () => {
398
+ const files = await fs.readdir(this.directory, {
399
+ withFileTypes: true
400
+ });
401
+ const promises = files.filter(file => file.isFile() && file.name.endsWith(".json")).map(file => {
402
+ return file.name.replace(".json", "");
403
+ });
404
+ return Promise.all(promises);
405
+ });
406
+ }
407
+ resolve(key, isMetadata = true) {
408
+ let fileKey = key;
409
+ if (isMetadata) {
410
+ fileKey = `${key}.json`;
411
+ }
412
+ return path.join(this.directory, fileKey);
413
+ }
414
+ }
415
+ class RewriteFileStore extends FileStore {
416
+ constructor(options) {
417
+ super(options);
418
+ }
419
+ async remove(key) {
420
+ this.configstore.delete(key);
421
+ this.configstore.delete(key, false);
422
+ }
423
+ }