@owlmeans/storage-resource 0.1.2 → 0.1.4
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/README.md +43 -464
- package/package.json +8 -7
- package/tsconfig.json +4 -4
package/README.md
CHANGED
|
@@ -1,512 +1,91 @@
|
|
|
1
1
|
# @owlmeans/storage-resource
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
S3-compatible object storage resource for OwlMeans server applications.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- **Stream Processing**: Efficient handling of file streams for large files
|
|
12
|
-
- **Type Validation**: Automatic file type detection and validation
|
|
13
|
-
- **Prefix Management**: Organized file storage with configurable prefixes
|
|
14
|
-
- **Resource Integration**: Seamless integration with OwlMeans resource management system
|
|
15
|
-
|
|
16
|
-
This package follows the OwlMeans resource pattern and provides server-side storage capabilities for applications requiring file management.
|
|
7
|
+
- `createStorageResource(alias?, configKey?)` — creates an S3-backed storage resource
|
|
8
|
+
- `appendStorageResource(ctx, alias?, configKey?)` — registers the resource in the context
|
|
9
|
+
- `StorageResource` — extends `Resource<StoredRecord>` for uploading and retrieving files
|
|
10
|
+
- `StoredConfigAppend` — config mixin type for storage bucket credentials
|
|
17
11
|
|
|
18
12
|
## Installation
|
|
19
13
|
|
|
20
14
|
```bash
|
|
21
|
-
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Dependencies
|
|
25
|
-
|
|
26
|
-
This package requires and integrates with:
|
|
27
|
-
- `@owlmeans/resource`: Base resource management system
|
|
28
|
-
- `@owlmeans/server-context`: Server context management
|
|
29
|
-
- `@owlmeans/storage-common`: Storage utilities and error types
|
|
30
|
-
- `@aws-sdk/client-s3`: AWS S3 SDK for storage operations
|
|
31
|
-
- `file-type`: File type detection utilities
|
|
32
|
-
|
|
33
|
-
## Core Concepts
|
|
34
|
-
|
|
35
|
-
### Storage Resource
|
|
36
|
-
|
|
37
|
-
A specialized resource that manages file storage operations including upload, download, and metadata management through S3-compatible APIs.
|
|
38
|
-
|
|
39
|
-
### File Validation
|
|
40
|
-
|
|
41
|
-
Built-in file type validation ensures that uploaded files match their declared MIME types, providing security against file type spoofing.
|
|
42
|
-
|
|
43
|
-
### Stream Processing
|
|
44
|
-
|
|
45
|
-
Efficient handling of file streams allows for processing large files without excessive memory usage.
|
|
46
|
-
|
|
47
|
-
### Bucket Configuration
|
|
48
|
-
|
|
49
|
-
Support for multiple storage buckets with different configurations for organizing files by purpose or security level.
|
|
50
|
-
|
|
51
|
-
## API Reference
|
|
52
|
-
|
|
53
|
-
### Types
|
|
54
|
-
|
|
55
|
-
#### `StoredRecord`
|
|
56
|
-
Interface for stored file records extending ResourceRecord.
|
|
57
|
-
|
|
58
|
-
```typescript
|
|
59
|
-
interface StoredRecord extends ResourceRecord {
|
|
60
|
-
url?: string // File URL for access
|
|
61
|
-
size?: number // File size in bytes
|
|
62
|
-
prefix: string // Storage prefix/path
|
|
63
|
-
stream?: Readable // File stream for upload
|
|
64
|
-
format?: StoredFileFormat // File format information
|
|
65
|
-
type?: string // MIME type
|
|
66
|
-
bytes?: Uint8Array // File bytes (alternative to stream)
|
|
67
|
-
base64?: string // Base64 encoded file data
|
|
68
|
-
}
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
#### `StorageConfig`
|
|
72
|
-
Configuration for storage bucket access.
|
|
73
|
-
|
|
74
|
-
```typescript
|
|
75
|
-
interface StorageConfig {
|
|
76
|
-
url: string // Storage service URL
|
|
77
|
-
apiKey: string // API key in format "keyId:keySecret"
|
|
78
|
-
basePrefix: string // Base prefix for all files in this bucket
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
#### `StorageResource`
|
|
83
|
-
Resource interface for storage operations.
|
|
84
|
-
|
|
85
|
-
```typescript
|
|
86
|
-
interface StorageResource extends Resource<StoredRecord> {
|
|
87
|
-
// Inherits all Resource methods for CRUD operations
|
|
88
|
-
// Specialized for file storage with validation
|
|
89
|
-
}
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
#### `StoredConfigAppend`
|
|
93
|
-
Configuration extension for storage bucket definitions.
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
interface StoredConfigAppend {
|
|
97
|
-
storageBuckets: { [key: string]: StorageConfig }
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### Factory Functions
|
|
102
|
-
|
|
103
|
-
#### `createStorageResource(alias?: string, configKey?: string): StorageResource`
|
|
104
|
-
|
|
105
|
-
Creates a storage resource instance with S3-compatible operations.
|
|
106
|
-
|
|
107
|
-
**Parameters:**
|
|
108
|
-
- `alias` (optional): Resource alias (default: `DEFAULT_ALIAS`)
|
|
109
|
-
- `configKey` (optional): Configuration key for bucket selection (defaults to alias)
|
|
110
|
-
|
|
111
|
-
**Returns:** StorageResource instance
|
|
112
|
-
|
|
113
|
-
**Features:**
|
|
114
|
-
- File upload with type validation
|
|
115
|
-
- Stream processing for efficient memory usage
|
|
116
|
-
- S3-compatible storage operations
|
|
117
|
-
- Automatic file type detection
|
|
118
|
-
- Error handling for storage operations
|
|
119
|
-
|
|
120
|
-
**Example:**
|
|
121
|
-
```typescript
|
|
122
|
-
import { createStorageResource } from '@owlmeans/storage-resource'
|
|
123
|
-
|
|
124
|
-
const storageResource = createStorageResource('files', 'main-bucket')
|
|
125
|
-
|
|
126
|
-
// Use with context
|
|
127
|
-
context.registerResource(storageResource)
|
|
128
|
-
await context.configure().init()
|
|
129
|
-
|
|
130
|
-
// Upload file
|
|
131
|
-
const uploadedFile = await storageResource.create({
|
|
132
|
-
stream: fileStream,
|
|
133
|
-
size: fileSize,
|
|
134
|
-
type: 'image/jpeg',
|
|
135
|
-
prefix: 'uploads/images'
|
|
136
|
-
})
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
### Storage Operations
|
|
140
|
-
|
|
141
|
-
#### File Upload
|
|
142
|
-
|
|
143
|
-
Upload files with validation and type checking:
|
|
144
|
-
|
|
145
|
-
```typescript
|
|
146
|
-
const fileRecord = {
|
|
147
|
-
stream: fileStream, // Readable stream
|
|
148
|
-
size: fileSize, // File size in bytes
|
|
149
|
-
type: 'image/jpeg', // Expected MIME type
|
|
150
|
-
prefix: 'uploads/avatars' // Storage prefix
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
try {
|
|
154
|
-
const storedFile = await storageResource.create(fileRecord)
|
|
155
|
-
console.log(`File uploaded: ${storedFile.url}`)
|
|
156
|
-
} catch (error) {
|
|
157
|
-
if (error instanceof FileTypeError) {
|
|
158
|
-
// Handle file type mismatch
|
|
159
|
-
} else if (error instanceof FileStreamError) {
|
|
160
|
-
// Handle stream errors
|
|
161
|
-
}
|
|
162
|
-
}
|
|
15
|
+
bun add @owlmeans/storage-resource
|
|
163
16
|
```
|
|
164
17
|
|
|
165
|
-
|
|
18
|
+
## Usage
|
|
166
19
|
|
|
167
|
-
|
|
20
|
+
Add storage config type:
|
|
168
21
|
|
|
169
22
|
```typescript
|
|
170
|
-
|
|
171
|
-
const file = await storageResource.get(fileId)
|
|
172
|
-
console.log(`File URL: ${file.url}`)
|
|
173
|
-
|
|
174
|
-
// Search files by prefix
|
|
175
|
-
const files = await storageResource.list({
|
|
176
|
-
filter: { prefix: 'uploads/images' }
|
|
177
|
-
})
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
### Error Handling
|
|
181
|
-
|
|
182
|
-
The package provides specific error types for storage operations:
|
|
23
|
+
import type { StoredConfigAppend } from '@owlmeans/storage-resource'
|
|
183
24
|
|
|
184
|
-
|
|
185
|
-
Thrown when file stream is missing or invalid.
|
|
186
|
-
|
|
187
|
-
```typescript
|
|
188
|
-
if (record.stream == null) {
|
|
189
|
-
throw new FileStreamError('no')
|
|
190
|
-
}
|
|
25
|
+
interface AppConfig extends BasicConfig, StoredConfigAppend {}
|
|
191
26
|
```
|
|
192
27
|
|
|
193
|
-
|
|
194
|
-
Thrown when required file properties are missing.
|
|
28
|
+
Register the resource:
|
|
195
29
|
|
|
196
30
|
```typescript
|
|
197
|
-
|
|
198
|
-
throw new FilePropertyError('size')
|
|
199
|
-
}
|
|
200
|
-
```
|
|
31
|
+
import { appendStorageResource } from '@owlmeans/storage-resource'
|
|
201
32
|
|
|
202
|
-
|
|
203
|
-
Thrown when file type doesn't match declared MIME type.
|
|
204
|
-
|
|
205
|
-
```typescript
|
|
206
|
-
if (type?.mime !== record.type) {
|
|
207
|
-
throw new FileTypeError('mime-mismatch')
|
|
208
|
-
}
|
|
33
|
+
appendStorageResource(context, 'images')
|
|
209
34
|
```
|
|
210
35
|
|
|
211
|
-
|
|
212
|
-
Thrown when storage API operations fail.
|
|
213
|
-
|
|
214
|
-
### Constants
|
|
36
|
+
Config (`config.json`):
|
|
215
37
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
## Usage Examples
|
|
224
|
-
|
|
225
|
-
### Basic Storage Setup
|
|
226
|
-
|
|
227
|
-
```typescript
|
|
228
|
-
import { createStorageResource } from '@owlmeans/storage-resource'
|
|
229
|
-
import { makeServerContext } from '@owlmeans/server-context'
|
|
230
|
-
|
|
231
|
-
// Configure server context with storage buckets
|
|
232
|
-
const context = makeServerContext({
|
|
233
|
-
service: 'file-service',
|
|
234
|
-
type: AppType.Backend,
|
|
235
|
-
layer: Layer.Service,
|
|
236
|
-
storageBuckets: {
|
|
237
|
-
'main': {
|
|
238
|
-
url: 'my-bucket.s3.amazonaws.com',
|
|
239
|
-
apiKey: 'AKIAIOSFODNN7EXAMPLE:wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
|
240
|
-
basePrefix: 'files'
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
// Create and register storage resource
|
|
246
|
-
const storageResource = createStorageResource('storage', 'main')
|
|
247
|
-
context.registerResource(storageResource)
|
|
248
|
-
|
|
249
|
-
await context.configure().init()
|
|
250
|
-
|
|
251
|
-
// Storage resource is now ready for use
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
### File Upload with Validation
|
|
255
|
-
|
|
256
|
-
```typescript
|
|
257
|
-
import { Readable } from 'stream'
|
|
258
|
-
|
|
259
|
-
const uploadFile = async (fileBuffer: Buffer, mimeType: string, filename: string) => {
|
|
260
|
-
const fileStream = Readable.from(fileBuffer)
|
|
261
|
-
|
|
262
|
-
try {
|
|
263
|
-
const uploadedFile = await storageResource.create({
|
|
264
|
-
stream: fileStream,
|
|
265
|
-
size: fileBuffer.length,
|
|
266
|
-
type: mimeType,
|
|
267
|
-
prefix: `uploads/${new Date().getFullYear()}`
|
|
268
|
-
})
|
|
269
|
-
|
|
270
|
-
return {
|
|
271
|
-
id: uploadedFile.id,
|
|
272
|
-
url: uploadedFile.url,
|
|
273
|
-
size: uploadedFile.size
|
|
274
|
-
}
|
|
275
|
-
} catch (error) {
|
|
276
|
-
if (error instanceof FileTypeError) {
|
|
277
|
-
throw new Error('File type mismatch - uploaded file doesn\'t match declared type')
|
|
278
|
-
} else if (error instanceof FilePropertyError) {
|
|
279
|
-
throw new Error('Missing required file properties')
|
|
280
|
-
} else {
|
|
281
|
-
throw new Error('File upload failed')
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"storageBuckets": {
|
|
41
|
+
"images": {
|
|
42
|
+
"url": "https://s3.amazonaws.com",
|
|
43
|
+
"apiKey": "...",
|
|
44
|
+
"basePrefix": "my-bucket/images"
|
|
282
45
|
}
|
|
283
46
|
}
|
|
284
47
|
}
|
|
285
48
|
```
|
|
286
49
|
|
|
287
|
-
|
|
50
|
+
Upload a file:
|
|
288
51
|
|
|
289
52
|
```typescript
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
basePrefix: 'public'
|
|
297
|
-
},
|
|
298
|
-
'private': {
|
|
299
|
-
url: 'private-bucket.s3.amazonaws.com',
|
|
300
|
-
apiKey: 'private-key:private-secret',
|
|
301
|
-
basePrefix: 'private'
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
// Create resources for different buckets
|
|
307
|
-
const publicStorage = createStorageResource('public-storage', 'public')
|
|
308
|
-
const privateStorage = createStorageResource('private-storage', 'private')
|
|
309
|
-
|
|
310
|
-
context.registerResource(publicStorage)
|
|
311
|
-
context.registerResource(privateStorage)
|
|
312
|
-
|
|
313
|
-
await context.configure().init()
|
|
314
|
-
|
|
315
|
-
// Upload to public bucket
|
|
316
|
-
const publicFile = await publicStorage.create({
|
|
317
|
-
stream: publicFileStream,
|
|
318
|
-
size: fileSize,
|
|
319
|
-
type: 'image/jpeg',
|
|
320
|
-
prefix: 'avatars'
|
|
321
|
-
})
|
|
322
|
-
|
|
323
|
-
// Upload to private bucket
|
|
324
|
-
const privateFile = await privateStorage.create({
|
|
325
|
-
stream: privateFileStream,
|
|
326
|
-
size: fileSize,
|
|
327
|
-
type: 'application/pdf',
|
|
328
|
-
prefix: 'documents'
|
|
53
|
+
const storage = context.resource<StorageResource>('images')
|
|
54
|
+
await storage.save({
|
|
55
|
+
id: fileId,
|
|
56
|
+
prefix: 'uploads',
|
|
57
|
+
stream: readableStream,
|
|
58
|
+
type: 'image/jpeg'
|
|
329
59
|
})
|
|
330
60
|
```
|
|
331
61
|
|
|
332
|
-
|
|
62
|
+
## API
|
|
333
63
|
|
|
334
|
-
|
|
335
|
-
const uploadWithValidation = async (fileStream: Readable, declaredType: string, size: number) => {
|
|
336
|
-
try {
|
|
337
|
-
const result = await storageResource.create({
|
|
338
|
-
stream: fileStream,
|
|
339
|
-
size: size,
|
|
340
|
-
type: declaredType,
|
|
341
|
-
prefix: 'validated-uploads'
|
|
342
|
-
})
|
|
343
|
-
|
|
344
|
-
console.log('File upload successful with type validation')
|
|
345
|
-
return result
|
|
346
|
-
} catch (error) {
|
|
347
|
-
if (error instanceof FileTypeError) {
|
|
348
|
-
console.error('File type validation failed - file content doesn\'t match declared MIME type')
|
|
349
|
-
throw error
|
|
350
|
-
}
|
|
351
|
-
throw error
|
|
352
|
-
}
|
|
353
|
-
}
|
|
64
|
+
### `createStorageResource(alias?, configKey?): StorageResource`
|
|
354
65
|
|
|
355
|
-
|
|
356
|
-
const jpegStream = fs.createReadStream('image.jpg')
|
|
357
|
-
await uploadWithValidation(jpegStream, 'image/jpeg', jpegStats.size)
|
|
358
|
-
```
|
|
66
|
+
Creates an S3 storage resource. `alias` defaults to `DEFAULT_ALIAS` (`'s3-storage'`).
|
|
359
67
|
|
|
360
|
-
###
|
|
68
|
+
### `appendStorageResource<C, T>(ctx, alias?, configKey?): T`
|
|
361
69
|
|
|
362
|
-
|
|
363
|
-
// Upload from bytes
|
|
364
|
-
const uploadFromBytes = async (bytes: Uint8Array, type: string) => {
|
|
365
|
-
const stream = Readable.from(Buffer.from(bytes))
|
|
366
|
-
|
|
367
|
-
return await storageResource.create({
|
|
368
|
-
stream: stream,
|
|
369
|
-
size: bytes.length,
|
|
370
|
-
type: type,
|
|
371
|
-
prefix: 'byte-uploads'
|
|
372
|
-
})
|
|
373
|
-
}
|
|
70
|
+
Registers the storage resource in the context.
|
|
374
71
|
|
|
375
|
-
|
|
376
|
-
const uploadFromBase64 = async (base64Data: string, type: string) => {
|
|
377
|
-
const buffer = Buffer.from(base64Data, 'base64')
|
|
378
|
-
const stream = Readable.from(buffer)
|
|
379
|
-
|
|
380
|
-
return await storageResource.create({
|
|
381
|
-
stream: stream,
|
|
382
|
-
size: buffer.length,
|
|
383
|
-
type: type,
|
|
384
|
-
prefix: 'base64-uploads'
|
|
385
|
-
})
|
|
386
|
-
}
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
### File Retrieval and Management
|
|
390
|
-
|
|
391
|
-
```typescript
|
|
392
|
-
// Get file metadata
|
|
393
|
-
const getFileInfo = async (fileId: string) => {
|
|
394
|
-
const file = await storageResource.get(fileId)
|
|
395
|
-
|
|
396
|
-
return {
|
|
397
|
-
id: file.id,
|
|
398
|
-
url: file.url,
|
|
399
|
-
size: file.size,
|
|
400
|
-
type: file.type,
|
|
401
|
-
prefix: file.prefix
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// List files by prefix
|
|
406
|
-
const listFilesByPrefix = async (prefix: string) => {
|
|
407
|
-
const files = await storageResource.list({
|
|
408
|
-
filter: { prefix: prefix }
|
|
409
|
-
})
|
|
410
|
-
|
|
411
|
-
return files.map(file => ({
|
|
412
|
-
id: file.id,
|
|
413
|
-
url: file.url,
|
|
414
|
-
size: file.size
|
|
415
|
-
}))
|
|
416
|
-
}
|
|
72
|
+
### `StoredRecord`
|
|
417
73
|
|
|
418
|
-
|
|
419
|
-
const deleteFile = async (fileId: string) => {
|
|
420
|
-
await storageResource.remove(fileId)
|
|
421
|
-
}
|
|
422
|
-
```
|
|
74
|
+
Extends `ResourceRecord` with: `url?`, `size?`, `prefix`, `stream?`, `format?`, `type?`, `bytes?`, `base64?`
|
|
423
75
|
|
|
424
|
-
|
|
76
|
+
### `StorageConfig`
|
|
425
77
|
|
|
426
|
-
|
|
78
|
+
`{ url: string, apiKey: string, basePrefix: string }`
|
|
427
79
|
|
|
428
|
-
|
|
80
|
+
### `stripData<Input, Output>(file): Output`
|
|
429
81
|
|
|
430
|
-
|
|
431
|
-
interface ServerConfig {
|
|
432
|
-
// ... other configuration
|
|
433
|
-
storageBuckets: {
|
|
434
|
-
[bucketAlias: string]: {
|
|
435
|
-
url: string // S3 endpoint URL
|
|
436
|
-
apiKey: string // Access key ID and secret key separated by ':'
|
|
437
|
-
basePrefix: string // Base prefix for all files
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
```
|
|
82
|
+
Strips raw data fields from a `StoredFileWithData` to produce a `StoredFile` (URL-only).
|
|
442
83
|
|
|
443
|
-
###
|
|
84
|
+
### `supportedMimeTypes`
|
|
444
85
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
```typescript
|
|
448
|
-
const config = {
|
|
449
|
-
storageBuckets: {
|
|
450
|
-
main: {
|
|
451
|
-
url: process.env.S3_BUCKET_URL,
|
|
452
|
-
apiKey: `${process.env.S3_ACCESS_KEY}:${process.env.S3_SECRET_KEY}`,
|
|
453
|
-
basePrefix: process.env.S3_BASE_PREFIX || 'files'
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
## Security Considerations
|
|
460
|
-
|
|
461
|
-
### File Type Validation
|
|
462
|
-
- Always validate file types to prevent malicious uploads
|
|
463
|
-
- Use MIME type detection from file content, not just extensions
|
|
464
|
-
- Implement file size limits to prevent abuse
|
|
465
|
-
|
|
466
|
-
### Access Control
|
|
467
|
-
- Use separate buckets for different security levels
|
|
468
|
-
- Implement proper API key management and rotation
|
|
469
|
-
- Consider signed URLs for temporary access
|
|
470
|
-
|
|
471
|
-
### Storage Security
|
|
472
|
-
- Use HTTPS for all storage operations
|
|
473
|
-
- Implement proper bucket policies and access controls
|
|
474
|
-
- Monitor storage access and usage patterns
|
|
475
|
-
|
|
476
|
-
## Performance Considerations
|
|
477
|
-
|
|
478
|
-
- **Stream Processing**: Use streams for large files to minimize memory usage
|
|
479
|
-
- **File Size Limits**: Implement appropriate file size limits
|
|
480
|
-
- **Concurrent Uploads**: Consider rate limiting for multiple concurrent uploads
|
|
481
|
-
- **CDN Integration**: Use CDN for frequently accessed files
|
|
482
|
-
|
|
483
|
-
## Integration with OwlMeans Ecosystem
|
|
484
|
-
|
|
485
|
-
This package integrates with:
|
|
486
|
-
|
|
487
|
-
- **@owlmeans/resource**: Base resource management patterns
|
|
488
|
-
- **@owlmeans/server-context**: Server context and configuration
|
|
489
|
-
- **@owlmeans/storage-common**: Shared storage utilities and errors
|
|
490
|
-
|
|
491
|
-
## Best Practices
|
|
492
|
-
|
|
493
|
-
1. **Validate file types** always from content, not just filename
|
|
494
|
-
2. **Use appropriate prefixes** to organize files logically
|
|
495
|
-
3. **Implement file size limits** to prevent abuse
|
|
496
|
-
4. **Handle errors gracefully** with specific error types
|
|
497
|
-
5. **Use separate buckets** for different security levels
|
|
498
|
-
6. **Monitor storage usage** and implement cleanup policies
|
|
86
|
+
Array of MIME types accepted for upload.
|
|
499
87
|
|
|
500
88
|
## Related Packages
|
|
501
89
|
|
|
502
|
-
-
|
|
503
|
-
-
|
|
504
|
-
- **@owlmeans/server-context**: Server context management
|
|
505
|
-
|
|
506
|
-
## AWS S3 Compatibility
|
|
507
|
-
|
|
508
|
-
This package works with:
|
|
509
|
-
- **AWS S3**: Native Amazon S3 service
|
|
510
|
-
- **MinIO**: Self-hosted S3-compatible storage
|
|
511
|
-
- **DigitalOcean Spaces**: S3-compatible cloud storage
|
|
512
|
-
- **Other S3-compatible services**: Any service implementing S3 API
|
|
90
|
+
- [`@owlmeans/storage-common`](../storage-common) — shared types (`StoredFileMeta`, `StoredFile`, etc.)
|
|
91
|
+
- [`@owlmeans/image-resource`](../image-resource) — image-specific resource built on top of this
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/storage-resource",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -25,17 +25,18 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@aws-sdk/client-s3": "^3.701.0",
|
|
28
|
-
"@owlmeans/context": "^0.1.
|
|
29
|
-
"@owlmeans/error": "^0.1.
|
|
30
|
-
"@owlmeans/resource": "^0.1.
|
|
31
|
-
"@owlmeans/server-context": "^0.1.
|
|
32
|
-
"@owlmeans/storage-common": "^0.1.
|
|
28
|
+
"@owlmeans/context": "^0.1.4",
|
|
29
|
+
"@owlmeans/error": "^0.1.4",
|
|
30
|
+
"@owlmeans/resource": "^0.1.4",
|
|
31
|
+
"@owlmeans/server-context": "^0.1.4",
|
|
32
|
+
"@owlmeans/storage-common": "^0.1.4",
|
|
33
33
|
"file-type": "^19.6.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
+
"@owlmeans/dep-config": "workspace:*",
|
|
36
37
|
"@types/node": "^24.10.1",
|
|
37
38
|
"nodemon": "^3.1.11",
|
|
38
|
-
"typescript": "^
|
|
39
|
+
"typescript": "^6.0.2"
|
|
39
40
|
},
|
|
40
41
|
"publishConfig": {
|
|
41
42
|
"access": "public"
|
package/tsconfig.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"extends": [
|
|
3
|
-
"
|
|
3
|
+
"@owlmeans/dep-config/tsconfig.base.json",
|
|
4
|
+
"@owlmeans/dep-config/tsconfig.node.json"
|
|
4
5
|
],
|
|
5
6
|
"compilerOptions": {
|
|
6
|
-
"rootDir": "./src/",
|
|
7
|
-
"outDir": "./build/"
|
|
8
|
-
"moduleResolution": "Bundler",
|
|
7
|
+
"rootDir": "./src/",
|
|
8
|
+
"outDir": "./build/"
|
|
9
9
|
},
|
|
10
10
|
"exclude": [
|
|
11
11
|
"./dist/**/*",
|