@momentumcms/storage 0.1.0
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 +10 -0
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/package.json +45 -0
- package/src/index.d.ts +4 -0
- package/src/index.js +18 -0
- package/src/index.js.map +1 -0
- package/src/lib/mime-validator.d.ts +22 -0
- package/src/lib/mime-validator.js +233 -0
- package/src/lib/mime-validator.js.map +1 -0
- package/src/lib/storage-local.d.ts +18 -0
- package/src/lib/storage-local.js +132 -0
- package/src/lib/storage-local.js.map +1 -0
- package/src/lib/storage-s3.d.ts +27 -0
- package/src/lib/storage-s3.js +235 -0
- package/src/lib/storage-s3.js.map +1 -0
- package/src/lib/storage.types.d.ts +94 -0
- package/src/lib/storage.types.js +7 -0
- package/src/lib/storage.types.js.map +1 -0
package/CHANGELOG.md
ADDED
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
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@momentumcms/storage",
|
|
3
|
+
"version": "0.1.0",
|
|
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
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
|
package/src/index.js.map
ADDED
|
@@ -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 @@
|
|
|
1
|
+
{"version":3,"file":"storage.types.js","sourceRoot":"","sources":["../../../../../libs/storage/src/lib/storage.types.ts"],"names":[],"mappings":";AAAA;;;GAGG"}
|