arkos 1.0.3-alpha → 1.0.4-alpha

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.
Files changed (29) hide show
  1. package/README.md +0 -32
  2. package/package.json +1 -1
  3. package/dist/cjs/modules/auth/__tests__/auth.controller.test.js +0 -494
  4. package/dist/cjs/modules/auth/__tests__/auth.controller.test.js.map +0 -1
  5. package/dist/cjs/modules/auth/__tests__/auth.service.test.js +0 -470
  6. package/dist/cjs/modules/auth/__tests__/auth.service.test.js.map +0 -1
  7. package/dist/cjs/modules/auth/utils/helpers/__tests__/auth.helpers.test.js +0 -43
  8. package/dist/cjs/modules/auth/utils/helpers/__tests__/auth.helpers.test.js.map +0 -1
  9. package/dist/cjs/modules/base/utils/helpers/__tests__/base.helpers.test.js +0 -754
  10. package/dist/cjs/modules/base/utils/helpers/__tests__/base.helpers.test.js.map +0 -1
  11. package/dist/cjs/modules/file-upload/file-upload.controller.js +0 -226
  12. package/dist/cjs/modules/file-upload/file-upload.controller.js.map +0 -1
  13. package/dist/cjs/modules/file-upload/file-upload.router.js +0 -50
  14. package/dist/cjs/modules/file-upload/file-upload.router.js.map +0 -1
  15. package/dist/cjs/modules/file-upload/file-upload.service.js +0 -353
  16. package/dist/cjs/modules/file-upload/file-upload.service.js.map +0 -1
  17. package/dist/cjs/modules/file-uploader/__tests__/file-uploader.service.test.js +0 -402
  18. package/dist/cjs/modules/file-uploader/__tests__/file-uploader.service.test.js.map +0 -1
  19. package/dist/cjs/modules/file-uploader/utils/helpers/__tests__/file-uploader.helpers.test.js +0 -164
  20. package/dist/cjs/modules/file-uploader/utils/helpers/__tests__/file-uploader.helpers.test.js.map +0 -1
  21. package/dist/es2020/modules/file-upload/file-upload.controller.js +0 -220
  22. package/dist/es2020/modules/file-upload/file-upload.controller.js.map +0 -1
  23. package/dist/es2020/modules/file-upload/file-upload.router.js +0 -44
  24. package/dist/es2020/modules/file-upload/file-upload.router.js.map +0 -1
  25. package/dist/es2020/modules/file-upload/file-upload.service.js +0 -345
  26. package/dist/es2020/modules/file-upload/file-upload.service.js.map +0 -1
  27. package/dist/types/modules/file-upload/file-upload.controller.d.ts +0 -3
  28. package/dist/types/modules/file-upload/file-upload.router.d.ts +0 -3
  29. package/dist/types/modules/file-upload/file-upload.service.d.ts +0 -30
@@ -1,345 +0,0 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import multer from "multer";
11
- import path from "path";
12
- import fs from "fs";
13
- import AppError from "../error-handler/utils/app-error";
14
- import { promisify } from "util";
15
- import { getArkosConfig } from "../../server";
16
- import deepmerge from "../../utils/helpers/deepmerge.helper";
17
- import sharp from "sharp";
18
- export class FileUploaderService {
19
- constructor(uploadDir, fileSizeLimit = 1024 * 1024 * 5, allowedFileTypes = /.*/, maxCount = 30) {
20
- this.fileFilter = (req, file, cb) => {
21
- const extName = this.allowedFileTypes.test(path.extname(file.originalname).toLowerCase());
22
- const mimeType = this.allowedFileTypes.test(file.mimetype);
23
- if (mimeType && extName) {
24
- cb(null, true);
25
- }
26
- else {
27
- cb(new AppError("Invalid file type", 400));
28
- }
29
- };
30
- this.uploadDir = path.join(".", `${uploadDir}/`);
31
- this.fileSizeLimit = fileSizeLimit;
32
- this.allowedFileTypes = allowedFileTypes;
33
- this.maxCount = maxCount;
34
- if (!fs.existsSync(this.uploadDir)) {
35
- fs.mkdirSync(this.uploadDir, { recursive: true });
36
- }
37
- this.storage = multer.diskStorage({
38
- destination: (req, file, cb) => {
39
- cb(null, this.uploadDir);
40
- },
41
- filename: (req, file, cb) => {
42
- const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
43
- cb(null, `${uniqueSuffix}${path.extname(file.originalname)}`);
44
- },
45
- });
46
- }
47
- getUploader() {
48
- return multer({
49
- storage: this.storage,
50
- fileFilter: this.fileFilter,
51
- limits: { fileSize: this.fileSizeLimit },
52
- });
53
- }
54
- handleSingleUpload(oldFilePath) {
55
- return (req, res, next) => {
56
- const upload = this.getUploader().single(this.getFieldName());
57
- upload(req, res, (err) => __awaiter(this, void 0, void 0, function* () {
58
- if (err instanceof multer.MulterError) {
59
- return next(err);
60
- }
61
- else if (err) {
62
- return next(err);
63
- }
64
- if (oldFilePath) {
65
- const filePath = path.join(oldFilePath);
66
- try {
67
- const stats = yield promisify(fs.stat)(filePath);
68
- if (stats) {
69
- yield promisify(fs.unlink)(filePath);
70
- }
71
- }
72
- catch (err) { }
73
- }
74
- next();
75
- }));
76
- };
77
- }
78
- handleMultipleUpload() {
79
- return (req, res, next) => {
80
- const upload = this.getUploader().array(this.getFieldName(), this.maxCount);
81
- upload(req, res, (err) => {
82
- if (err instanceof multer.MulterError)
83
- return next(err);
84
- else if (err)
85
- return next(err);
86
- next();
87
- });
88
- };
89
- }
90
- handleDeleteSingleFile(oldFilePath) {
91
- return (req, res, next) => {
92
- (() => __awaiter(this, void 0, void 0, function* () {
93
- const filePath = path.join(oldFilePath);
94
- try {
95
- const stats = yield promisify(fs.stat)(filePath);
96
- if (stats) {
97
- yield promisify(fs.unlink)(filePath);
98
- }
99
- }
100
- catch (err) { }
101
- next();
102
- }))();
103
- };
104
- }
105
- deleteFileByUrl(fileUrl) {
106
- return __awaiter(this, void 0, void 0, function* () {
107
- try {
108
- const { fileUpload } = getArkosConfig();
109
- const baseRoute = (fileUpload === null || fileUpload === void 0 ? void 0 : fileUpload.baseRoute) || "/api/uploads";
110
- let urlPath;
111
- if (fileUrl.startsWith("http")) {
112
- const url = new URL(fileUrl);
113
- urlPath = url.pathname;
114
- }
115
- else {
116
- urlPath = fileUrl;
117
- }
118
- const baseRouteIndex = urlPath.indexOf(baseRoute);
119
- if (baseRouteIndex === -1) {
120
- throw new AppError("Invalid file URL: base route not found", 400);
121
- }
122
- const pathAfterBaseRoute = urlPath.substring(baseRouteIndex + baseRoute.length);
123
- const cleanPath = pathAfterBaseRoute.startsWith("/")
124
- ? pathAfterBaseRoute.substring(1)
125
- : pathAfterBaseRoute;
126
- const fileTypes = ["images", "videos", "documents", "files"];
127
- let fileType = null;
128
- let fileName = null;
129
- for (const type of fileTypes) {
130
- const typeIndex = cleanPath.indexOf(type + "/");
131
- if (typeIndex !== -1) {
132
- fileType = type;
133
- fileName = cleanPath.substring(typeIndex + type.length + 1);
134
- break;
135
- }
136
- }
137
- if (!fileType || !fileName) {
138
- throw new AppError("Unable to determine file type or file name from URL", 400);
139
- }
140
- const { documentUploaderService, fileUploaderService, imageUploaderService, videoUploaderService, } = getFileUploaderServices();
141
- let filePath;
142
- switch (fileType) {
143
- case "images":
144
- filePath = path.join(imageUploaderService.uploadDir, fileName);
145
- break;
146
- case "videos":
147
- filePath = path.join(videoUploaderService.uploadDir, fileName);
148
- break;
149
- case "documents":
150
- filePath = path.join(documentUploaderService.uploadDir, fileName);
151
- break;
152
- case "files":
153
- filePath = path.join(fileUploaderService.uploadDir, fileName);
154
- break;
155
- default:
156
- throw new AppError(`Unsupported file type: ${fileType}`, 400);
157
- }
158
- yield promisify(fs.access)(filePath, fs.constants.F_OK);
159
- yield promisify(fs.unlink)(filePath);
160
- return true;
161
- }
162
- catch (error) {
163
- if (error instanceof AppError) {
164
- throw error;
165
- }
166
- if (error.code === "ENOENT") {
167
- throw new AppError("File not found", 404);
168
- }
169
- throw new AppError(`Failed to delete file: ${error.message}`, 500);
170
- }
171
- });
172
- }
173
- getFieldName() {
174
- let fieldName = "files";
175
- if (this.uploadDir.endsWith("images") || this.uploadDir.endsWith("images/"))
176
- fieldName === "images";
177
- if (this.uploadDir.endsWith("videos") || this.uploadDir.endsWith("videos/"))
178
- fieldName === "videos";
179
- if (this.uploadDir.endsWith("documents") ||
180
- this.uploadDir.endsWith("documents/"))
181
- fieldName === "documents";
182
- if (this.uploadDir.endsWith("files") || this.uploadDir.endsWith("files/"))
183
- fieldName === "files";
184
- return fieldName;
185
- }
186
- upload(req_1, res_1) {
187
- return __awaiter(this, arguments, void 0, function* (req, res, options = {}) {
188
- const { fileUpload } = getArkosConfig();
189
- const baseRoute = (fileUpload === null || fileUpload === void 0 ? void 0 : fileUpload.baseRoute) || "/api/uploads";
190
- return new Promise((resolve, reject) => {
191
- const isMultiple = Array.isArray(req.query.multiple)
192
- ? req.query.multiple[0] == "true"
193
- : req.query.multiple == "true";
194
- const uploadHandler = isMultiple
195
- ? this.getUploader().array(this.getFieldName(), this.maxCount)
196
- : this.getUploader().single(this.getFieldName());
197
- uploadHandler(req, res, (err) => __awaiter(this, void 0, void 0, function* () {
198
- var _a;
199
- if (err)
200
- return reject(err);
201
- try {
202
- const protocol = ((_a = req.get("host")) === null || _a === void 0 ? void 0 : _a.includes("localhost"))
203
- ? "http"
204
- : "https";
205
- const baseURL = `${protocol}://${req.get("host")}`;
206
- const dirParts = this.uploadDir.split("/");
207
- const fileType = dirParts[dirParts.length - 1] || "files";
208
- const generateRelativePath = (filePath) => {
209
- const baseUploadDir = (fileUpload === null || fileUpload === void 0 ? void 0 : fileUpload.baseUploadDir) || "/uploads";
210
- if (baseUploadDir.startsWith("..")) {
211
- return path.join(fileType, path.basename(filePath));
212
- }
213
- else {
214
- return filePath.replace(`${process.cwd()}${baseUploadDir}/`, "");
215
- }
216
- };
217
- const processImage = (filePath) => __awaiter(this, void 0, void 0, function* () {
218
- const ext = path.extname(filePath).toLowerCase();
219
- const originalFormat = ext.replace(".", "");
220
- const outputFormat = options.format || originalFormat;
221
- if (!/jpeg|jpg|png|gif|webp|svg|bmp|tiff|heif/i.test(originalFormat)) {
222
- const relativePath = generateRelativePath(filePath);
223
- return `${baseURL}${baseRoute}/${relativePath}`;
224
- }
225
- const tempName = `${path.basename(filePath, ext)}_${Date.now()}${ext}`;
226
- const tempPath = path.join(path.dirname(filePath), tempName);
227
- try {
228
- let transformer = sharp(filePath);
229
- const metadata = yield transformer.metadata();
230
- if (options.resizeTo && metadata.width && metadata.height) {
231
- const targetSize = options.resizeTo;
232
- const scaleFactor = targetSize / Math.min(metadata.width, metadata.height);
233
- const newWidth = Math.round(metadata.width * scaleFactor);
234
- const newHeight = Math.round(metadata.height * scaleFactor);
235
- transformer = transformer.resize(newWidth, newHeight);
236
- }
237
- else if (options.width || options.height) {
238
- transformer = transformer.resize(options.width || null, options.height || null, {
239
- fit: "inside",
240
- });
241
- }
242
- if (outputFormat === "webp") {
243
- transformer = transformer.toFormat("webp");
244
- }
245
- else if (outputFormat === "jpeg" || outputFormat === "jpg") {
246
- transformer = transformer.toFormat("jpeg");
247
- }
248
- yield transformer.toFile(tempPath);
249
- yield promisify(fs.rename)(tempPath, filePath);
250
- const relativePath = generateRelativePath(filePath);
251
- return `${baseURL}${baseRoute}/${relativePath}`;
252
- }
253
- catch (error) {
254
- try {
255
- yield promisify(fs.stat)(tempPath);
256
- yield promisify(fs.unlink)(tempPath);
257
- }
258
- catch (_a) {
259
- }
260
- throw error;
261
- }
262
- });
263
- const processFile = (filePath) => __awaiter(this, void 0, void 0, function* () {
264
- const relativePath = generateRelativePath(filePath);
265
- return `${baseURL}${baseRoute}/${relativePath}`;
266
- });
267
- let data;
268
- if (req.files && Array.isArray(req.files)) {
269
- const isImageUploader = this.uploadDir.includes("/images");
270
- if (isImageUploader) {
271
- data = yield Promise.all(req.files.map((file) => processImage(file.path)));
272
- }
273
- else {
274
- data = yield Promise.all(req.files.map((file) => processFile(file.path)));
275
- }
276
- data = data.filter((url) => url !== null);
277
- }
278
- else if (req.file) {
279
- const isImageUploader = this.uploadDir.includes("/images");
280
- if (isImageUploader) {
281
- data = yield processImage(req.file.path);
282
- }
283
- else {
284
- data = yield processFile(req.file.path);
285
- }
286
- }
287
- else {
288
- return reject(new AppError("No file uploaded", 400));
289
- }
290
- resolve(data);
291
- }
292
- catch (error) {
293
- reject(error);
294
- }
295
- }));
296
- });
297
- });
298
- }
299
- }
300
- export const getFileUploaderServices = () => {
301
- const { fileUpload } = getArkosConfig();
302
- const baseUploadDir = (fileUpload === null || fileUpload === void 0 ? void 0 : fileUpload.baseUploadDir) || "/uploads";
303
- const defaultRegexPatterns = {
304
- image: /jpeg|jpg|png|gif|webp|svg|bmp|tiff|heif|heic|ico|jfif|raw|cr2|nef|orf|sr2|arw|dng|pef|raf|rw2|psd|ai|eps|xcf|jxr|wdp|hdp|jp2|j2k|jpf|jpx|jpm|mj2|avif/,
305
- video: /mp4|avi|mov|mkv|flv|wmv|webm|mpg|mpeg|3gp|m4v|ts|rm|rmvb|vob|ogv|dv|qt|asf|m2ts|mts|divx|f4v|swf|mxf|roq|nsv|mvb|svi|mpe|m2v|mp2|mpv|h264|h265|hevc/,
306
- document: /pdf|doc|docx|xls|xlsx|ppt|pptx|odt|ods|odg|odp|txt|rtf|csv|epub|md|tex|pages|numbers|key|xml|json|yaml|yml|ini|cfg|conf|log|html|htm|xhtml|djvu|mobi|azw|azw3|fb2|lit|ps|wpd|wps|dot|dotx|xlt|xltx|pot|potx|oft|one|onetoc2|opf|oxps|hwp/,
307
- other: /.*/,
308
- };
309
- const defaultRestrictions = {
310
- images: {
311
- maxCount: 30,
312
- maxSize: 1024 * 1024 * 15,
313
- supportedFilesRegex: defaultRegexPatterns.image,
314
- },
315
- videos: {
316
- maxCount: 10,
317
- maxSize: 1024 * 1024 * 5096,
318
- supportedFilesRegex: defaultRegexPatterns.video,
319
- },
320
- documents: {
321
- maxCount: 30,
322
- maxSize: 1024 * 1024 * 50,
323
- supportedFilesRegex: defaultRegexPatterns.document,
324
- },
325
- others: {
326
- maxCount: 10,
327
- maxSize: 1024 * 1024 * 5096,
328
- supportedFilesRegex: defaultRegexPatterns.other,
329
- },
330
- };
331
- const restrictions = (fileUpload === null || fileUpload === void 0 ? void 0 : fileUpload.uploadRestrictions)
332
- ? deepmerge(defaultRestrictions, fileUpload.uploadRestrictions)
333
- : defaultRestrictions;
334
- const imageUploaderService = new FileUploaderService(`${baseUploadDir}/images`, restrictions.images.maxSize, restrictions.images.supportedFilesRegex, restrictions.images.maxCount);
335
- const videoUploaderService = new FileUploaderService(`${baseUploadDir}/videos`, restrictions.videos.maxSize, restrictions.videos.supportedFilesRegex, restrictions.videos.maxCount);
336
- const documentUploaderService = new FileUploaderService(`${baseUploadDir}/documents`, restrictions.documents.maxSize, restrictions.documents.supportedFilesRegex, restrictions.documents.maxCount);
337
- const fileUploaderService = new FileUploaderService(`${baseUploadDir}/files`, restrictions.others.maxSize, restrictions.others.supportedFilesRegex, restrictions.others.maxCount);
338
- return {
339
- imageUploaderService,
340
- videoUploaderService,
341
- documentUploaderService,
342
- fileUploaderService,
343
- };
344
- };
345
- //# sourceMappingURL=file-upload.service.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"file-upload.service.js","sourceRoot":"","sources":["../../../../src/modules/file-upload/file-upload.service.ts"],"names":[],"mappings":"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","sourcesContent":["import multer, { StorageEngine } from \"multer\";\nimport path from \"path\";\nimport fs from \"fs\";\nimport { Request, Response, NextFunction } from \"express\";\nimport AppError from \"../error-handler/utils/app-error\";\nimport { promisify } from \"util\";\nimport { getArkosConfig } from \"../../server\";\nimport deepmerge from \"../../utils/helpers/deepmerge.helper\";\nimport sharp from \"sharp\";\nimport { ArkosRequest, ArkosResponse } from \"../../types\";\n\n/**\n * Service to handle file uploads, including single and multiple file uploads,\n * file validation (type, size), and file deletion.\n */\nexport class FileUploaderService {\n private uploadDir: string;\n private fileSizeLimit: number;\n private allowedFileTypes: RegExp;\n private storage: StorageEngine;\n private maxCount: number;\n\n /**\n * Constructor to initialize the file uploader service.\n * @param {string} uploadDir - The directory where files will be uploaded.\n * @param {number} fileSizeLimit - The maximum allowed file size.\n * @param {RegExp} allowedFileTypes - The regular expression for allowed file types.\n */\n constructor(\n uploadDir: string,\n fileSizeLimit: number = 1024 * 1024 * 5,\n allowedFileTypes: RegExp = /.*/,\n maxCount: number = 30\n ) {\n this.uploadDir = path.join(\".\", `${uploadDir}/`);\n this.fileSizeLimit = fileSizeLimit;\n this.allowedFileTypes = allowedFileTypes;\n this.maxCount = maxCount;\n\n if (!fs.existsSync(this.uploadDir)) {\n fs.mkdirSync(this.uploadDir, { recursive: true });\n }\n\n this.storage = multer.diskStorage({\n destination: (req, file, cb) => {\n cb(null, this.uploadDir);\n },\n filename: (req, file, cb) => {\n const uniqueSuffix = Date.now() + \"-\" + Math.round(Math.random() * 1e9);\n cb(null, `${uniqueSuffix}${path.extname(file.originalname)}`);\n },\n });\n }\n\n /**\n * Validates the file's type and MIME type.\n * @param {Request} req - The Express request object.\n * @param {Express.Multer.File} file - The uploaded file.\n * @param {Function} cb - The callback function to indicate if file is valid.\n */\n private fileFilter = (req: any, file: any, cb: any) => {\n const extName = this.allowedFileTypes.test(\n path.extname(file.originalname).toLowerCase()\n );\n const mimeType = this.allowedFileTypes.test(file.mimetype);\n\n if (mimeType && extName) {\n cb(null, true);\n } else {\n cb(new AppError(\"Invalid file type\", 400));\n }\n };\n\n /**\n * Returns the multer upload configuration.\n * @returns {multer.Instance} The multer instance configured for file uploads.\n */\n public getUploader() {\n return multer({\n storage: this.storage,\n fileFilter: this.fileFilter,\n limits: { fileSize: this.fileSizeLimit },\n });\n }\n\n /**\n * Middleware to handle single file upload.\n * @param {string} [oldFilePath] - The path to the file to delete before uploading.\n * @returns {Function} Middleware function for handling file upload.\n */\n public handleSingleUpload(oldFilePath?: string) {\n return (req: ArkosRequest, res: ArkosResponse, next: NextFunction) => {\n const upload = this.getUploader().single(this.getFieldName());\n upload(req, res, async (err) => {\n if (err instanceof multer.MulterError) {\n return next(err);\n } else if (err) {\n return next(err);\n }\n\n if (oldFilePath) {\n const filePath = path.join(oldFilePath);\n try {\n const stats = await promisify(fs.stat)(filePath);\n if (stats) {\n await promisify(fs.unlink)(filePath);\n }\n } catch (err) {}\n }\n\n next();\n });\n };\n }\n\n /**\n * Middleware to handle multiple file uploads.\n * @param {number} maxCount - The maximum number of files allowed for upload.\n * @returns {Function} Middleware function for handling multiple file uploads.\n */\n public handleMultipleUpload() {\n return (req: ArkosRequest, res: ArkosResponse, next: NextFunction) => {\n const upload = this.getUploader().array(\n this.getFieldName(),\n this.maxCount\n );\n upload(req, res, (err) => {\n if (err instanceof multer.MulterError) return next(err);\n else if (err) return next(err);\n next();\n });\n };\n }\n\n /**\n * Middleware to handle deletion of a single file from the filesystem.\n * @param {string} oldFilePath - The path to the file to be deleted.\n * @returns {Function} Middleware function for handling file deletion.\n */\n public handleDeleteSingleFile(oldFilePath: string) {\n return (req: ArkosRequest, res: ArkosResponse, next: NextFunction) => {\n (async () => {\n const filePath = path.join(oldFilePath);\n try {\n const stats = await promisify(fs.stat)(filePath);\n if (stats) {\n await promisify(fs.unlink)(filePath);\n }\n } catch (err) {}\n\n next();\n })();\n };\n }\n\n /**\n * Deletes a file based on its URL by identifying the appropriate uploader service\n * @param {string} fileUrl - The URL of the file to delete\n * @returns {Promise<boolean>} - True if deletion successful, false otherwise\n */\n public async deleteFileByUrl(fileUrl: string): Promise<boolean> {\n try {\n // Get configuration values\n const { fileUpload } = getArkosConfig();\n const baseRoute = fileUpload?.baseRoute || \"/api/uploads\";\n\n // Parse the URL to get the path\n let urlPath: string;\n if (fileUrl.startsWith(\"http\")) {\n const url = new URL(fileUrl);\n urlPath = url.pathname;\n } else {\n urlPath = fileUrl;\n }\n\n // Extract the path after the base route\n const baseRouteIndex = urlPath.indexOf(baseRoute);\n if (baseRouteIndex === -1) {\n throw new AppError(\"Invalid file URL: base route not found\", 400);\n }\n\n const pathAfterBaseRoute = urlPath.substring(\n baseRouteIndex + baseRoute.length\n );\n const cleanPath = pathAfterBaseRoute.startsWith(\"/\")\n ? pathAfterBaseRoute.substring(1)\n : pathAfterBaseRoute;\n\n // Determine file type and file name\n const fileTypes = [\"images\", \"videos\", \"documents\", \"files\"];\n let fileType: string | null = null;\n let fileName: string | null = null;\n\n for (const type of fileTypes) {\n const typeIndex = cleanPath.indexOf(type + \"/\");\n if (typeIndex !== -1) {\n fileType = type;\n fileName = cleanPath.substring(typeIndex + type.length + 1);\n break;\n }\n }\n\n if (!fileType || !fileName) {\n throw new AppError(\n \"Unable to determine file type or file name from URL\",\n 400\n );\n }\n\n // Get the appropriate uploader service based on file type\n const {\n documentUploaderService,\n fileUploaderService,\n imageUploaderService,\n videoUploaderService,\n } = getFileUploaderServices();\n\n let filePath: string;\n switch (fileType) {\n case \"images\":\n filePath = path.join(imageUploaderService.uploadDir, fileName);\n break;\n case \"videos\":\n filePath = path.join(videoUploaderService.uploadDir, fileName);\n break;\n case \"documents\":\n filePath = path.join(documentUploaderService.uploadDir, fileName);\n break;\n case \"files\":\n filePath = path.join(fileUploaderService.uploadDir, fileName);\n break;\n default:\n throw new AppError(`Unsupported file type: ${fileType}`, 400);\n }\n\n // Delete the file\n await promisify(fs.access)(filePath, fs.constants.F_OK);\n await promisify(fs.unlink)(filePath);\n\n return true;\n } catch (error: any) {\n if (error instanceof AppError) {\n throw error;\n }\n\n if (error.code === \"ENOENT\") {\n throw new AppError(\"File not found\", 404);\n }\n\n throw new AppError(`Failed to delete file: ${error.message}`, 500);\n }\n }\n\n private getFieldName() {\n let fieldName = \"files\";\n if (this.uploadDir.endsWith(\"images\") || this.uploadDir.endsWith(\"images/\"))\n fieldName === \"images\";\n if (this.uploadDir.endsWith(\"videos\") || this.uploadDir.endsWith(\"videos/\"))\n fieldName === \"videos\";\n if (\n this.uploadDir.endsWith(\"documents\") ||\n this.uploadDir.endsWith(\"documents/\")\n )\n fieldName === \"documents\";\n if (this.uploadDir.endsWith(\"files\") || this.uploadDir.endsWith(\"files/\"))\n fieldName === \"files\";\n return fieldName;\n }\n\n /**\n * Handles the upload process and returns the full URLs of uploaded files\n * @param {ArkosRequest} req - Arkos request object containing the files\n * @param {ArkosResponse} res - Arkos response object\n * @param {object} options - Optional parameters for image processing\n * @returns {Promise<string|string[]>} URL or array of URLs to the uploaded files\n */\n public async upload(\n req: ArkosRequest,\n res: ArkosResponse,\n options: {\n format?: string;\n width?: number;\n height?: number;\n resizeTo?: number;\n } = {}\n ): Promise<string | string[] | null> {\n const { fileUpload } = getArkosConfig();\n const baseRoute = fileUpload?.baseRoute || \"/api/uploads\";\n\n return new Promise((resolve, reject) => {\n // Determine if it's a single or multiple file upload\n const isMultiple = Array.isArray(req.query.multiple)\n ? req.query.multiple[0] == \"true\"\n : req.query.multiple == \"true\";\n\n // Use appropriate upload handler\n const uploadHandler = isMultiple\n ? this.getUploader().array(this.getFieldName(), this.maxCount)\n : this.getUploader().single(this.getFieldName());\n\n uploadHandler(req, res, async (err) => {\n if (err) return reject(err);\n\n try {\n // Determine the base URL for file access\n const protocol = req.get(\"host\")?.includes(\"localhost\")\n ? \"http\"\n : \"https\";\n const baseURL = `${protocol}://${req.get(\"host\")}`;\n\n // Get file type from uploadDir path\n const dirParts = this.uploadDir.split(\"/\");\n const fileType = dirParts[dirParts.length - 1] || \"files\";\n\n /**\n * Generates the correct relative path regardless of upload directory location\n */\n const generateRelativePath = (filePath: string) => {\n const baseUploadDir = fileUpload?.baseUploadDir || \"/uploads\";\n if (baseUploadDir.startsWith(\"..\")) {\n // For paths outside project directory\n return path.join(fileType, path.basename(filePath));\n } else {\n // For paths within project\n return filePath.replace(`${process.cwd()}${baseUploadDir}/`, \"\");\n }\n };\n\n /**\n * Processes image files using Sharp for resizing and format conversion\n */\n const processImage = async (\n filePath: string\n ): Promise<string | null> => {\n const ext = path.extname(filePath).toLowerCase();\n const originalFormat = ext.replace(\".\", \"\");\n const outputFormat = options.format || originalFormat;\n\n // Skip processing for non-image files\n if (\n !/jpeg|jpg|png|gif|webp|svg|bmp|tiff|heif/i.test(originalFormat)\n ) {\n const relativePath = generateRelativePath(filePath);\n return `${baseURL}${baseRoute}/${relativePath}`;\n }\n\n // Create a temp filename with original name + random string\n const tempName = `${path.basename(\n filePath,\n ext\n )}_${Date.now()}${ext}`;\n const tempPath = path.join(path.dirname(filePath), tempName);\n\n try {\n let transformer = sharp(filePath);\n const metadata = await transformer.metadata();\n\n // Apply resize transformations if requested\n if (options.resizeTo && metadata.width && metadata.height) {\n const targetSize = options.resizeTo;\n const scaleFactor =\n targetSize / Math.min(metadata.width, metadata.height);\n const newWidth = Math.round(metadata.width * scaleFactor);\n const newHeight = Math.round(metadata.height * scaleFactor);\n transformer = transformer.resize(newWidth, newHeight);\n } else if (options.width || options.height) {\n transformer = transformer.resize(\n options.width || null,\n options.height || null,\n {\n fit: \"inside\",\n }\n );\n }\n\n // Apply format transformations if requested\n if (outputFormat === \"webp\") {\n transformer = transformer.toFormat(\"webp\");\n } else if (outputFormat === \"jpeg\" || outputFormat === \"jpg\") {\n transformer = transformer.toFormat(\"jpeg\");\n }\n\n // Save to temp file first\n await transformer.toFile(tempPath);\n\n // Rename temp file to original filename\n await promisify(fs.rename)(tempPath, filePath);\n\n // Return the public URL for the file\n const relativePath = generateRelativePath(filePath);\n return `${baseURL}${baseRoute}/${relativePath}`;\n } catch (error) {\n // Clean up temp file if it exists\n try {\n await promisify(fs.stat)(tempPath);\n await promisify(fs.unlink)(tempPath);\n } catch {\n // If temp file doesn't exist, no need to clean up\n }\n throw error;\n }\n };\n\n /**\n * Handles basic file processing for non-image files\n */\n const processFile = async (filePath: string): Promise<string> => {\n const relativePath = generateRelativePath(filePath);\n return `${baseURL}${baseRoute}/${relativePath}`;\n };\n\n // Process all uploaded files\n let data;\n if (req.files && Array.isArray(req.files)) {\n // Process multiple files\n const isImageUploader = this.uploadDir.includes(\"/images\");\n if (isImageUploader) {\n data = await Promise.all(\n req.files.map((file) => processImage(file.path))\n );\n } else {\n data = await Promise.all(\n req.files.map((file) => processFile(file.path))\n );\n }\n // Filter out any null values from failed processing\n data = data.filter((url) => url !== null);\n } else if (req.file) {\n // Process a single file\n const isImageUploader = this.uploadDir.includes(\"/images\");\n if (isImageUploader) {\n data = await processImage(req.file.path);\n } else {\n data = await processFile(req.file.path);\n }\n } else {\n return reject(new AppError(\"No file uploaded\", 400));\n }\n\n resolve(data);\n } catch (error) {\n reject(error);\n }\n });\n });\n }\n}\n\n/**\n * Creates and returns all file uploader services based on config\n * @returns Object containing all specialized file uploader services\n */\nexport const getFileUploaderServices = () => {\n const { fileUpload } = getArkosConfig();\n const baseUploadDir = fileUpload?.baseUploadDir || \"/uploads\";\n\n // Default regex patterns for each file type\n const defaultRegexPatterns = {\n image:\n /jpeg|jpg|png|gif|webp|svg|bmp|tiff|heif|heic|ico|jfif|raw|cr2|nef|orf|sr2|arw|dng|pef|raf|rw2|psd|ai|eps|xcf|jxr|wdp|hdp|jp2|j2k|jpf|jpx|jpm|mj2|avif/,\n video:\n /mp4|avi|mov|mkv|flv|wmv|webm|mpg|mpeg|3gp|m4v|ts|rm|rmvb|vob|ogv|dv|qt|asf|m2ts|mts|divx|f4v|swf|mxf|roq|nsv|mvb|svi|mpe|m2v|mp2|mpv|h264|h265|hevc/,\n document:\n /pdf|doc|docx|xls|xlsx|ppt|pptx|odt|ods|odg|odp|txt|rtf|csv|epub|md|tex|pages|numbers|key|xml|json|yaml|yml|ini|cfg|conf|log|html|htm|xhtml|djvu|mobi|azw|azw3|fb2|lit|ps|wpd|wps|dot|dotx|xlt|xltx|pot|potx|oft|one|onetoc2|opf|oxps|hwp/,\n other: /.*/,\n };\n\n // Default upload restrictions\n const defaultRestrictions = {\n images: {\n maxCount: 30,\n maxSize: 1024 * 1024 * 15, // 15 MB\n supportedFilesRegex: defaultRegexPatterns.image,\n },\n videos: {\n maxCount: 10,\n maxSize: 1024 * 1024 * 5096, // 5 GB\n supportedFilesRegex: defaultRegexPatterns.video,\n },\n documents: {\n maxCount: 30,\n maxSize: 1024 * 1024 * 50, // 50 MB\n supportedFilesRegex: defaultRegexPatterns.document,\n },\n others: {\n maxCount: 10,\n maxSize: 1024 * 1024 * 5096, // 5 GB\n supportedFilesRegex: defaultRegexPatterns.other,\n },\n };\n\n // Merge with user configuration (if any)\n const restrictions = fileUpload?.uploadRestrictions\n ? deepmerge(defaultRestrictions, fileUpload.uploadRestrictions)\n : defaultRestrictions;\n\n /**\n * Specialized file uploader service for handling image uploads.\n */\n const imageUploaderService = new FileUploaderService(\n `${baseUploadDir}/images`,\n restrictions.images.maxSize,\n restrictions.images.supportedFilesRegex,\n restrictions.images.maxCount\n );\n\n /**\n * Specialized file uploader service for handling video uploads.\n */\n const videoUploaderService = new FileUploaderService(\n `${baseUploadDir}/videos`,\n restrictions.videos.maxSize,\n restrictions.videos.supportedFilesRegex,\n restrictions.videos.maxCount\n );\n\n /**\n * Specialized file uploader service for handling document uploads.\n */\n const documentUploaderService = new FileUploaderService(\n `${baseUploadDir}/documents`,\n restrictions.documents.maxSize,\n restrictions.documents.supportedFilesRegex,\n restrictions.documents.maxCount\n );\n\n /**\n * Generic file uploader service for handling all file uploads.\n */\n const fileUploaderService = new FileUploaderService(\n `${baseUploadDir}/files`,\n restrictions.others.maxSize,\n restrictions.others.supportedFilesRegex,\n restrictions.others.maxCount\n );\n\n return {\n imageUploaderService,\n videoUploaderService,\n documentUploaderService,\n fileUploaderService,\n };\n};\n"]}
@@ -1,3 +0,0 @@
1
- export declare const uploadFile: (req: import("../../types").ArkosRequest, res: import("../../types").ArkosResponse, next: import("../../types").ArkosNextFunction) => Promise<void>;
2
- export declare const deleteFile: (req: import("../../types").ArkosRequest, res: import("../../types").ArkosResponse, next: import("../../types").ArkosNextFunction) => Promise<void>;
3
- export declare const streamFile: (req: import("../../types").ArkosRequest, res: import("../../types").ArkosResponse, next: import("../../types").ArkosNextFunction) => Promise<void>;
@@ -1,3 +0,0 @@
1
- import { Router } from "express";
2
- import { ArkosConfig } from "../../types/arkos-config";
3
- export declare function getFileUploaderRouter({ fileUpload }: ArkosConfig): Promise<Router>;
@@ -1,30 +0,0 @@
1
- import multer from "multer";
2
- import { NextFunction } from "express";
3
- import { ArkosRequest, ArkosResponse } from "../../types";
4
- export declare class FileUploaderService {
5
- private uploadDir;
6
- private fileSizeLimit;
7
- private allowedFileTypes;
8
- private storage;
9
- private maxCount;
10
- constructor(uploadDir: string, fileSizeLimit?: number, allowedFileTypes?: RegExp, maxCount?: number);
11
- private fileFilter;
12
- getUploader(): multer.Multer;
13
- handleSingleUpload(oldFilePath?: string): (req: ArkosRequest, res: ArkosResponse, next: NextFunction) => void;
14
- handleMultipleUpload(): (req: ArkosRequest, res: ArkosResponse, next: NextFunction) => void;
15
- handleDeleteSingleFile(oldFilePath: string): (req: ArkosRequest, res: ArkosResponse, next: NextFunction) => void;
16
- deleteFileByUrl(fileUrl: string): Promise<boolean>;
17
- private getFieldName;
18
- upload(req: ArkosRequest, res: ArkosResponse, options?: {
19
- format?: string;
20
- width?: number;
21
- height?: number;
22
- resizeTo?: number;
23
- }): Promise<string | string[] | null>;
24
- }
25
- export declare const getFileUploaderServices: () => {
26
- imageUploaderService: FileUploaderService;
27
- videoUploaderService: FileUploaderService;
28
- documentUploaderService: FileUploaderService;
29
- fileUploaderService: FileUploaderService;
30
- };