b2b-platform-utils 1.1.24 → 1.1.25
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/fileSystem.js +158 -135
- package/package.json +1 -1
package/fileSystem.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @module fileSystem
|
|
@@ -19,10 +19,11 @@
|
|
|
19
19
|
* It is NOT recommended in hot request/response paths.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
const fs = require(
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
const {
|
|
22
|
+
const fs = require("fs");
|
|
23
|
+
const fsPromises = require("fs/promises");
|
|
24
|
+
const path = require("path");
|
|
25
|
+
const { warningNote } = require("./logger");
|
|
26
|
+
const { getValue } = require("./localCache");
|
|
26
27
|
|
|
27
28
|
// -----------------------------------------------------------------------------
|
|
28
29
|
// Internal helpers
|
|
@@ -36,9 +37,9 @@ const { getValue } = require('./localCache');
|
|
|
36
37
|
* @returns {string} Absolute path.
|
|
37
38
|
*/
|
|
38
39
|
function resolvePath(fileName) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
return path.isAbsolute(fileName)
|
|
41
|
+
? fileName
|
|
42
|
+
: path.resolve(process.cwd(), fileName);
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
/**
|
|
@@ -47,7 +48,7 @@ function resolvePath(fileName) {
|
|
|
47
48
|
* @param {string} absolutePath
|
|
48
49
|
*/
|
|
49
50
|
function ensureDirForFile(absolutePath) {
|
|
50
|
-
|
|
51
|
+
fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
/**
|
|
@@ -59,11 +60,11 @@ function ensureDirForFile(absolutePath) {
|
|
|
59
60
|
* @returns {string} extension without dot, e.g. "png"
|
|
60
61
|
*/
|
|
61
62
|
function extractCleanExtension(filename) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
// remove query/hash if passed like "avatar.png?ts=123" or "file.pdf#v2"
|
|
64
|
+
const cleanName = filename.split("?")[0].split("#")[0];
|
|
65
|
+
const parts = cleanName.split(".");
|
|
66
|
+
if (parts.length < 2) return "";
|
|
67
|
+
return parts.pop().toLowerCase();
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
// -----------------------------------------------------------------------------
|
|
@@ -80,13 +81,13 @@ function extractCleanExtension(filename) {
|
|
|
80
81
|
* @param {number} [options.spaces=0] - JSON indentation level, e.g. 2 for pretty debug output.
|
|
81
82
|
*/
|
|
82
83
|
function saveFile(fileName, data, options = {}) {
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
const absPath = resolvePath(fileName);
|
|
85
|
+
ensureDirForFile(absPath);
|
|
85
86
|
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
const spaces = Number.isInteger(options.spaces) ? options.spaces : 0;
|
|
88
|
+
const json = JSON.stringify(data, null, spaces);
|
|
88
89
|
|
|
89
|
-
|
|
90
|
+
fs.writeFileSync(absPath, json, { encoding: "utf-8" });
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
/**
|
|
@@ -97,8 +98,8 @@ function saveFile(fileName, data, options = {}) {
|
|
|
97
98
|
* @returns {string} File contents.
|
|
98
99
|
*/
|
|
99
100
|
function readFile(fileName) {
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
const absPath = resolvePath(fileName);
|
|
102
|
+
return fs.readFileSync(absPath, "utf-8");
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
/**
|
|
@@ -110,14 +111,14 @@ function readFile(fileName) {
|
|
|
110
111
|
* @returns {any|null}
|
|
111
112
|
*/
|
|
112
113
|
function readJsonFile(fileName) {
|
|
113
|
-
|
|
114
|
+
const absPath = resolvePath(fileName);
|
|
114
115
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
if (!fs.existsSync(absPath)) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
118
119
|
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
const raw = fs.readFileSync(absPath, "utf-8");
|
|
121
|
+
return JSON.parse(raw);
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
/**
|
|
@@ -127,13 +128,34 @@ function readJsonFile(fileName) {
|
|
|
127
128
|
* @param {string} fileName
|
|
128
129
|
*/
|
|
129
130
|
function removeFile(fileName) {
|
|
130
|
-
|
|
131
|
+
const absPath = resolvePath(fileName);
|
|
131
132
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
if (!fs.existsSync(absPath)) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
135
136
|
|
|
136
|
-
|
|
137
|
+
fs.unlinkSync(absPath);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Remove a file from the filesystem if it exists (async).
|
|
142
|
+
* Safe no-op if the file is already missing.
|
|
143
|
+
*
|
|
144
|
+
* @param {string} fileName - Target file path (absolute or relative).
|
|
145
|
+
* @returns {Promise<boolean>} True if removed, false if file did not exist.
|
|
146
|
+
*/
|
|
147
|
+
async function unlinkIfExists(fileName) {
|
|
148
|
+
const absPath = resolvePath(fileName);
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
await fsPromises.unlink(absPath);
|
|
152
|
+
return true;
|
|
153
|
+
} catch (err) {
|
|
154
|
+
if (err?.code === "ENOENT") {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
throw err;
|
|
158
|
+
}
|
|
137
159
|
}
|
|
138
160
|
|
|
139
161
|
// -----------------------------------------------------------------------------
|
|
@@ -149,58 +171,58 @@ function removeFile(fileName) {
|
|
|
149
171
|
* @returns {{ extension: string|null, error: string }}
|
|
150
172
|
*/
|
|
151
173
|
function getImageExtension(mimetype) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
174
|
+
let extension = null;
|
|
175
|
+
let error = "";
|
|
176
|
+
|
|
177
|
+
switch (mimetype) {
|
|
178
|
+
case "image/png":
|
|
179
|
+
extension = "png";
|
|
180
|
+
break;
|
|
181
|
+
case "image/jpeg":
|
|
182
|
+
extension = "jpeg";
|
|
183
|
+
break;
|
|
184
|
+
case "image/jpg":
|
|
185
|
+
extension = "jpg";
|
|
186
|
+
break;
|
|
187
|
+
case "image/svg+xml":
|
|
188
|
+
extension = "svg";
|
|
189
|
+
break;
|
|
190
|
+
case "image/webp":
|
|
191
|
+
extension = "webp";
|
|
192
|
+
break;
|
|
193
|
+
case "image/gif":
|
|
194
|
+
extension = "gif";
|
|
195
|
+
break;
|
|
196
|
+
case "image/vnd.microsoft.icon":
|
|
197
|
+
extension = "ico";
|
|
198
|
+
break;
|
|
199
|
+
case "font/ttf":
|
|
200
|
+
extension = "ttf";
|
|
201
|
+
break;
|
|
202
|
+
case "text/plain":
|
|
203
|
+
extension = "txt";
|
|
204
|
+
break;
|
|
205
|
+
case "text/csv":
|
|
206
|
+
extension = "csv";
|
|
207
|
+
break;
|
|
208
|
+
case "application/pdf":
|
|
209
|
+
extension = "pdf";
|
|
210
|
+
break;
|
|
211
|
+
case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
|
|
212
|
+
extension = "xlsx";
|
|
213
|
+
break;
|
|
214
|
+
default:
|
|
215
|
+
error = `Undefined file mimetype: ${mimetype}`;
|
|
216
|
+
extension = null;
|
|
217
|
+
|
|
218
|
+
// Emit a non-fatal warning. Service can still decide what to do.
|
|
219
|
+
if (typeof warningNote === "function") {
|
|
220
|
+
warningNote(error);
|
|
221
|
+
}
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
202
224
|
|
|
203
|
-
|
|
225
|
+
return { extension, error };
|
|
204
226
|
}
|
|
205
227
|
|
|
206
228
|
/**
|
|
@@ -211,43 +233,43 @@ function getImageExtension(mimetype) {
|
|
|
211
233
|
* @returns {string} MIME type string or "" if unknown.
|
|
212
234
|
*/
|
|
213
235
|
function getMimeTypeByFileExtension(filename) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
236
|
+
const ext = extractCleanExtension(filename);
|
|
237
|
+
|
|
238
|
+
switch (ext) {
|
|
239
|
+
case "png":
|
|
240
|
+
return "image/png";
|
|
241
|
+
case "gif":
|
|
242
|
+
return "image/gif";
|
|
243
|
+
case "jpeg":
|
|
244
|
+
case "jpg":
|
|
245
|
+
return "image/jpeg";
|
|
246
|
+
case "webp":
|
|
247
|
+
return "image/webp";
|
|
248
|
+
case "ico":
|
|
249
|
+
return "image/vnd.microsoft.icon";
|
|
250
|
+
case "svg":
|
|
251
|
+
return "image/svg+xml";
|
|
252
|
+
case "csv":
|
|
253
|
+
return "text/csv";
|
|
254
|
+
case "txt":
|
|
255
|
+
return "text/plain";
|
|
256
|
+
case "xml":
|
|
257
|
+
return "application/xml";
|
|
258
|
+
case "pdf":
|
|
259
|
+
return "application/pdf";
|
|
260
|
+
case "xls":
|
|
261
|
+
return "application/vnd.ms-excel";
|
|
262
|
+
case "xlsx":
|
|
263
|
+
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
|
264
|
+
case "doc":
|
|
265
|
+
return "application/msword";
|
|
266
|
+
case "docx":
|
|
267
|
+
return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
268
|
+
case "mp4":
|
|
269
|
+
return "video/mp4";
|
|
270
|
+
default:
|
|
271
|
+
return "";
|
|
272
|
+
}
|
|
251
273
|
}
|
|
252
274
|
|
|
253
275
|
// -----------------------------------------------------------------------------
|
|
@@ -273,8 +295,8 @@ function getMimeTypeByFileExtension(filename) {
|
|
|
273
295
|
* @returns {string} Absolute URL.
|
|
274
296
|
*/
|
|
275
297
|
function getAbsoluteMediaResourceUrl(service, instance, fileName) {
|
|
276
|
-
|
|
277
|
-
|
|
298
|
+
const baseUrl = getValue("baseApiURL") || "";
|
|
299
|
+
return `${baseUrl}/file/${service}/${instance}/${fileName}`;
|
|
278
300
|
}
|
|
279
301
|
|
|
280
302
|
// -----------------------------------------------------------------------------
|
|
@@ -282,17 +304,18 @@ function getAbsoluteMediaResourceUrl(service, instance, fileName) {
|
|
|
282
304
|
// -----------------------------------------------------------------------------
|
|
283
305
|
|
|
284
306
|
module.exports = {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
307
|
+
// Filesystem I/O
|
|
308
|
+
resolvePath,
|
|
309
|
+
saveFile,
|
|
310
|
+
readFile,
|
|
311
|
+
readJsonFile,
|
|
312
|
+
removeFile,
|
|
313
|
+
unlinkIfExists,
|
|
314
|
+
|
|
315
|
+
// MIME helpers
|
|
316
|
+
getImageExtension,
|
|
317
|
+
getMimeTypeByFileExtension,
|
|
318
|
+
|
|
319
|
+
// URL helpers
|
|
320
|
+
getAbsoluteMediaResourceUrl,
|
|
298
321
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "b2b-platform-utils",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.25",
|
|
4
4
|
"description": "Shared utilities for Node.js microservices: errors map, local cache, logger, numbers, dates, filesystem, media optimization, paginator, slugger, crypto wrapper, sanitize HTML, sorting.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"license": "KingSizer",
|