@momentumcms/storage 0.0.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ ## 0.1.1 (2026-02-16)
2
+
3
+ This was a version bump only for storage to align it with other projects, there were no code changes.
4
+
5
+ ## 0.1.0 (2026-02-16)
6
+
7
+ ### 🩹 Fixes
8
+
9
+ - address security and reliability issues from code review ([#7](https://github.com/DonaldMurillo/momentum-cms/pull/7))
10
+
11
+ ### ❤️ Thank You
12
+
13
+ - Claude Opus 4.5
14
+ - Donald Murillo @DonaldMurillo
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-present Momentum CMS Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # storage
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Building
6
+
7
+ Run `nx build storage` to build the library.
8
+
9
+ ## Running unit tests
10
+
11
+ Run `nx test storage` to execute the unit tests via [Vitest](https://vitest.dev/).
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@momentumcms/storage",
3
+ "version": "0.0.1",
4
+ "description": "File storage adapters (local filesystem, S3) for Momentum CMS",
5
+ "license": "MIT",
6
+ "author": "Momentum CMS Contributors",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/momentum-cms/momentum-cms.git",
10
+ "directory": "libs/storage"
11
+ },
12
+ "homepage": "https://github.com/momentum-cms/momentum-cms#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/momentum-cms/momentum-cms/issues"
15
+ },
16
+ "keywords": [
17
+ "cms",
18
+ "momentum-cms",
19
+ "storage",
20
+ "file-upload",
21
+ "s3"
22
+ ],
23
+ "engines": {
24
+ "node": ">=18"
25
+ },
26
+ "type": "commonjs",
27
+ "main": "./src/index.js",
28
+ "types": "./src/index.d.ts",
29
+ "dependencies": {
30
+ "tslib": "^2.3.0"
31
+ },
32
+ "peerDependencies": {
33
+ "@momentumcms/core": ">=0.0.1",
34
+ "@aws-sdk/client-s3": "^3.0.0",
35
+ "@aws-sdk/s3-request-presigner": "^3.0.0"
36
+ },
37
+ "peerDependenciesMeta": {
38
+ "@aws-sdk/client-s3": {
39
+ "optional": true
40
+ },
41
+ "@aws-sdk/s3-request-presigner": {
42
+ "optional": true
43
+ }
44
+ }
45
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './lib/storage.types';
2
+ export { localStorageAdapter } from './lib/storage-local';
3
+ export { s3StorageAdapter } from './lib/storage-s3';
4
+ export { validateMimeType, isMimeTypeAllowed, detectMimeType, mimeTypeMatches, } from './lib/mime-validator';
package/src/index.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mimeTypeMatches = exports.detectMimeType = exports.isMimeTypeAllowed = exports.validateMimeType = exports.s3StorageAdapter = exports.localStorageAdapter = void 0;
4
+ const tslib_1 = require("tslib");
5
+ // Storage types
6
+ tslib_1.__exportStar(require("./lib/storage.types"), exports);
7
+ // Storage adapters
8
+ var storage_local_1 = require("./lib/storage-local");
9
+ Object.defineProperty(exports, "localStorageAdapter", { enumerable: true, get: function () { return storage_local_1.localStorageAdapter; } });
10
+ var storage_s3_1 = require("./lib/storage-s3");
11
+ Object.defineProperty(exports, "s3StorageAdapter", { enumerable: true, get: function () { return storage_s3_1.s3StorageAdapter; } });
12
+ // MIME validation
13
+ var mime_validator_1 = require("./lib/mime-validator");
14
+ Object.defineProperty(exports, "validateMimeType", { enumerable: true, get: function () { return mime_validator_1.validateMimeType; } });
15
+ Object.defineProperty(exports, "isMimeTypeAllowed", { enumerable: true, get: function () { return mime_validator_1.isMimeTypeAllowed; } });
16
+ Object.defineProperty(exports, "detectMimeType", { enumerable: true, get: function () { return mime_validator_1.detectMimeType; } });
17
+ Object.defineProperty(exports, "mimeTypeMatches", { enumerable: true, get: function () { return mime_validator_1.mimeTypeMatches; } });
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/storage/src/index.ts"],"names":[],"mappings":";;;;AAAA,gBAAgB;AAChB,8DAAoC;AAEpC,mBAAmB;AACnB,qDAA0D;AAAjD,oHAAA,mBAAmB,OAAA;AAC5B,+CAAoD;AAA3C,8GAAA,gBAAgB,OAAA;AAEzB,kBAAkB;AAClB,uDAK8B;AAJ7B,kHAAA,gBAAgB,OAAA;AAChB,mHAAA,iBAAiB,OAAA;AACjB,gHAAA,cAAc,OAAA;AACd,iHAAA,eAAe,OAAA"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * MIME Type Validator
3
+ * Validates files by checking magic bytes (file signatures)
4
+ */
5
+ import type { MimeValidationResult } from './storage.types';
6
+ /**
7
+ * Detect MIME type from file buffer using magic bytes.
8
+ */
9
+ export declare function detectMimeType(buffer: Buffer): string | null;
10
+ /**
11
+ * Check if a MIME type matches a pattern.
12
+ * Supports glob patterns like 'image/*' and 'video/*'.
13
+ */
14
+ export declare function mimeTypeMatches(mimeType: string, pattern: string): boolean;
15
+ /**
16
+ * Check if a MIME type is allowed by a list of patterns.
17
+ */
18
+ export declare function isMimeTypeAllowed(mimeType: string, allowedTypes: string[]): boolean;
19
+ /**
20
+ * Validate a file's MIME type by checking magic bytes.
21
+ */
22
+ export declare function validateMimeType(buffer: Buffer, claimedType: string, allowedTypes?: string[]): MimeValidationResult;
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+ /**
3
+ * MIME Type Validator
4
+ * Validates files by checking magic bytes (file signatures)
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.detectMimeType = detectMimeType;
8
+ exports.mimeTypeMatches = mimeTypeMatches;
9
+ exports.isMimeTypeAllowed = isMimeTypeAllowed;
10
+ exports.validateMimeType = validateMimeType;
11
+ /**
12
+ * Common file signatures (magic bytes) for MIME type detection.
13
+ */
14
+ const FILE_SIGNATURES = [
15
+ // Images
16
+ { mimeType: 'image/jpeg', bytes: [0xff, 0xd8, 0xff] },
17
+ { mimeType: 'image/png', bytes: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a] },
18
+ { mimeType: 'image/gif', bytes: [0x47, 0x49, 0x46, 0x38] }, // GIF8
19
+ { mimeType: 'image/webp', bytes: [0x52, 0x49, 0x46, 0x46], offset: 0 }, // RIFF (need to check for WEBP at offset 8)
20
+ { mimeType: 'image/bmp', bytes: [0x42, 0x4d] }, // BM
21
+ { mimeType: 'image/tiff', bytes: [0x49, 0x49, 0x2a, 0x00] }, // Little-endian TIFF
22
+ { mimeType: 'image/tiff', bytes: [0x4d, 0x4d, 0x00, 0x2a] }, // Big-endian TIFF
23
+ { mimeType: 'image/x-icon', bytes: [0x00, 0x00, 0x01, 0x00] }, // ICO
24
+ { mimeType: 'image/svg+xml', bytes: [0x3c, 0x73, 0x76, 0x67] }, // <svg (partial match)
25
+ // Documents
26
+ { mimeType: 'application/pdf', bytes: [0x25, 0x50, 0x44, 0x46] }, // %PDF
27
+ // Archives
28
+ { mimeType: 'application/zip', bytes: [0x50, 0x4b, 0x03, 0x04] }, // PK
29
+ { mimeType: 'application/gzip', bytes: [0x1f, 0x8b] },
30
+ { mimeType: 'application/x-rar-compressed', bytes: [0x52, 0x61, 0x72, 0x21] }, // Rar!
31
+ // Audio
32
+ { mimeType: 'audio/mpeg', bytes: [0x49, 0x44, 0x33] }, // ID3 (MP3)
33
+ { mimeType: 'audio/mpeg', bytes: [0xff, 0xfb] }, // MP3 frame sync
34
+ { mimeType: 'audio/wav', bytes: [0x52, 0x49, 0x46, 0x46] }, // RIFF (need to check for WAVE)
35
+ { mimeType: 'audio/ogg', bytes: [0x4f, 0x67, 0x67, 0x53] }, // OggS
36
+ { mimeType: 'audio/flac', bytes: [0x66, 0x4c, 0x61, 0x43] }, // fLaC
37
+ // Video
38
+ { mimeType: 'video/mp4', bytes: [0x00, 0x00, 0x00], offset: 0 }, // Need to check for ftyp at offset 4
39
+ { mimeType: 'video/webm', bytes: [0x1a, 0x45, 0xdf, 0xa3] }, // EBML header
40
+ { mimeType: 'video/avi', bytes: [0x52, 0x49, 0x46, 0x46] }, // RIFF (need to check for AVI)
41
+ // Executables (for blocking)
42
+ { mimeType: 'application/x-executable', bytes: [0x7f, 0x45, 0x4c, 0x46] }, // ELF
43
+ { mimeType: 'application/x-msdownload', bytes: [0x4d, 0x5a] }, // MZ (Windows EXE)
44
+ ];
45
+ /**
46
+ * Detect MIME type from file buffer using magic bytes.
47
+ */
48
+ function detectMimeType(buffer) {
49
+ var _a;
50
+ for (const sig of FILE_SIGNATURES) {
51
+ const offset = (_a = sig.offset) !== null && _a !== void 0 ? _a : 0;
52
+ if (buffer.length < offset + sig.bytes.length) {
53
+ continue;
54
+ }
55
+ let match = true;
56
+ for (let i = 0; i < sig.bytes.length; i++) {
57
+ if (buffer[offset + i] !== sig.bytes[i]) {
58
+ match = false;
59
+ break;
60
+ }
61
+ }
62
+ if (match) {
63
+ // Special handling for RIFF-based formats
64
+ if (sig.bytes[0] === 0x52 && sig.bytes[1] === 0x49) {
65
+ // Check for WEBP
66
+ if (buffer.length >= 12) {
67
+ const formatId = buffer.slice(8, 12).toString('ascii');
68
+ if (formatId === 'WEBP') {
69
+ return 'image/webp';
70
+ }
71
+ if (formatId === 'WAVE') {
72
+ return 'audio/wav';
73
+ }
74
+ if (formatId === 'AVI ') {
75
+ return 'video/avi';
76
+ }
77
+ }
78
+ }
79
+ // Special handling for MP4 (check for ftyp box)
80
+ if (sig.mimeType === 'video/mp4' && buffer.length >= 8) {
81
+ const boxType = buffer.slice(4, 8).toString('ascii');
82
+ if (boxType === 'ftyp') {
83
+ return 'video/mp4';
84
+ }
85
+ }
86
+ return sig.mimeType;
87
+ }
88
+ }
89
+ // Check for text/plain or JSON
90
+ if (isTextContent(buffer)) {
91
+ const text = buffer.toString('utf8', 0, Math.min(buffer.length, 1000));
92
+ if (text.trim().startsWith('{') || text.trim().startsWith('[')) {
93
+ return 'application/json';
94
+ }
95
+ if (text.trim().startsWith('<')) {
96
+ if (text.includes('<svg')) {
97
+ return 'image/svg+xml';
98
+ }
99
+ if (text.includes('<!DOCTYPE html') || text.includes('<html')) {
100
+ return 'text/html';
101
+ }
102
+ return 'application/xml';
103
+ }
104
+ return 'text/plain';
105
+ }
106
+ return null;
107
+ }
108
+ /**
109
+ * Check if buffer appears to be text content.
110
+ */
111
+ function isTextContent(buffer) {
112
+ // Check first 512 bytes for non-text characters
113
+ const checkLength = Math.min(buffer.length, 512);
114
+ for (let i = 0; i < checkLength; i++) {
115
+ const byte = buffer[i];
116
+ // Allow printable ASCII, newlines, tabs, and UTF-8 continuation bytes
117
+ if (byte < 0x09 || // Control chars before tab
118
+ (byte > 0x0d && byte < 0x20) || // Control chars between CR and space
119
+ byte === 0x7f // DEL
120
+ ) {
121
+ // Allow UTF-8 continuation bytes (0x80-0xBF when following a lead byte)
122
+ if (byte >= 0x80 && byte <= 0xbf) {
123
+ continue;
124
+ }
125
+ // Allow UTF-8 lead bytes
126
+ if (byte >= 0xc0 && byte <= 0xf7) {
127
+ continue;
128
+ }
129
+ return false;
130
+ }
131
+ }
132
+ return true;
133
+ }
134
+ /**
135
+ * Check if a MIME type matches a pattern.
136
+ * Supports glob patterns like 'image/*' and 'video/*'.
137
+ */
138
+ function mimeTypeMatches(mimeType, pattern) {
139
+ if (pattern === '*' || pattern === '*/*') {
140
+ return true;
141
+ }
142
+ if (pattern.endsWith('/*')) {
143
+ // Category pattern like 'image/*'
144
+ const category = pattern.slice(0, -2);
145
+ return mimeType.startsWith(`${category}/`);
146
+ }
147
+ // Exact match
148
+ return mimeType === pattern;
149
+ }
150
+ /**
151
+ * Check if a MIME type is allowed by a list of patterns.
152
+ */
153
+ function isMimeTypeAllowed(mimeType, allowedTypes) {
154
+ if (allowedTypes.length === 0) {
155
+ return true; // No restrictions
156
+ }
157
+ return allowedTypes.some((pattern) => mimeTypeMatches(mimeType, pattern));
158
+ }
159
+ /**
160
+ * Validate a file's MIME type by checking magic bytes.
161
+ */
162
+ function validateMimeType(buffer, claimedType, allowedTypes) {
163
+ const detectedType = detectMimeType(buffer);
164
+ // Check if detected type is in allowed list
165
+ if (allowedTypes && allowedTypes.length > 0) {
166
+ const typeToCheck = detectedType !== null && detectedType !== void 0 ? detectedType : claimedType;
167
+ if (!isMimeTypeAllowed(typeToCheck, allowedTypes)) {
168
+ return {
169
+ valid: false,
170
+ detectedType,
171
+ claimedType,
172
+ error: `File type '${typeToCheck}' is not allowed. Allowed types: ${allowedTypes.join(', ')}`,
173
+ };
174
+ }
175
+ }
176
+ // If we couldn't detect the type, trust the claimed type (for unknown formats)
177
+ if (!detectedType) {
178
+ return {
179
+ valid: true,
180
+ detectedType: null,
181
+ claimedType,
182
+ };
183
+ }
184
+ // Check if detected type matches claimed type (allow compatible types)
185
+ const compatible = areMimeTypesCompatible(detectedType, claimedType);
186
+ if (!compatible) {
187
+ return {
188
+ valid: false,
189
+ detectedType,
190
+ claimedType,
191
+ error: `File appears to be '${detectedType}' but was uploaded as '${claimedType}'`,
192
+ };
193
+ }
194
+ return {
195
+ valid: true,
196
+ detectedType,
197
+ claimedType,
198
+ };
199
+ }
200
+ /**
201
+ * Check if two MIME types are compatible (allowing for variations).
202
+ */
203
+ function areMimeTypesCompatible(detected, claimed) {
204
+ // Exact match
205
+ if (detected === claimed) {
206
+ return true;
207
+ }
208
+ // Same category (e.g., image/jpeg vs image/jpg)
209
+ const [detectedCategory] = detected.split('/');
210
+ const [claimedCategory] = claimed.split('/');
211
+ if (detectedCategory !== claimedCategory) {
212
+ return false;
213
+ }
214
+ // Allow common variations
215
+ const variations = {
216
+ 'image/jpeg': ['image/jpg', 'image/pjpeg'],
217
+ 'text/plain': ['text/x-plain'],
218
+ 'application/json': ['text/json'],
219
+ 'application/javascript': ['text/javascript', 'application/x-javascript'],
220
+ };
221
+ const allowedVariations = variations[detected];
222
+ if (allowedVariations && allowedVariations.includes(claimed)) {
223
+ return true;
224
+ }
225
+ // Check reverse
226
+ for (const [canonical, variants] of Object.entries(variations)) {
227
+ if (variants.includes(detected) && (canonical === claimed || variants.includes(claimed))) {
228
+ return true;
229
+ }
230
+ }
231
+ return false;
232
+ }
233
+ //# sourceMappingURL=mime-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mime-validator.js","sourceRoot":"","sources":["../../../../../libs/storage/src/lib/mime-validator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Local Filesystem Storage Adapter
3
+ * Stores files on the local filesystem
4
+ */
5
+ import type { StorageAdapter } from '@momentumcms/core';
6
+ import type { LocalStorageOptions } from './storage.types';
7
+ /**
8
+ * Creates a local filesystem storage adapter.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const storage = localStorageAdapter({
13
+ * directory: './uploads',
14
+ * baseUrl: 'http://localhost:4200/uploads'
15
+ * });
16
+ * ```
17
+ */
18
+ export declare function localStorageAdapter(options: LocalStorageOptions): StorageAdapter;
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ /**
3
+ * Local Filesystem Storage Adapter
4
+ * Stores files on the local filesystem
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.localStorageAdapter = localStorageAdapter;
8
+ const tslib_1 = require("tslib");
9
+ const node_fs_1 = require("node:fs");
10
+ const node_path_1 = require("node:path");
11
+ const node_crypto_1 = require("node:crypto");
12
+ /**
13
+ * Creates a local filesystem storage adapter.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const storage = localStorageAdapter({
18
+ * directory: './uploads',
19
+ * baseUrl: 'http://localhost:4200/uploads'
20
+ * });
21
+ * ```
22
+ */
23
+ function localStorageAdapter(options) {
24
+ const { directory, baseUrl } = options;
25
+ const resolvedRoot = (0, node_path_1.resolve)(directory);
26
+ // Ensure the upload directory exists
27
+ if (!(0, node_fs_1.existsSync)(resolvedRoot)) {
28
+ (0, node_fs_1.mkdirSync)(resolvedRoot, { recursive: true });
29
+ }
30
+ /**
31
+ * Sanitize a path to prevent directory traversal attacks.
32
+ * Resolves the path relative to the upload root and verifies it stays within bounds.
33
+ */
34
+ function safePath(unsafePath) {
35
+ const normalized = (0, node_path_1.normalize)(unsafePath).replace(/^(\.\.(\/|\\|$))+/, '');
36
+ const full = (0, node_path_1.resolve)(resolvedRoot, normalized);
37
+ if (!full.startsWith(resolvedRoot)) {
38
+ throw new Error('Invalid path: directory traversal not allowed');
39
+ }
40
+ // Reject symlinks to prevent escape via symlink chains
41
+ if ((0, node_fs_1.existsSync)(full) && (0, node_fs_1.lstatSync)(full).isSymbolicLink()) {
42
+ throw new Error('Invalid path: symbolic links not allowed');
43
+ }
44
+ return full;
45
+ }
46
+ return {
47
+ upload(file, uploadOptions) {
48
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
49
+ var _a;
50
+ // Generate unique filename
51
+ const ext = (0, node_path_1.extname)(file.originalName) || getExtensionFromMimeType(file.mimeType);
52
+ const filename = (uploadOptions === null || uploadOptions === void 0 ? void 0 : uploadOptions.filename)
53
+ ? `${uploadOptions.filename}${ext}`
54
+ : `${(0, node_crypto_1.randomUUID)()}${ext}`;
55
+ // Determine subdirectory (sanitized)
56
+ const subdir = (_a = uploadOptions === null || uploadOptions === void 0 ? void 0 : uploadOptions.directory) !== null && _a !== void 0 ? _a : '';
57
+ const targetDir = subdir ? safePath(subdir) : resolvedRoot;
58
+ // Ensure subdirectory exists
59
+ if (subdir && !(0, node_fs_1.existsSync)(targetDir)) {
60
+ (0, node_fs_1.mkdirSync)(targetDir, { recursive: true });
61
+ }
62
+ // Write file to disk (validate full path)
63
+ const filePath = safePath(subdir ? (0, node_path_1.join)(subdir, filename) : filename);
64
+ const relativePath = subdir ? (0, node_path_1.join)((0, node_path_1.normalize)(subdir), filename) : filename;
65
+ (0, node_fs_1.writeFileSync)(filePath, file.buffer);
66
+ // Generate URL
67
+ const url = baseUrl ? `${baseUrl}/${relativePath}` : `/api/media/file/${relativePath}`;
68
+ return {
69
+ path: relativePath,
70
+ url,
71
+ filename,
72
+ mimeType: file.mimeType,
73
+ size: file.size,
74
+ };
75
+ });
76
+ },
77
+ delete(path) {
78
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
79
+ const filePath = safePath(path);
80
+ if ((0, node_fs_1.existsSync)(filePath)) {
81
+ (0, node_fs_1.unlinkSync)(filePath);
82
+ return true;
83
+ }
84
+ return false;
85
+ });
86
+ },
87
+ getUrl(path) {
88
+ return baseUrl ? `${baseUrl}/${path}` : `/api/media/file/${path}`;
89
+ },
90
+ exists(path) {
91
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
92
+ const filePath = safePath(path);
93
+ return (0, node_fs_1.existsSync)(filePath);
94
+ });
95
+ },
96
+ read(path) {
97
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
98
+ const filePath = safePath(path);
99
+ if ((0, node_fs_1.existsSync)(filePath)) {
100
+ return (0, node_fs_1.readFileSync)(filePath);
101
+ }
102
+ return null;
103
+ });
104
+ },
105
+ };
106
+ }
107
+ /**
108
+ * Get file extension from MIME type.
109
+ */
110
+ function getExtensionFromMimeType(mimeType) {
111
+ var _a;
112
+ const mimeToExt = {
113
+ 'image/jpeg': '.jpg',
114
+ 'image/png': '.png',
115
+ 'image/gif': '.gif',
116
+ 'image/webp': '.webp',
117
+ 'image/svg+xml': '.svg',
118
+ 'application/pdf': '.pdf',
119
+ 'application/json': '.json',
120
+ 'text/plain': '.txt',
121
+ 'text/html': '.html',
122
+ 'text/css': '.css',
123
+ 'application/javascript': '.js',
124
+ 'video/mp4': '.mp4',
125
+ 'video/webm': '.webm',
126
+ 'audio/mpeg': '.mp3',
127
+ 'audio/wav': '.wav',
128
+ 'application/zip': '.zip',
129
+ };
130
+ return (_a = mimeToExt[mimeType]) !== null && _a !== void 0 ? _a : '';
131
+ }
132
+ //# sourceMappingURL=storage-local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-local.js","sourceRoot":"","sources":["../../../../../libs/storage/src/lib/storage-local.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * S3-compatible Storage Adapter
3
+ * Stores files in Amazon S3 or compatible services (MinIO, DigitalOcean Spaces, etc.)
4
+ */
5
+ import type { StorageAdapter } from '@momentumcms/core';
6
+ import type { S3StorageOptions } from './storage.types';
7
+ /**
8
+ * Creates an S3-compatible storage adapter.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * // Amazon S3
13
+ * const storage = s3StorageAdapter({
14
+ * bucket: 'my-bucket',
15
+ * region: 'us-east-1',
16
+ * });
17
+ *
18
+ * // MinIO
19
+ * const storage = s3StorageAdapter({
20
+ * bucket: 'my-bucket',
21
+ * region: 'us-east-1',
22
+ * endpoint: 'http://localhost:9000',
23
+ * forcePathStyle: true,
24
+ * });
25
+ * ```
26
+ */
27
+ export declare function s3StorageAdapter(options: S3StorageOptions): StorageAdapter;
@@ -0,0 +1,235 @@
1
+ "use strict";
2
+ /**
3
+ * S3-compatible Storage Adapter
4
+ * Stores files in Amazon S3 or compatible services (MinIO, DigitalOcean Spaces, etc.)
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.s3StorageAdapter = s3StorageAdapter;
8
+ const tslib_1 = require("tslib");
9
+ const node_crypto_1 = require("node:crypto");
10
+ const node_path_1 = require("node:path");
11
+ // Dynamic import for AWS SDK to avoid bundling issues
12
+ let S3Client;
13
+ let PutObjectCommand;
14
+ let DeleteObjectCommand;
15
+ let HeadObjectCommand;
16
+ let GetObjectCommand;
17
+ let getSignedUrl;
18
+ /**
19
+ * Load AWS SDK modules dynamically.
20
+ */
21
+ function loadAwsSdk() {
22
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
23
+ if (S3Client)
24
+ return;
25
+ const s3Module = yield Promise.resolve().then(() => require('@aws-sdk/client-s3'));
26
+ const presignerModule = yield Promise.resolve().then(() => require('@aws-sdk/s3-request-presigner'));
27
+ S3Client = s3Module.S3Client;
28
+ PutObjectCommand = s3Module.PutObjectCommand;
29
+ DeleteObjectCommand = s3Module.DeleteObjectCommand;
30
+ HeadObjectCommand = s3Module.HeadObjectCommand;
31
+ GetObjectCommand = s3Module.GetObjectCommand;
32
+ getSignedUrl = presignerModule.getSignedUrl;
33
+ });
34
+ }
35
+ /**
36
+ * Creates an S3-compatible storage adapter.
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * // Amazon S3
41
+ * const storage = s3StorageAdapter({
42
+ * bucket: 'my-bucket',
43
+ * region: 'us-east-1',
44
+ * });
45
+ *
46
+ * // MinIO
47
+ * const storage = s3StorageAdapter({
48
+ * bucket: 'my-bucket',
49
+ * region: 'us-east-1',
50
+ * endpoint: 'http://localhost:9000',
51
+ * forcePathStyle: true,
52
+ * });
53
+ * ```
54
+ */
55
+ function s3StorageAdapter(options) {
56
+ const { bucket, region, accessKeyId, secretAccessKey, endpoint, baseUrl, forcePathStyle = false, acl = 'private', presignedUrlExpiry = 3600, } = options;
57
+ // Lazy-initialized S3 client
58
+ let client = null;
59
+ function getClient() {
60
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
61
+ yield loadAwsSdk();
62
+ if (!client) {
63
+ const clientConfig = {
64
+ region,
65
+ forcePathStyle,
66
+ };
67
+ if (endpoint) {
68
+ clientConfig.endpoint = endpoint;
69
+ }
70
+ if (accessKeyId && secretAccessKey) {
71
+ clientConfig.credentials = {
72
+ accessKeyId,
73
+ secretAccessKey,
74
+ };
75
+ }
76
+ client = new S3Client(clientConfig);
77
+ }
78
+ return client;
79
+ });
80
+ }
81
+ /**
82
+ * Get the public URL for a file.
83
+ */
84
+ function getPublicUrl(key) {
85
+ if (baseUrl) {
86
+ return `${baseUrl}/${key}`;
87
+ }
88
+ if (endpoint) {
89
+ return `${endpoint}/${bucket}/${key}`;
90
+ }
91
+ return `https://${bucket}.s3.${region}.amazonaws.com/${key}`;
92
+ }
93
+ return {
94
+ upload(file, uploadOptions) {
95
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
96
+ const s3 = yield getClient();
97
+ // Generate unique key
98
+ const ext = (0, node_path_1.extname)(file.originalName) || getExtensionFromMimeType(file.mimeType);
99
+ const filename = (uploadOptions === null || uploadOptions === void 0 ? void 0 : uploadOptions.filename)
100
+ ? `${uploadOptions.filename}${ext}`
101
+ : `${(0, node_crypto_1.randomUUID)()}${ext}`;
102
+ // Build the S3 key
103
+ const key = (uploadOptions === null || uploadOptions === void 0 ? void 0 : uploadOptions.directory) ? `${uploadOptions.directory}/${filename}` : filename;
104
+ // Upload to S3
105
+ yield s3.send(new PutObjectCommand({
106
+ Bucket: bucket,
107
+ Key: key,
108
+ Body: file.buffer,
109
+ ContentType: file.mimeType,
110
+ ACL: acl,
111
+ }));
112
+ return {
113
+ path: key,
114
+ url: acl === 'public-read' ? getPublicUrl(key) : `/api/media/file/${key}`,
115
+ filename,
116
+ mimeType: file.mimeType,
117
+ size: file.size,
118
+ };
119
+ });
120
+ },
121
+ delete(path) {
122
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
123
+ const s3 = yield getClient();
124
+ try {
125
+ yield s3.send(new DeleteObjectCommand({
126
+ Bucket: bucket,
127
+ Key: path,
128
+ }));
129
+ return true;
130
+ }
131
+ catch (_a) {
132
+ return false;
133
+ }
134
+ });
135
+ },
136
+ getUrl(path) {
137
+ if (acl === 'public-read') {
138
+ return getPublicUrl(path);
139
+ }
140
+ return `/api/media/file/${path}`;
141
+ },
142
+ exists(path) {
143
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
144
+ const s3 = yield getClient();
145
+ try {
146
+ yield s3.send(new HeadObjectCommand({
147
+ Bucket: bucket,
148
+ Key: path,
149
+ }));
150
+ return true;
151
+ }
152
+ catch (_a) {
153
+ return false;
154
+ }
155
+ });
156
+ },
157
+ getSignedUrl(path, expiresIn) {
158
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
159
+ const s3 = yield getClient();
160
+ yield loadAwsSdk();
161
+ const command = new GetObjectCommand({
162
+ Bucket: bucket,
163
+ Key: path,
164
+ });
165
+ return getSignedUrl(s3, command, {
166
+ expiresIn: expiresIn !== null && expiresIn !== void 0 ? expiresIn : presignedUrlExpiry,
167
+ });
168
+ });
169
+ },
170
+ read(path) {
171
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
172
+ var _a, e_1, _b, _c;
173
+ const s3 = yield getClient();
174
+ try {
175
+ const response = yield s3.send(new GetObjectCommand({
176
+ Bucket: bucket,
177
+ Key: path,
178
+ }));
179
+ if (!response.Body) {
180
+ return null;
181
+ }
182
+ // Convert stream to buffer
183
+ const chunks = [];
184
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- AWS SDK Body type needs cast to iterate
185
+ const body = response.Body;
186
+ try {
187
+ for (var _d = true, body_1 = tslib_1.__asyncValues(body), body_1_1; body_1_1 = yield body_1.next(), _a = body_1_1.done, !_a; _d = true) {
188
+ _c = body_1_1.value;
189
+ _d = false;
190
+ const chunk = _c;
191
+ chunks.push(chunk);
192
+ }
193
+ }
194
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
195
+ finally {
196
+ try {
197
+ if (!_d && !_a && (_b = body_1.return)) yield _b.call(body_1);
198
+ }
199
+ finally { if (e_1) throw e_1.error; }
200
+ }
201
+ return Buffer.concat(chunks);
202
+ }
203
+ catch (_e) {
204
+ return null;
205
+ }
206
+ });
207
+ },
208
+ };
209
+ }
210
+ /**
211
+ * Get file extension from MIME type.
212
+ */
213
+ function getExtensionFromMimeType(mimeType) {
214
+ var _a;
215
+ const mimeToExt = {
216
+ 'image/jpeg': '.jpg',
217
+ 'image/png': '.png',
218
+ 'image/gif': '.gif',
219
+ 'image/webp': '.webp',
220
+ 'image/svg+xml': '.svg',
221
+ 'application/pdf': '.pdf',
222
+ 'application/json': '.json',
223
+ 'text/plain': '.txt',
224
+ 'text/html': '.html',
225
+ 'text/css': '.css',
226
+ 'application/javascript': '.js',
227
+ 'video/mp4': '.mp4',
228
+ 'video/webm': '.webm',
229
+ 'audio/mpeg': '.mp3',
230
+ 'audio/wav': '.wav',
231
+ 'application/zip': '.zip',
232
+ };
233
+ return (_a = mimeToExt[mimeType]) !== null && _a !== void 0 ? _a : '';
234
+ }
235
+ //# sourceMappingURL=storage-s3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-s3.js","sourceRoot":"","sources":["../../../../../libs/storage/src/lib/storage-s3.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Storage Types for Momentum CMS
3
+ * Re-exports core storage interfaces and adds implementation-specific types
4
+ */
5
+ export type { StorageAdapter, UploadedFile, StoredFile, UploadOptions } from '@momentumcms/core';
6
+ /**
7
+ * Options for local filesystem storage adapter.
8
+ */
9
+ export interface LocalStorageOptions {
10
+ /**
11
+ * Directory to store uploaded files.
12
+ * @default './uploads'
13
+ */
14
+ directory: string;
15
+ /**
16
+ * Base URL for serving files.
17
+ * If not provided, files will be served via the API.
18
+ * @example 'http://localhost:4200/uploads'
19
+ */
20
+ baseUrl?: string;
21
+ }
22
+ /**
23
+ * Options for S3-compatible storage adapter.
24
+ */
25
+ export interface S3StorageOptions {
26
+ /**
27
+ * S3 bucket name.
28
+ */
29
+ bucket: string;
30
+ /**
31
+ * AWS region (e.g., 'us-east-1').
32
+ */
33
+ region: string;
34
+ /**
35
+ * AWS access key ID.
36
+ * Can also be set via AWS_ACCESS_KEY_ID environment variable.
37
+ */
38
+ accessKeyId?: string;
39
+ /**
40
+ * AWS secret access key.
41
+ * Can also be set via AWS_SECRET_ACCESS_KEY environment variable.
42
+ */
43
+ secretAccessKey?: string;
44
+ /**
45
+ * Custom endpoint URL for S3-compatible services (MinIO, DigitalOcean Spaces, etc.).
46
+ * @example 'http://localhost:9000' for MinIO
47
+ */
48
+ endpoint?: string;
49
+ /**
50
+ * Base URL for public file access.
51
+ * If not provided, uses standard S3 URL format or generates presigned URLs.
52
+ */
53
+ baseUrl?: string;
54
+ /**
55
+ * Force path-style URLs instead of virtual-hosted style.
56
+ * Required for some S3-compatible services like MinIO.
57
+ * @default false
58
+ */
59
+ forcePathStyle?: boolean;
60
+ /**
61
+ * Default ACL for uploaded files.
62
+ * @default 'private'
63
+ */
64
+ acl?: 'private' | 'public-read' | 'public-read-write' | 'authenticated-read';
65
+ /**
66
+ * Default expiration time for presigned URLs in seconds.
67
+ * @default 3600 (1 hour)
68
+ */
69
+ presignedUrlExpiry?: number;
70
+ }
71
+ /**
72
+ * MIME type validation result.
73
+ */
74
+ export interface MimeValidationResult {
75
+ /** Whether the file is valid */
76
+ valid: boolean;
77
+ /** Detected MIME type from file signature */
78
+ detectedType: string | null;
79
+ /** Claimed MIME type from upload */
80
+ claimedType: string;
81
+ /** Error message if invalid */
82
+ error?: string;
83
+ }
84
+ /**
85
+ * File signature (magic bytes) for MIME type detection.
86
+ */
87
+ export interface FileSignature {
88
+ /** MIME type this signature identifies */
89
+ mimeType: string;
90
+ /** Byte pattern to match */
91
+ bytes: number[];
92
+ /** Offset from start of file */
93
+ offset?: number;
94
+ }
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ /**
3
+ * Storage Types for Momentum CMS
4
+ * Re-exports core storage interfaces and adds implementation-specific types
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ //# sourceMappingURL=storage.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.types.js","sourceRoot":"","sources":["../../../../../libs/storage/src/lib/storage.types.ts"],"names":[],"mappings":";AAAA;;;GAGG"}