@venizia/ignis-docs 0.0.5 → 0.0.6-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.
Files changed (98) hide show
  1. package/package.json +1 -1
  2. package/wiki/best-practices/architecture-decisions.md +0 -8
  3. package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
  4. package/wiki/best-practices/performance-optimization.md +3 -3
  5. package/wiki/best-practices/security-guidelines.md +2 -2
  6. package/wiki/best-practices/troubleshooting-tips.md +1 -1
  7. package/wiki/guides/core-concepts/components-guide.md +1 -1
  8. package/wiki/guides/core-concepts/components.md +2 -2
  9. package/wiki/guides/core-concepts/dependency-injection.md +1 -1
  10. package/wiki/guides/core-concepts/services.md +1 -1
  11. package/wiki/guides/tutorials/building-a-crud-api.md +1 -1
  12. package/wiki/guides/tutorials/ecommerce-api.md +2 -2
  13. package/wiki/guides/tutorials/realtime-chat.md +6 -6
  14. package/wiki/guides/tutorials/testing.md +1 -1
  15. package/wiki/references/base/bootstrapping.md +0 -2
  16. package/wiki/references/base/components.md +2 -2
  17. package/wiki/references/base/controllers.md +0 -1
  18. package/wiki/references/base/datasources.md +1 -1
  19. package/wiki/references/base/dependency-injection.md +1 -1
  20. package/wiki/references/base/filter-system/quick-reference.md +0 -14
  21. package/wiki/references/base/middlewares.md +0 -8
  22. package/wiki/references/base/providers.md +0 -9
  23. package/wiki/references/base/services.md +0 -1
  24. package/wiki/references/components/authentication/api.md +444 -0
  25. package/wiki/references/components/authentication/errors.md +177 -0
  26. package/wiki/references/components/authentication/index.md +571 -0
  27. package/wiki/references/components/authentication/usage.md +781 -0
  28. package/wiki/references/components/health-check.md +292 -103
  29. package/wiki/references/components/index.md +14 -12
  30. package/wiki/references/components/mail/api.md +505 -0
  31. package/wiki/references/components/mail/errors.md +176 -0
  32. package/wiki/references/components/mail/index.md +535 -0
  33. package/wiki/references/components/mail/usage.md +404 -0
  34. package/wiki/references/components/request-tracker.md +229 -25
  35. package/wiki/references/components/socket-io/api.md +1051 -0
  36. package/wiki/references/components/socket-io/errors.md +119 -0
  37. package/wiki/references/components/socket-io/index.md +410 -0
  38. package/wiki/references/components/socket-io/usage.md +322 -0
  39. package/wiki/references/components/static-asset/api.md +261 -0
  40. package/wiki/references/components/static-asset/errors.md +89 -0
  41. package/wiki/references/components/static-asset/index.md +617 -0
  42. package/wiki/references/components/static-asset/usage.md +364 -0
  43. package/wiki/references/components/swagger.md +390 -110
  44. package/wiki/references/components/template/api-page.md +125 -0
  45. package/wiki/references/components/template/errors-page.md +100 -0
  46. package/wiki/references/components/template/index.md +104 -0
  47. package/wiki/references/components/template/setup-page.md +134 -0
  48. package/wiki/references/components/template/single-page.md +132 -0
  49. package/wiki/references/components/template/usage-page.md +127 -0
  50. package/wiki/references/components/websocket/api.md +508 -0
  51. package/wiki/references/components/websocket/errors.md +123 -0
  52. package/wiki/references/components/websocket/index.md +453 -0
  53. package/wiki/references/components/websocket/usage.md +475 -0
  54. package/wiki/references/helpers/cron/index.md +224 -0
  55. package/wiki/references/helpers/crypto/index.md +537 -0
  56. package/wiki/references/helpers/env/index.md +214 -0
  57. package/wiki/references/helpers/error/index.md +232 -0
  58. package/wiki/references/helpers/index.md +16 -15
  59. package/wiki/references/helpers/inversion/index.md +608 -0
  60. package/wiki/references/helpers/logger/index.md +600 -0
  61. package/wiki/references/helpers/network/api.md +986 -0
  62. package/wiki/references/helpers/network/index.md +620 -0
  63. package/wiki/references/helpers/queue/index.md +589 -0
  64. package/wiki/references/helpers/redis/index.md +495 -0
  65. package/wiki/references/helpers/socket-io/api.md +497 -0
  66. package/wiki/references/helpers/socket-io/index.md +513 -0
  67. package/wiki/references/helpers/storage/api.md +705 -0
  68. package/wiki/references/helpers/storage/index.md +583 -0
  69. package/wiki/references/helpers/template/index.md +66 -0
  70. package/wiki/references/helpers/template/single-page.md +126 -0
  71. package/wiki/references/helpers/testing/index.md +510 -0
  72. package/wiki/references/helpers/types/index.md +512 -0
  73. package/wiki/references/helpers/uid/index.md +272 -0
  74. package/wiki/references/helpers/websocket/api.md +736 -0
  75. package/wiki/references/helpers/websocket/index.md +574 -0
  76. package/wiki/references/helpers/worker-thread/index.md +470 -0
  77. package/wiki/references/quick-reference.md +3 -18
  78. package/wiki/references/utilities/jsx.md +1 -8
  79. package/wiki/references/utilities/statuses.md +0 -7
  80. package/wiki/references/components/authentication.md +0 -476
  81. package/wiki/references/components/mail.md +0 -687
  82. package/wiki/references/components/socket-io.md +0 -562
  83. package/wiki/references/components/static-asset.md +0 -1277
  84. package/wiki/references/helpers/cron.md +0 -108
  85. package/wiki/references/helpers/crypto.md +0 -132
  86. package/wiki/references/helpers/env.md +0 -83
  87. package/wiki/references/helpers/error.md +0 -97
  88. package/wiki/references/helpers/inversion.md +0 -176
  89. package/wiki/references/helpers/logger.md +0 -296
  90. package/wiki/references/helpers/network.md +0 -396
  91. package/wiki/references/helpers/queue.md +0 -150
  92. package/wiki/references/helpers/redis.md +0 -142
  93. package/wiki/references/helpers/socket-io.md +0 -932
  94. package/wiki/references/helpers/storage.md +0 -665
  95. package/wiki/references/helpers/testing.md +0 -133
  96. package/wiki/references/helpers/types.md +0 -167
  97. package/wiki/references/helpers/uid.md +0 -167
  98. package/wiki/references/helpers/worker-thread.md +0 -178
@@ -0,0 +1,617 @@
1
+ # Static Asset
2
+
3
+ > Flexible file management system with support for multiple storage backends (local disk, MinIO/S3-compatible) through a unified interface, featuring factory-based controller generation and optional database file tracking via MetaLink.
4
+
5
+ ## Quick Reference
6
+
7
+ | Item | Value |
8
+ |------|-------|
9
+ | **Package** | `@venizia/ignis` |
10
+ | **Class** | `StaticAssetComponent` |
11
+ | **Helper** | [`DiskHelper`](/references/helpers/storage/), [`MinioHelper`](/references/helpers/storage/) |
12
+ | **Runtimes** | Both |
13
+
14
+ #### Import Paths
15
+ ```typescript
16
+ import {
17
+ StaticAssetComponent,
18
+ StaticAssetComponentBindingKeys,
19
+ StaticAssetStorageTypes,
20
+ DiskHelper,
21
+ MinioHelper,
22
+ } from '@venizia/ignis';
23
+ import type {
24
+ TStaticAssetsComponentOptions,
25
+ TMetaLinkConfig,
26
+ TStaticAssetExtraOptions,
27
+ TStaticAssetStorageType,
28
+ } from '@venizia/ignis';
29
+ ```
30
+
31
+ ### Key Features
32
+
33
+ | Feature | Description |
34
+ |---------|-------------|
35
+ | **Unified Storage Interface** | Single API for all storage types |
36
+ | **Multiple Storage Instances** | Configure multiple storage backends simultaneously |
37
+ | **Factory Pattern** | Dynamic controller generation per storage backend |
38
+ | **Built-in Security** | Comprehensive name validation, path traversal protection, header sanitization |
39
+ | **Database Tracking (MetaLink)** | Optional database-backed file tracking with metadata, principal association, and sync status |
40
+ | **Flexible Configuration** | Environment-based, production-ready setup |
41
+
42
+ ## Setup
43
+
44
+ ### Step 1: Bind Configuration
45
+
46
+ ```typescript
47
+ import {
48
+ BaseApplication,
49
+ DiskHelper,
50
+ MinioHelper,
51
+ StaticAssetComponentBindingKeys,
52
+ StaticAssetStorageTypes,
53
+ TStaticAssetsComponentOptions,
54
+ } from '@venizia/ignis';
55
+
56
+ export class Application extends BaseApplication {
57
+ preConfigure() {
58
+ this.bind<TStaticAssetsComponentOptions>({
59
+ key: StaticAssetComponentBindingKeys.STATIC_ASSET_COMPONENT_OPTIONS,
60
+ }).toValue({
61
+ // MinIO storage for user uploads
62
+ staticAsset: {
63
+ controller: {
64
+ name: 'AssetController',
65
+ basePath: '/assets',
66
+ isStrict: true,
67
+ },
68
+ storage: StaticAssetStorageTypes.MINIO,
69
+ helper: new MinioHelper({
70
+ endPoint: 'localhost',
71
+ port: 9000,
72
+ accessKey: 'minioadmin',
73
+ secretKey: 'minioadmin',
74
+ useSSL: false,
75
+ }),
76
+ extra: {
77
+ parseMultipartBody: { storage: 'memory' },
78
+ },
79
+ },
80
+ // Local disk storage for temporary files
81
+ staticResource: {
82
+ controller: {
83
+ name: 'ResourceController',
84
+ basePath: '/resources',
85
+ isStrict: true,
86
+ },
87
+ storage: StaticAssetStorageTypes.DISK,
88
+ helper: new DiskHelper({
89
+ basePath: './app_data/resources',
90
+ }),
91
+ extra: {
92
+ parseMultipartBody: { storage: 'memory' },
93
+ },
94
+ },
95
+ });
96
+ }
97
+ }
98
+ ```
99
+
100
+ Each storage backend gets a unique key (`staticAsset`, `staticResource`), its own controller configuration, and a helper instance.
101
+
102
+ ### Step 2: Register Component
103
+
104
+ ```typescript
105
+ import { StaticAssetComponent } from '@venizia/ignis';
106
+
107
+ export class Application extends BaseApplication {
108
+ preConfigure() {
109
+ // ... Step 1 binding ...
110
+ this.component(StaticAssetComponent);
111
+ }
112
+ }
113
+ ```
114
+
115
+ ### Step 3: Use the Endpoints
116
+
117
+ The component auto-registers REST endpoints for each configured backend. No injection needed in downstream code.
118
+
119
+ ```
120
+ GET /assets/buckets — List all buckets
121
+ GET /assets/buckets/:bucketName — Get bucket details (or null)
122
+ POST /assets/buckets/:bucketName — Create a bucket
123
+ DELETE /assets/buckets/:bucketName — Delete a bucket
124
+ POST /assets/buckets/:bucketName/upload — Upload files
125
+ GET /assets/buckets/:bucketName/objects — List objects in bucket
126
+ GET /assets/buckets/:bucketName/objects/:obj — Stream file inline
127
+ GET /assets/buckets/:bucketName/objects/:obj/download — Download file (attachment)
128
+ DELETE /assets/buckets/:bucketName/objects/:obj — Delete file
129
+ PUT /assets/buckets/:bucketName/objects/:obj/meta-links — Sync MetaLink (MetaLink only)
130
+ ```
131
+
132
+ Each storage backend gets its own base path (`/assets`, `/resources`, etc.) with the same endpoint structure.
133
+
134
+ #### Environment Variables
135
+
136
+ Add these to your `.env` file for MinIO:
137
+
138
+ ```bash
139
+ APP_ENV_MINIO_HOST=localhost
140
+ APP_ENV_MINIO_API_PORT=9000
141
+ APP_ENV_MINIO_ACCESS_KEY=minioadmin
142
+ APP_ENV_MINIO_SECRET_KEY=minioadmin
143
+ ```
144
+
145
+ #### Environment Keys Configuration
146
+
147
+ ```typescript
148
+ // src/common/environments.ts
149
+ import { EnvironmentKeys as BaseEnv } from '@venizia/ignis';
150
+
151
+ export class EnvironmentKeys extends BaseEnv {
152
+ static readonly APP_ENV_MINIO_HOST = 'APP_ENV_MINIO_HOST';
153
+ static readonly APP_ENV_MINIO_API_PORT = 'APP_ENV_MINIO_API_PORT';
154
+ static readonly APP_ENV_MINIO_ACCESS_KEY = 'APP_ENV_MINIO_ACCESS_KEY';
155
+ static readonly APP_ENV_MINIO_SECRET_KEY = 'APP_ENV_MINIO_SECRET_KEY';
156
+ }
157
+ ```
158
+
159
+ #### Docker Compose for MinIO
160
+
161
+ ```yaml
162
+ version: '3.8'
163
+ services:
164
+ minio:
165
+ image: minio/minio:latest
166
+ container_name: minio
167
+ ports:
168
+ - "9000:9000" # API port
169
+ - "9001:9001" # Console port
170
+ environment:
171
+ MINIO_ROOT_USER: minioadmin
172
+ MINIO_ROOT_PASSWORD: minioadmin
173
+ command: server /data --console-address ":9001"
174
+ volumes:
175
+ - minio_data:/data
176
+
177
+ volumes:
178
+ minio_data:
179
+ ```
180
+
181
+ Start with `docker-compose up -d` and access the console at `http://localhost:9001`.
182
+
183
+ ## Configuration
184
+
185
+ ### Storage Types
186
+
187
+ | Type | Constant | Helper | Description |
188
+ |------|----------|--------|-------------|
189
+ | `'disk'` | `StaticAssetStorageTypes.DISK` | `DiskHelper` | Local filesystem with bucket-based directory structure |
190
+ | `'minio'` | `StaticAssetStorageTypes.MINIO` | `MinioHelper` | S3-compatible object storage (MinIO, AWS S3, etc.) |
191
+
192
+ The `StaticAssetStorageTypes` class provides a `SCHEME_SET` (a `Set` of all valid storage type strings) and an `isValid(orgType)` method for runtime validation:
193
+
194
+ ```typescript
195
+ StaticAssetStorageTypes.isValid('minio'); // true
196
+ StaticAssetStorageTypes.isValid('s3'); // false
197
+ StaticAssetStorageTypes.SCHEME_SET; // Set { 'disk', 'minio' }
198
+ ```
199
+
200
+ ### `TStaticAssetsComponentOptions`
201
+
202
+ Each key in the options object defines a separate storage backend with its own controller:
203
+
204
+ | Option | Type | Default | Description |
205
+ |--------|------|---------|-------------|
206
+ | `controller.name` | `string` | -- | Controller class name |
207
+ | `controller.basePath` | `string` | -- | Base URL path (e.g., `'/assets'`) |
208
+ | `controller.isStrict` | `boolean` | `true` | Strict routing mode |
209
+ | `storage` | `'disk' \| 'minio'` | -- | Storage type |
210
+ | `helper` | `DiskHelper \| MinioHelper` | -- | Storage helper instance |
211
+ | `extra` | `TStaticAssetExtraOptions` | `undefined` | Extra options (multipart parsing, name normalization) |
212
+ | `useMetaLink` | `boolean` | `false` | Enable database file tracking |
213
+ | `metaLink` | `TMetaLinkConfig` | -- | MetaLink configuration (required when `useMetaLink: true`) |
214
+
215
+ #### TStaticAssetsComponentOptions -- Full Reference
216
+ ```typescript
217
+ type TStaticAssetsComponentOptions = {
218
+ [key: string]: {
219
+ controller: {
220
+ name: string;
221
+ basePath: string;
222
+ isStrict?: boolean;
223
+ };
224
+ extra?: TStaticAssetExtraOptions;
225
+ } & (
226
+ | { storage: typeof StaticAssetStorageTypes.DISK; helper: DiskHelper }
227
+ | { storage: typeof StaticAssetStorageTypes.MINIO; helper: MinioHelper }
228
+ ) &
229
+ ({ useMetaLink?: false | undefined } | { useMetaLink: true; metaLink: TMetaLinkConfig });
230
+ };
231
+
232
+ type TStaticAssetExtraOptions = {
233
+ parseMultipartBody?: {
234
+ storage?: 'memory' | 'disk';
235
+ uploadDir?: string;
236
+ };
237
+ normalizeNameFn?: (opts: { originalName: string; folderPath?: string }) => string;
238
+ normalizeLinkFn?: (opts: { bucketName: string; normalizeName: string }) => string;
239
+ [key: string]: any;
240
+ };
241
+
242
+ type TMetaLinkConfig<Schema extends TMetaLinkSchema = TMetaLinkSchema> = {
243
+ model: typeof BaseEntity<Schema>;
244
+ repository: DefaultCRUDRepository<Schema>;
245
+ };
246
+ ```
247
+
248
+ ### DiskHelper
249
+
250
+ Stores files on the local filesystem using a bucket-based directory structure.
251
+
252
+ ```typescript
253
+ new DiskHelper({
254
+ basePath: string; // Base directory for storage
255
+ scope?: string; // Logger scope
256
+ identifier?: string; // Helper identifier
257
+ })
258
+ ```
259
+
260
+ **Example:**
261
+
262
+ ```typescript
263
+ const diskHelper = new DiskHelper({
264
+ basePath: './app_data/storage',
265
+ });
266
+ ```
267
+
268
+ **Directory structure:**
269
+ ```
270
+ app_data/storage/
271
+ ├── bucket-1/
272
+ │ ├── file1.pdf
273
+ │ └── file2.jpg
274
+ ├── bucket-2/
275
+ │ └── document.docx
276
+ ```
277
+
278
+ Features: automatic directory creation, built-in path validation, metadata from file stats, stream-based operations.
279
+
280
+ ### MinioHelper
281
+
282
+ Connects to MinIO or any S3-compatible object storage.
283
+
284
+ ```typescript
285
+ new MinioHelper({
286
+ endPoint: string; // MinIO server hostname
287
+ port: number; // API port (default: 9000)
288
+ useSSL: boolean; // Use HTTPS
289
+ accessKey: string; // Access key
290
+ secretKey: string; // Secret key
291
+ })
292
+ ```
293
+
294
+ **Example:**
295
+
296
+ ```typescript
297
+ const minioHelper = new MinioHelper({
298
+ endPoint: 'minio.example.com',
299
+ port: 9000,
300
+ useSSL: true,
301
+ accessKey: process.env.MINIO_ACCESS_KEY,
302
+ secretKey: process.env.MINIO_SECRET_KEY,
303
+ });
304
+ ```
305
+
306
+ ### MetaLink Configuration
307
+
308
+ MetaLink is an optional feature that tracks uploaded files in a database, storing file location, metadata (mimetype, size, etag), storage type, principal association (`principalType`, `principalId`), timestamps, and custom metadata (JSONB).
309
+
310
+ #### Benefits
311
+
312
+ - Query uploaded files by bucket, name, mimetype, etc.
313
+ - Track file history and audit trails
314
+ - Store custom metadata about files
315
+ - Associate files with principals via `principalType` and `principalId` (passed as query parameters on the upload endpoint)
316
+ - Graceful errors -- upload succeeds even if MetaLink creation fails
317
+
318
+ #### Setup
319
+
320
+ **1. Create Model:**
321
+
322
+ ```typescript
323
+ import { BaseMetaLinkModel, model } from '@venizia/ignis';
324
+
325
+ @model({ type: 'entity' })
326
+ export class FileMetaLinkModel extends BaseMetaLinkModel {
327
+ // Inherits all fields from BaseMetaLinkModel
328
+ }
329
+ ```
330
+
331
+ **2. Create Repository:**
332
+
333
+ ```typescript
334
+ import { BaseMetaLinkRepository, repository, inject, IDataSource } from '@venizia/ignis';
335
+
336
+ @repository({})
337
+ export class FileMetaLinkRepository extends BaseMetaLinkRepository {
338
+ constructor(@inject({ key: 'datasources.postgres' }) dataSource: IDataSource) {
339
+ super({
340
+ entityClass: FileMetaLinkModel,
341
+ relations: {},
342
+ dataSource,
343
+ });
344
+ }
345
+ }
346
+ ```
347
+
348
+ **3. Create Database Table:**
349
+
350
+ The model has `skipMigrate: true`, so create the table manually:
351
+
352
+ ```sql
353
+ CREATE TABLE "MetaLink" (
354
+ id TEXT PRIMARY KEY,
355
+ created_at TIMESTAMP NOT NULL DEFAULT NOW(),
356
+ modified_at TIMESTAMP NOT NULL DEFAULT NOW(),
357
+ bucket_name TEXT NOT NULL,
358
+ object_name TEXT NOT NULL,
359
+ link TEXT NOT NULL,
360
+ mimetype TEXT NOT NULL,
361
+ size INTEGER NOT NULL,
362
+ etag TEXT,
363
+ metadata JSONB,
364
+ storage_type TEXT NOT NULL,
365
+ is_synced BOOLEAN NOT NULL DEFAULT false,
366
+ principal_type TEXT,
367
+ principal_id TEXT
368
+ );
369
+
370
+ CREATE INDEX "IDX_MetaLink_bucketName" ON "MetaLink"(bucket_name);
371
+ CREATE INDEX "IDX_MetaLink_objectName" ON "MetaLink"(object_name);
372
+ CREATE INDEX "IDX_MetaLink_storageType" ON "MetaLink"(storage_type);
373
+ CREATE INDEX "IDX_MetaLink_isSynced" ON "MetaLink"(is_synced);
374
+ ```
375
+
376
+ **4. Configure Component:**
377
+
378
+ ```typescript
379
+ import { FileMetaLinkModel, FileMetaLinkRepository } from './your-models';
380
+
381
+ export class Application extends BaseApplication {
382
+ configureComponents(): void {
383
+ this.repository(FileMetaLinkRepository);
384
+
385
+ this.bind<TStaticAssetsComponentOptions>({
386
+ key: StaticAssetComponentBindingKeys.STATIC_ASSET_COMPONENT_OPTIONS,
387
+ }).toValue({
388
+ uploads: {
389
+ controller: {
390
+ name: 'UploadController',
391
+ basePath: '/uploads',
392
+ isStrict: true,
393
+ },
394
+ storage: StaticAssetStorageTypes.MINIO,
395
+ helper: new MinioHelper({ /* ... */ }),
396
+ useMetaLink: true,
397
+ metaLink: {
398
+ model: FileMetaLinkModel,
399
+ repository: this.getSync(FileMetaLinkRepository),
400
+ },
401
+ extra: {
402
+ parseMultipartBody: { storage: 'memory' },
403
+ },
404
+ },
405
+ });
406
+
407
+ this.component(StaticAssetComponent);
408
+ }
409
+ }
410
+ ```
411
+
412
+ **5. Upload with Principal Association:**
413
+
414
+ When MetaLink is enabled, you can associate uploaded files with a principal (user, service, etc.) by passing query parameters on the upload endpoint:
415
+
416
+ ```typescript
417
+ const formData = new FormData();
418
+ formData.append('file', fileBlob, 'document.pdf');
419
+
420
+ // Associate the upload with a user
421
+ const response = await fetch(
422
+ '/uploads/buckets/user-files/upload?principalType=user&principalId=42',
423
+ { method: 'POST', body: formData },
424
+ );
425
+ ```
426
+
427
+ The `principalId` value is always stored as a string regardless of input type (coerced via `String()`).
428
+
429
+ #### Querying MetaLinks
430
+
431
+ ```typescript
432
+ // Get all files in a bucket
433
+ const files = await fileMetaLinkRepository.find({
434
+ where: { bucketName: 'user-uploads' },
435
+ });
436
+
437
+ // Get files by mimetype
438
+ const pdfs = await fileMetaLinkRepository.find({
439
+ where: { mimetype: 'application/pdf' },
440
+ });
441
+
442
+ // Get files by storage type
443
+ const minioFiles = await fileMetaLinkRepository.find({
444
+ where: { storageType: 'minio' },
445
+ });
446
+
447
+ // Get files by principal
448
+ const userFiles = await fileMetaLinkRepository.find({
449
+ where: { principalType: 'user', principalId: '42' },
450
+ });
451
+
452
+ // Get synced files only
453
+ const syncedFiles = await fileMetaLinkRepository.find({
454
+ where: { isSynced: true },
455
+ });
456
+
457
+ // Get unsynced files (for manual sync operations)
458
+ const unsyncedFiles = await fileMetaLinkRepository.find({
459
+ where: { isSynced: false },
460
+ });
461
+
462
+ // Count synced files
463
+ const syncedCount = await fileMetaLinkRepository.count({
464
+ where: { isSynced: true },
465
+ });
466
+
467
+ // Get recent uploads
468
+ const recent = await fileMetaLinkRepository.find({
469
+ orderBy: { createdAt: 'desc' },
470
+ limit: 10,
471
+ });
472
+ ```
473
+
474
+ ### Quick Start Options
475
+
476
+ **Option 1: MinIO Only**
477
+ ```typescript
478
+ this.bind({
479
+ key: StaticAssetComponentBindingKeys.STATIC_ASSET_COMPONENT_OPTIONS,
480
+ }).toValue({
481
+ cloudStorage: {
482
+ controller: { name: 'CloudController', basePath: '/cloud' },
483
+ storage: StaticAssetStorageTypes.MINIO,
484
+ helper: new MinioHelper({ /* ... */ }),
485
+ extra: { parseMultipartBody: { storage: 'memory' } },
486
+ },
487
+ });
488
+ this.component(StaticAssetComponent);
489
+ ```
490
+
491
+ **Option 2: Local Disk Only**
492
+ ```typescript
493
+ this.bind({
494
+ key: StaticAssetComponentBindingKeys.STATIC_ASSET_COMPONENT_OPTIONS,
495
+ }).toValue({
496
+ localStorage: {
497
+ controller: { name: 'LocalController', basePath: '/files' },
498
+ storage: StaticAssetStorageTypes.DISK,
499
+ helper: new DiskHelper({ basePath: './uploads' }),
500
+ extra: { parseMultipartBody: { storage: 'disk' } },
501
+ },
502
+ });
503
+ this.component(StaticAssetComponent);
504
+ ```
505
+
506
+ **Option 3: Multiple Storage Backends (Recommended)**
507
+ ```typescript
508
+ this.bind({
509
+ key: StaticAssetComponentBindingKeys.STATIC_ASSET_COMPONENT_OPTIONS,
510
+ }).toValue({
511
+ userUploads: {
512
+ controller: { name: 'UploadsController', basePath: '/uploads' },
513
+ storage: StaticAssetStorageTypes.MINIO,
514
+ helper: new MinioHelper({ /* ... */ }),
515
+ extra: {},
516
+ },
517
+ tempFiles: {
518
+ controller: { name: 'TempController', basePath: '/temp' },
519
+ storage: StaticAssetStorageTypes.DISK,
520
+ helper: new DiskHelper({ basePath: './temp' }),
521
+ extra: {},
522
+ },
523
+ publicAssets: {
524
+ controller: { name: 'PublicController', basePath: '/public' },
525
+ storage: StaticAssetStorageTypes.DISK,
526
+ helper: new DiskHelper({ basePath: './public' }),
527
+ extra: {},
528
+ },
529
+ });
530
+ this.component(StaticAssetComponent);
531
+ ```
532
+
533
+ ### Custom Filename Normalization
534
+
535
+ ```typescript
536
+ {
537
+ uploads: {
538
+ controller: { name: 'UploadController', basePath: '/uploads' },
539
+ storage: StaticAssetStorageTypes.MINIO,
540
+ helper: new MinioHelper({ /* ... */ }),
541
+ extra: {
542
+ parseMultipartBody: { storage: 'memory' },
543
+ normalizeNameFn: ({ originalName, folderPath }) => {
544
+ const prefix = folderPath ? `${folderPath}/` : '';
545
+ return `${prefix}${Date.now()}_${originalName.toLowerCase().replace(/\s/g, '_')}`;
546
+ },
547
+ normalizeLinkFn: ({ bucketName, normalizeName }) => {
548
+ return `/api/files/${bucketName}/${encodeURIComponent(normalizeName)}`;
549
+ },
550
+ },
551
+ },
552
+ }
553
+ ```
554
+
555
+ The `normalizeNameFn` receives both the `originalName` and an optional `folderPath` from the uploaded file. The `folderPath` is passed through from the `IUploadFile` object and can be used to organize files into subdirectories.
556
+
557
+ ### Custom Storage Implementation
558
+
559
+ You can implement your own storage backend by extending `BaseStorageHelper`:
560
+
561
+ ```typescript
562
+ import { BaseStorageHelper, IUploadFile, IUploadResult } from '@venizia/ignis-helpers';
563
+
564
+ class S3Helper extends BaseStorageHelper {
565
+ constructor(config: S3Config) {
566
+ super({ scope: 'S3Helper', identifier: 'S3Helper' });
567
+ // Initialize S3 client
568
+ }
569
+
570
+ async isBucketExists(opts: { name: string }): Promise<boolean> {
571
+ // Implementation
572
+ }
573
+
574
+ async upload(opts: {
575
+ bucket: string;
576
+ files: IUploadFile[];
577
+ normalizeNameFn?: (opts: { originalName: string; folderPath?: string }) => string;
578
+ normalizeLinkFn?: (opts: { bucketName: string; normalizeName: string }) => string;
579
+ }): Promise<IUploadResult[]> {
580
+ // Implementation
581
+ }
582
+
583
+ // Implement other IStorageHelper methods...
584
+ }
585
+
586
+ // Usage
587
+ this.bind<TStaticAssetsComponentOptions>({
588
+ key: StaticAssetComponentBindingKeys.STATIC_ASSET_COMPONENT_OPTIONS,
589
+ }).toValue({
590
+ s3Storage: {
591
+ controller: { name: 'S3Controller', basePath: '/s3-assets' },
592
+ storage: 'custom-s3',
593
+ helper: new S3Helper({ /* ... */ }),
594
+ extra: {},
595
+ },
596
+ });
597
+ ```
598
+
599
+ ## Binding Keys
600
+
601
+ | Key | Constant | Type | Required | Default |
602
+ |-----|----------|------|----------|---------|
603
+ | `@app/static-asset-component/options` | `StaticAssetComponentBindingKeys.STATIC_ASSET_COMPONENT_OPTIONS` | `TStaticAssetsComponentOptions` | Yes | `{}` |
604
+
605
+ > [!NOTE]
606
+ > The component provides an empty default binding. You must bind this key with your storage configuration before registering the component.
607
+
608
+ ## See Also
609
+
610
+ - [Usage & Examples](./usage) - API Endpoints and Frontend Integration
611
+ - [API Reference](./api) - Controller Factory, Storage Interface, MetaLink Schema
612
+ - [Error Reference](./errors) - Name Validation and Troubleshooting
613
+ - [Storage Helpers](/references/helpers/storage/) - DiskHelper, MinioHelper, BaseStorageHelper
614
+ - [Request Utilities](/references/utilities/request) - File upload utilities
615
+ - [Security Guidelines](/best-practices/security-guidelines) - File upload security
616
+ - [Components Overview](/guides/core-concepts/components) - Component system basics
617
+ - [Controllers](/guides/core-concepts/controllers) - File upload endpoints