@venizia/ignis-docs 0.2.0 → 0.2.1-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.
Files changed (174) hide show
  1. package/README.md +14 -14
  2. package/content/best-practices/api-usage-examples.md +39 -19
  3. package/content/best-practices/architectural-patterns.md +24 -13
  4. package/content/best-practices/architecture-decisions.md +29 -16
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
  6. package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
  7. package/content/best-practices/code-style-standards/control-flow.md +33 -1
  8. package/content/best-practices/code-style-standards/documentation.md +28 -8
  9. package/content/best-practices/code-style-standards/function-patterns.md +15 -4
  10. package/content/best-practices/code-style-standards/index.md +7 -2
  11. package/content/best-practices/code-style-standards/naming-conventions.md +27 -3
  12. package/content/best-practices/code-style-standards/route-definitions.md +9 -5
  13. package/content/best-practices/code-style-standards/tooling.md +14 -1
  14. package/content/best-practices/code-style-standards/type-safety.md +39 -6
  15. package/content/best-practices/common-pitfalls.md +59 -35
  16. package/content/best-practices/contribution-workflow.md +8 -4
  17. package/content/best-practices/data-modeling.md +78 -62
  18. package/content/best-practices/deployment-strategies.md +56 -19
  19. package/content/best-practices/error-handling.md +247 -153
  20. package/content/best-practices/index.md +6 -0
  21. package/content/best-practices/performance-optimization.md +26 -13
  22. package/content/best-practices/security-guidelines.md +35 -9
  23. package/content/best-practices/testing-strategies.md +7 -2
  24. package/content/best-practices/troubleshooting-tips.md +27 -17
  25. package/content/extensions/components/api-reference.md +109 -323
  26. package/content/extensions/components/authentication/api.md +489 -602
  27. package/content/extensions/components/authentication/errors.md +135 -498
  28. package/content/extensions/components/authentication/index.md +89 -801
  29. package/content/extensions/components/authentication/usage.md +231 -955
  30. package/content/extensions/components/authorization/api.md +991 -644
  31. package/content/extensions/components/authorization/errors.md +204 -208
  32. package/content/extensions/components/authorization/getting-started.md +227 -0
  33. package/content/extensions/components/authorization/index.md +88 -795
  34. package/content/extensions/components/authorization/usage.md +219 -528
  35. package/content/extensions/components/health-check.md +77 -243
  36. package/content/extensions/components/index.md +24 -90
  37. package/content/extensions/components/mail/api.md +553 -285
  38. package/content/extensions/components/mail/errors.md +76 -62
  39. package/content/extensions/components/mail/index.md +111 -463
  40. package/content/extensions/components/mail/usage.md +139 -173
  41. package/content/extensions/components/request-tracker.md +70 -173
  42. package/content/extensions/components/socket-io/api.md +462 -785
  43. package/content/extensions/components/socket-io/errors.md +49 -51
  44. package/content/extensions/components/socket-io/index.md +69 -372
  45. package/content/extensions/components/socket-io/usage.md +212 -105
  46. package/content/extensions/components/static-asset/api.md +461 -141
  47. package/content/extensions/components/static-asset/errors.md +121 -53
  48. package/content/extensions/components/static-asset/index.md +84 -606
  49. package/content/extensions/components/static-asset/usage.md +184 -299
  50. package/content/extensions/components/template/index.md +3 -3
  51. package/content/extensions/components/websocket/api.md +311 -404
  52. package/content/extensions/components/websocket/errors.md +47 -56
  53. package/content/extensions/components/websocket/index.md +75 -407
  54. package/content/extensions/components/websocket/usage.md +120 -338
  55. package/content/extensions/helpers/cron/index.md +52 -160
  56. package/content/extensions/helpers/crypto/index.md +67 -480
  57. package/content/extensions/helpers/crypto/reference.md +528 -0
  58. package/content/extensions/helpers/env/index.md +64 -178
  59. package/content/extensions/helpers/error/index.md +286 -196
  60. package/content/extensions/helpers/index.md +61 -47
  61. package/content/extensions/helpers/inversion/index.md +76 -550
  62. package/content/extensions/helpers/inversion/reference.md +530 -0
  63. package/content/extensions/helpers/kafka/admin.md +23 -3
  64. package/content/extensions/helpers/kafka/compile-binary.md +83 -52
  65. package/content/extensions/helpers/kafka/consumer.md +65 -28
  66. package/content/extensions/helpers/kafka/examples.md +46 -253
  67. package/content/extensions/helpers/kafka/index.md +55 -617
  68. package/content/extensions/helpers/kafka/producer.md +156 -22
  69. package/content/extensions/helpers/kafka/schema-registry.md +59 -79
  70. package/content/extensions/helpers/logger/hf-logger.md +220 -0
  71. package/content/extensions/helpers/logger/index.md +78 -552
  72. package/content/extensions/helpers/logger/pino.md +105 -0
  73. package/content/extensions/helpers/logger/reference.md +937 -0
  74. package/content/extensions/helpers/network/api.md +276 -195
  75. package/content/extensions/helpers/network/index.md +87 -524
  76. package/content/extensions/helpers/queue/index.md +80 -897
  77. package/content/extensions/helpers/queue/reference.md +494 -0
  78. package/content/extensions/helpers/redis/index.md +86 -640
  79. package/content/extensions/helpers/redis/reference.md +784 -0
  80. package/content/extensions/helpers/secrets/index.md +136 -0
  81. package/content/extensions/helpers/socket-io/api.md +325 -204
  82. package/content/extensions/helpers/socket-io/index.md +79 -429
  83. package/content/extensions/helpers/storage/api.md +584 -464
  84. package/content/extensions/helpers/storage/index.md +80 -573
  85. package/content/extensions/helpers/types/index.md +75 -495
  86. package/content/extensions/helpers/types/reference.md +689 -0
  87. package/content/extensions/helpers/uid/index.md +176 -189
  88. package/content/extensions/helpers/websocket/api.md +366 -214
  89. package/content/extensions/helpers/websocket/index.md +68 -503
  90. package/content/extensions/helpers/worker-thread/index.md +62 -396
  91. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  92. package/content/extensions/index.md +38 -39
  93. package/content/extensions/src-details/mcp-server.md +96 -548
  94. package/content/guides/core-concepts/persistent/datasources.md +2 -2
  95. package/content/guides/core-concepts/persistent/index.md +5 -1
  96. package/content/guides/core-concepts/persistent/models.md +1 -1
  97. package/content/guides/core-concepts/persistent/pglite.md +218 -0
  98. package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
  99. package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
  100. package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
  101. package/content/guides/core-concepts/persistent/sqlite.md +296 -0
  102. package/content/guides/core-concepts/persistent/transactions.md +12 -11
  103. package/content/guides/core-concepts/secrets-vault.md +177 -0
  104. package/content/guides/core-concepts/services.md +1 -1
  105. package/content/guides/get-started/5-minute-quickstart.md +59 -206
  106. package/content/guides/get-started/philosophy.md +135 -670
  107. package/content/guides/get-started/setup.md +53 -74
  108. package/content/guides/migrations/redis-helpers-migration.md +5 -4
  109. package/content/guides/migrations/scoped-rbac-migration.md +5 -5
  110. package/content/guides/migrations/unified-connectors-migration.md +8 -7
  111. package/content/guides/tutorials/ecommerce-api.md +3 -8
  112. package/content/guides/tutorials/realtime-chat.md +1 -1
  113. package/content/references/base/application.md +64 -22
  114. package/content/references/base/components.md +3 -3
  115. package/content/references/base/connectors.md +79 -136
  116. package/content/references/base/controllers.md +12 -12
  117. package/content/references/base/datasources-reference.md +600 -0
  118. package/content/references/base/datasources.md +84 -444
  119. package/content/references/base/dependency-injection.md +25 -46
  120. package/content/references/base/filter-system/application-usage.md +95 -123
  121. package/content/references/base/filter-system/array-operators.md +33 -43
  122. package/content/references/base/filter-system/comparison-operators.md +55 -63
  123. package/content/references/base/filter-system/default-filter.md +144 -350
  124. package/content/references/base/filter-system/fields-order-pagination.md +116 -148
  125. package/content/references/base/filter-system/index.md +114 -253
  126. package/content/references/base/filter-system/json-filtering.md +52 -181
  127. package/content/references/base/filter-system/list-operators.md +28 -44
  128. package/content/references/base/filter-system/logical-operators.md +69 -114
  129. package/content/references/base/filter-system/null-operators.md +41 -98
  130. package/content/references/base/filter-system/pattern-matching.md +48 -51
  131. package/content/references/base/filter-system/quick-reference.md +93 -196
  132. package/content/references/base/filter-system/range-operators.md +22 -38
  133. package/content/references/base/filter-system/tips.md +70 -133
  134. package/content/references/base/filter-system/use-cases.md +173 -232
  135. package/content/references/base/grpc-controllers.md +53 -17
  136. package/content/references/base/index.md +5 -3
  137. package/content/references/base/middlewares.md +43 -28
  138. package/content/references/base/models-reference.md +886 -0
  139. package/content/references/base/models.md +81 -1452
  140. package/content/references/base/providers.md +8 -8
  141. package/content/references/base/repositories/advanced.md +281 -419
  142. package/content/references/base/repositories/index.md +84 -644
  143. package/content/references/base/repositories/mixins.md +25 -21
  144. package/content/references/base/repositories/relations.md +194 -375
  145. package/content/references/base/repositories/soft-deletable.md +67 -55
  146. package/content/references/base/secrets.md +267 -0
  147. package/content/references/base/services.md +8 -6
  148. package/content/references/configuration/environment-variables.md +73 -21
  149. package/content/references/configuration/index.md +51 -31
  150. package/content/references/index.md +1 -1
  151. package/content/references/quick-reference.md +3 -16
  152. package/content/references/utilities/crypto.md +35 -76
  153. package/content/references/utilities/date.md +33 -73
  154. package/content/references/utilities/duration.md +85 -0
  155. package/content/references/utilities/index.md +6 -2
  156. package/content/references/utilities/jsx-reference.md +298 -0
  157. package/content/references/utilities/jsx.md +82 -525
  158. package/content/references/utilities/module.md +81 -61
  159. package/content/references/utilities/parse.md +34 -64
  160. package/content/references/utilities/performance.md +33 -58
  161. package/content/references/utilities/promise.md +28 -62
  162. package/content/references/utilities/request.md +58 -218
  163. package/content/references/utilities/retry.md +139 -0
  164. package/content/references/utilities/schema.md +43 -137
  165. package/content/references/utilities/statuses-reference.md +361 -0
  166. package/content/references/utilities/statuses.md +63 -667
  167. package/dist/mcp-server/index.js +0 -0
  168. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  169. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
  170. package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
  171. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  172. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
  173. package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
  174. package/package.json +24 -23
@@ -1,235 +1,95 @@
1
- # Static Asset
1
+ ---
2
+ title: Static Asset Component
3
+ description: Generates bucket/object CRUD REST endpoints for disk, MinIO, and Bun S3 storage backends, with optional database-backed file tracking via MetaLink
4
+ difficulty: intermediate
5
+ ---
2
6
 
3
- > Flexible file management system with support for multiple storage backends (local disk, MinIO/S3-compatible, Bun S3) through a unified interface, featuring factory-based controller generation and optional database file tracking via MetaLink.
7
+ # Static Asset Component
4
8
 
5
- ## Quick Reference
6
-
7
- | Item | Value |
8
- |------|-------|
9
- | **Package** | `@venizia/ignis` |
10
- | **Class** | `StaticAssetComponent` |
11
- | **Helper** | [`DiskHelper`](/extensions/helpers/storage/), [`MinioHelper`](/extensions/helpers/storage/), [`BunS3Helper`](/extensions/helpers/storage/) |
12
- | **Runtimes** | Both |
13
-
14
- #### Import Paths
9
+ `StaticAssetComponent` reads a map of storage backend configurations and generates a full bucket/object REST controller for each one - disk, MinIO, or Bun S3 - all through the same [`IStorageHelper`](./api#istoragehelper-interface) contract.
15
10
 
16
11
  > [!IMPORTANT]
17
- > `StaticAssetComponent` and its related exports are **not** exported from the `@venizia/ignis` barrel. You must import from the `@venizia/ignis/static-asset` subpath.
18
-
19
- ```typescript
20
- // From core -- subpath import (NOT from '@venizia/ignis')
21
- import {
22
- StaticAssetComponent,
23
- StaticAssetComponentBindingKeys,
24
- StaticAssetStorageTypes,
25
- AssetControllerFactory,
26
- BaseMetaLinkModel,
27
- BaseMetaLinkRepository,
28
- } from '@venizia/ignis/static-asset';
29
-
30
- import { DiskHelper } from '@venizia/ignis-helpers';
31
- import { MinioHelper } from '@venizia/ignis-helpers/minio';
32
- import { BunS3Helper } from '@venizia/ignis-helpers/bun-s3';
33
-
34
- import type {
35
- TStaticAssetsComponentOptions,
36
- TMetaLinkConfig,
37
- TStaticAssetExtraOptions,
38
- TStaticAssetStorageType,
39
- } from '@venizia/ignis/static-asset';
40
- ```
41
-
42
- ### Key Features
43
-
44
- | Feature | Description |
45
- |---------|-------------|
46
- | **Unified Storage Interface** | Single API for all storage types |
47
- | **Multiple Storage Instances** | Configure multiple storage backends simultaneously |
48
- | **Factory Pattern** | Dynamic controller generation per storage backend |
49
- | **Built-in Security** | Comprehensive name validation, path traversal protection, header sanitization |
50
- | **Database Tracking (MetaLink)** | Optional database-backed file tracking with metadata, principal association, variant support, and sync status |
51
- | **Per-Route Configuration** | Override authentication, middleware, and path for individual routes |
52
- | **Flexible Configuration** | Environment-based, production-ready setup |
12
+ > `StaticAssetComponent` and its related exports are **not** on the `@venizia/ignis` root barrel. Import from the `@venizia/ignis/static-asset` subpath.
53
13
 
54
- ## Setup
55
-
56
- ### Step 1: Bind Configuration
14
+ ## In one example
57
15
 
58
16
  ```typescript
59
17
  import { BaseApplication } from '@venizia/ignis';
60
18
  import {
19
+ StaticAssetComponent,
61
20
  StaticAssetComponentBindingKeys,
62
21
  StaticAssetStorageTypes,
63
22
  } from '@venizia/ignis/static-asset';
64
- import { DiskHelper } from '@venizia/ignis-helpers';
65
- import { MinioHelper } from '@venizia/ignis-helpers/minio';
66
23
  import type { TStaticAssetsComponentOptions } from '@venizia/ignis/static-asset';
24
+ import { DiskHelper } from '@venizia/ignis-helpers';
67
25
 
68
26
  export class Application extends BaseApplication {
69
27
  preConfigure() {
70
28
  this.bind<TStaticAssetsComponentOptions>({
71
29
  key: StaticAssetComponentBindingKeys.STATIC_ASSET_COMPONENT_OPTIONS,
72
30
  }).toValue({
73
- // MinIO storage for user uploads
74
31
  staticAsset: {
75
- controller: {
76
- name: 'AssetController',
77
- basePath: '/assets',
78
- isStrict: true,
79
- },
80
- storage: StaticAssetStorageTypes.MINIO,
81
- helper: new MinioHelper({
82
- endPoint: 'localhost',
83
- port: 9000,
84
- accessKey: 'minioadmin',
85
- secretKey: 'minioadmin',
86
- useSSL: false,
87
- }),
88
- extra: {
89
- parseMultipartBody: { storage: 'memory' },
90
- },
91
- },
92
- // Local disk storage for temporary files
93
- staticResource: {
94
- controller: {
95
- name: 'ResourceController',
96
- basePath: '/resources',
97
- isStrict: true,
98
- },
32
+ controller: { name: 'AssetController', basePath: '/assets', isStrict: true },
99
33
  storage: StaticAssetStorageTypes.DISK,
100
- helper: new DiskHelper({
101
- basePath: './app_data/resources',
102
- }),
103
- extra: {
104
- parseMultipartBody: { storage: 'memory' },
105
- },
34
+ helper: new DiskHelper({ basePath: './app_data/assets' }),
35
+ extra: { parseMultipartBody: { storage: 'memory' } },
106
36
  },
107
37
  });
108
- }
109
- }
110
- ```
111
38
 
112
- Each storage backend gets a unique key (`staticAsset`, `staticResource`), its own controller configuration, and a helper instance.
113
-
114
- ### Step 2: Register Component
115
-
116
- ```typescript
117
- import { StaticAssetComponent } from '@venizia/ignis/static-asset';
118
-
119
- export class Application extends BaseApplication {
120
- preConfigure() {
121
- // ... Step 1 binding ...
122
39
  this.component(StaticAssetComponent);
123
40
  }
124
41
  }
125
42
  ```
126
43
 
127
- ### Step 3: Use the Endpoints
44
+ This registers `GET`/`POST`/`DELETE` on `/assets/buckets/:bucketName`, `POST /assets/buckets/:bucketName/upload`, `GET /assets/buckets/:bucketName/objects`, plus stream/download/delete-object routes - no controller class to write by hand.
128
45
 
129
- The component auto-registers REST endpoints for each configured backend. No injection needed in downstream code.
46
+ ## How it works
130
47
 
131
- ```
132
- GET /assets/buckets - List all buckets
133
- GET /assets/buckets/:bucketName - Get bucket details (or null)
134
- POST /assets/buckets/:bucketName - Create a bucket
135
- DELETE /assets/buckets/:bucketName - Delete a bucket
136
- POST /assets/buckets/:bucketName/upload - Upload files
137
- GET /assets/buckets/:bucketName/objects - List objects in bucket
138
- GET /assets/buckets/:bucketName/objects/:obj - Stream file inline
139
- GET /assets/buckets/:bucketName/download/:obj - Download file (attachment)
140
- DELETE /assets/buckets/:bucketName/objects/:obj - Delete file
141
- PUT /assets/buckets/:bucketName/meta-links/:obj - Sync MetaLink (MetaLink only)
142
- ```
48
+ - **One options key, one generated controller.** `TStaticAssetsComponentOptions` is a record. Each key (`staticAsset` above) becomes an independently configured storage backend with its own `basePath`. `AssetControllerFactory.defineAssetController()` builds the controller inside `StaticAssetComponent.binding()`.
49
+ - **`storage` and `helper` are a discriminated pair.** Mismatching them fails at compile time, not at runtime:
143
50
 
144
- Each storage backend gets its own base path (`/assets`, `/resources`, etc.) with the same endpoint structure.
51
+ | `storage` | Required `helper` |
52
+ |-----------|--------------------|
53
+ | `'disk'` | `DiskHelper` |
54
+ | `'minio'` | `MinioHelper` |
55
+ | `'bun-s3'` | `BunS3Helper` |
145
56
 
146
- #### Environment Variables
57
+ - **Every backend implements the same `IStorageHelper` contract.** `DiskHelper`, `MinioHelper`, and `BunS3Helper` all extend `BaseStorageHelper`. Bucket/object operations, name validation (`isValidName`/`isValidPath`), and upload normalization behave identically regardless of backend.
58
+ - **Object names can embed folder paths, encoded as one segment.** `objects/{objectName}` percent-encodes the whole `folder/file.ext` string via `encodeURIComponent()`, which also escapes `/`. Hono decodes it before your handler runs - encode the name client-side, and never decode it again.
59
+ - **MetaLink is opt-in.** Set `useMetaLink: true` and provide `metaLink.repository`. IGNIS then persists a database row (`bucket/object/mimetype/size/etag/principal/variant`) alongside every upload. That row lets you query "which files does user X own" without listing a whole bucket.
60
+ - **The default binding is empty.** `StaticAssetComponentBindingKeys.STATIC_ASSET_COMPONENT_OPTIONS` defaults to `{}`. Bind it with at least one storage backend before `this.component(StaticAssetComponent)` - an empty binding produces zero routes.
147
61
 
148
- Add these to your `.env` file for MinIO:
62
+ ## Common tasks
149
63
 
150
- ```bash
151
- APP_ENV_MINIO_HOST=localhost
152
- APP_ENV_MINIO_API_PORT=9000
153
- APP_ENV_MINIO_ACCESS_KEY=minioadmin
154
- APP_ENV_MINIO_SECRET_KEY=minioadmin
155
- ```
64
+ ### Add a second storage backend
156
65
 
157
- #### Environment Keys Configuration
66
+ Each key in the options object is independent - mix disk and MinIO under different base paths in one binding.
158
67
 
159
68
  ```typescript
160
- // src/common/environments.ts
161
- import { EnvironmentKeys as BaseEnv } from '@venizia/ignis';
162
-
163
- export class EnvironmentKeys extends BaseEnv {
164
- static readonly APP_ENV_MINIO_HOST = 'APP_ENV_MINIO_HOST';
165
- static readonly APP_ENV_MINIO_API_PORT = 'APP_ENV_MINIO_API_PORT';
166
- static readonly APP_ENV_MINIO_ACCESS_KEY = 'APP_ENV_MINIO_ACCESS_KEY';
167
- static readonly APP_ENV_MINIO_SECRET_KEY = 'APP_ENV_MINIO_SECRET_KEY';
168
- }
169
- ```
170
-
171
- #### Docker Compose for MinIO
172
-
173
- ```yaml
174
- version: '3.8'
175
- services:
176
- minio:
177
- image: minio/minio:latest
178
- container_name: minio
179
- ports:
180
- - "9000:9000" # API port
181
- - "9001:9001" # Console port
182
- environment:
183
- MINIO_ROOT_USER: minioadmin
184
- MINIO_ROOT_PASSWORD: minioadmin
185
- command: server /data --console-address ":9001"
186
- volumes:
187
- - minio_data:/data
188
-
189
- volumes:
190
- minio_data:
191
- ```
192
-
193
- Start with `docker-compose up -d` and access the console at `http://localhost:9001`.
194
-
195
- ## Configuration
196
-
197
- ### Storage Types
198
-
199
- | Type | Constant | Helper | Description |
200
- |------|----------|--------|-------------|
201
- | `'disk'` | `StaticAssetStorageTypes.DISK` | `DiskHelper` | Local filesystem with bucket-based directory structure |
202
- | `'minio'` | `StaticAssetStorageTypes.MINIO` | `MinioHelper` | S3-compatible object storage (MinIO, AWS S3, etc.) |
203
- | `'bun-s3'` | `StaticAssetStorageTypes.BUN_S3` | `BunS3Helper` | Bun-native S3 client (requires Bun runtime) |
204
-
205
- The `StaticAssetStorageTypes` class provides a `SCHEME_SET` (a `Set` of all valid storage type strings) and an `isValid(orgType)` method for runtime validation:
69
+ import { MinioHelper } from '@venizia/ignis-helpers/minio';
206
70
 
207
- ```typescript
208
- StaticAssetStorageTypes.isValid('minio'); // true
209
- StaticAssetStorageTypes.isValid('bun-s3'); // true
210
- StaticAssetStorageTypes.isValid('s3'); // false
211
- StaticAssetStorageTypes.SCHEME_SET; // Set { 'disk', 'minio', 'bun-s3' }
71
+ this.bind<TStaticAssetsComponentOptions>({
72
+ key: StaticAssetComponentBindingKeys.STATIC_ASSET_COMPONENT_OPTIONS,
73
+ }).toValue({
74
+ uploads: {
75
+ controller: { name: 'UploadsController', basePath: '/uploads' },
76
+ storage: StaticAssetStorageTypes.MINIO,
77
+ helper: new MinioHelper({
78
+ endPoint: 'localhost', port: 9000, useSSL: false,
79
+ accessKey: 'minioadmin', secretKey: 'minioadmin',
80
+ }),
81
+ },
82
+ tempFiles: {
83
+ controller: { name: 'TempController', basePath: '/temp' },
84
+ storage: StaticAssetStorageTypes.DISK,
85
+ helper: new DiskHelper({ basePath: './temp' }),
86
+ },
87
+ });
212
88
  ```
213
89
 
214
- ### `TStaticAssetsComponentOptions`
90
+ ### Lock down individual routes
215
91
 
216
- Each key in the options object defines a separate storage backend with its own controller:
217
-
218
- | Option | Type | Default | Description |
219
- |--------|------|---------|-------------|
220
- | `controller.name` | `string` | -- | Controller class name |
221
- | `controller.basePath` | `string` | -- | Base URL path (e.g., `'/assets'`) |
222
- | `controller.isStrict` | `boolean` | `true` | Strict routing mode |
223
- | `controller.routes` | `object` | `undefined` | Per-route overrides (authenticate, middleware, path) |
224
- | `storage` | `'disk' \| 'minio' \| 'bun-s3'` | -- | Storage type |
225
- | `helper` | `DiskHelper \| MinioHelper \| BunS3Helper` | -- | Storage helper instance |
226
- | `extra` | `TStaticAssetExtraOptions` | `undefined` | Extra options (multipart parsing, name normalization) |
227
- | `useMetaLink` | `boolean` | `false` | Enable database file tracking |
228
- | `metaLink` | `TMetaLinkConfig` | -- | MetaLink configuration (required when `useMetaLink: true`) |
229
-
230
- #### Per-Route Configuration
231
-
232
- Each route can be individually configured with authentication, middleware, and path overrides:
92
+ Each key under `controller.routes` accepts a partial `authenticate`/`authorize`/`middleware`/`path` override; unset routes stay public.
233
93
 
234
94
  ```typescript
235
95
  {
@@ -237,446 +97,64 @@ Each route can be individually configured with authentication, middleware, and p
237
97
  name: 'AssetController',
238
98
  basePath: '/assets',
239
99
  routes: {
240
- getBuckets: { authenticate: { strategies: ['jwt'], mode: 'any' } },
241
- upload: { authenticate: { strategies: ['jwt'], mode: 'any' }, middleware: [rateLimitMw] },
242
- getObjectByName: { /* public -- no authenticate */ },
243
- downloadObjectByName: { /* public */ },
244
- deleteObject: { authenticate: { strategies: ['jwt'], mode: 'any' } },
245
- deleteBucket: { authenticate: { strategies: ['jwt'], mode: 'any' } },
100
+ upload: { authenticate: { strategies: ['jwt'], mode: 'required' } },
101
+ deleteObject: { authenticate: { strategies: ['jwt'], mode: 'required' } },
102
+ deleteBucket: { authenticate: { strategies: ['jwt'], mode: 'required' } },
246
103
  },
247
104
  },
248
- // ...
249
105
  }
250
106
  ```
251
107
 
252
- Available route keys: `getBuckets`, `getBucketByName`, `createBucket`, `deleteBucket`, `upload`, `listObjects`, `deleteObject`, `getObjectByName`, `downloadObjectByName`, `recreateMetaLink`.
253
-
254
- #### TStaticAssetsComponentOptions -- Full Reference
255
- ```typescript
256
- type TStaticAssetsComponentOptions = {
257
- [key: string]: {
258
- controller: {
259
- name: string;
260
- basePath: string;
261
- isStrict?: boolean;
262
- routes?: {
263
- getBuckets?: Partial<Omit<IAuthRouteConfig, 'method' | 'request' | 'responses'>>;
264
- getBucketByName?: Partial<Omit<IAuthRouteConfig, 'method' | 'request' | 'responses'>>;
265
- createBucket?: Partial<Omit<IAuthRouteConfig, 'method' | 'request' | 'responses'>>;
266
- deleteBucket?: Partial<Omit<IAuthRouteConfig, 'method' | 'request' | 'responses'>>;
267
- upload?: Partial<Omit<IAuthRouteConfig, 'method' | 'request' | 'responses'>>;
268
- listObjects?: Partial<Omit<IAuthRouteConfig, 'method' | 'request' | 'responses'>>;
269
- deleteObject?: Partial<Omit<IAuthRouteConfig, 'method' | 'request' | 'responses'>>;
270
- getObjectByName?: Partial<Omit<IAuthRouteConfig, 'method' | 'request' | 'responses'>>;
271
- downloadObjectByName?: Partial<Omit<IAuthRouteConfig, 'method' | 'request' | 'responses'>>;
272
- recreateMetaLink?: Partial<Omit<IAuthRouteConfig, 'method' | 'request' | 'responses'>>;
273
- };
274
- };
275
- extra?: TStaticAssetExtraOptions;
276
- } & (
277
- | { storage: typeof StaticAssetStorageTypes.BUN_S3; helper: BunS3Helper }
278
- | { storage: typeof StaticAssetStorageTypes.DISK; helper: DiskHelper }
279
- | { storage: typeof StaticAssetStorageTypes.MINIO; helper: MinioHelper }
280
- ) &
281
- ({ useMetaLink?: false | undefined } | { useMetaLink: true; metaLink: TMetaLinkConfig });
282
- };
283
-
284
- type TStaticAssetExtraOptions = {
285
- parseMultipartBody?: {
286
- storage?: 'memory' | 'disk';
287
- uploadDir?: string;
288
- };
289
- normalizeNameFn?: (opts: { originalName: string }) => string;
290
- normalizeLinkFn?: (opts: { bucketName: string; normalizeName: string }) => string;
291
- /** Maximum folder nesting depth allowed in object paths. Default: 2 */
292
- maxFolderDepth?: number;
293
- [key: string]: any;
294
- };
295
-
296
- type TMetaLinkConfig<Schema extends TMetaLinkSchema = TMetaLinkSchema> = {
297
- model: typeof BasePostgresEntity<Schema>;
298
- repository: DefaultCRUDRepository<Schema>;
299
- createMetaLink?: (opts: {
300
- uploadResult: IUploadResult;
301
- fileStat: IFileStat;
302
- query: TUploadQuery;
303
- }) => ValueOrPromise<{ count: number; data: Schema }>;
304
- };
305
- ```
306
-
307
- > [!NOTE]
308
- > The `normalizeNameFn` receives only `{ originalName }` -- there is no `folderPath` parameter.
309
-
310
- > [!TIP]
311
- > The `createMetaLink` callback on `TMetaLinkConfig` is optional. When provided, it replaces the default MetaLink creation logic during upload, giving you full control over how file metadata is stored.
312
-
313
- ### DiskHelper
314
-
315
- Stores files on the local filesystem using a bucket-based directory structure.
316
-
317
- ```typescript
318
- new DiskHelper({
319
- basePath: string; // Base directory for storage
320
- scope?: string; // Logger scope
321
- identifier?: string; // Helper identifier
322
- })
323
- ```
108
+ ### Switch multipart parsing to disk for large uploads
324
109
 
325
- **Example:**
110
+ `parseMultipartBody: { storage: 'memory' }` (the default) buffers the whole file in RAM before writing it to the backend. Switch to `'disk'` to spool it to a temp file instead.
326
111
 
327
112
  ```typescript
328
- const diskHelper = new DiskHelper({
329
- basePath: './app_data/storage',
330
- });
331
- ```
332
-
333
- **Directory structure:**
334
- ```
335
- app_data/storage/
336
- ├── bucket-1/
337
- │ ├── file1.pdf
338
- │ └── file2.jpg
339
- ├── bucket-2/
340
- │ └── document.docx
341
- ```
342
-
343
- Features: automatic directory creation, built-in path validation, metadata from file stats, stream-based operations.
344
-
345
- ### MinioHelper
346
-
347
- Connects to MinIO or any S3-compatible object storage.
348
-
349
- ```typescript
350
- new MinioHelper({
351
- endPoint: string; // MinIO server hostname
352
- port: number; // API port (default: 9000)
353
- useSSL: boolean; // Use HTTPS
354
- accessKey: string; // Access key
355
- secretKey: string; // Secret key
356
- })
357
- ```
358
-
359
- **Example:**
360
-
361
- ```typescript
362
- const minioHelper = new MinioHelper({
363
- endPoint: 'minio.example.com',
364
- port: 9000,
365
- useSSL: true,
366
- accessKey: process.env.MINIO_ACCESS_KEY,
367
- secretKey: process.env.MINIO_SECRET_KEY,
368
- });
369
- ```
370
-
371
- ### BunS3Helper
372
-
373
- Bun-native S3 client for direct S3/S3-compatible access using Bun's built-in S3 support. Requires Bun runtime.
374
-
375
- ```typescript
376
- import { BunS3Helper } from '@venizia/ignis-helpers/bun-s3';
377
- ```
378
-
379
- ### MetaLink Configuration
380
-
381
- 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`), variant, timestamps, and custom metadata (JSONB).
382
-
383
- #### Benefits
384
-
385
- - Query uploaded files by bucket, name, mimetype, variant, etc.
386
- - Track file history and audit trails
387
- - Store custom metadata about files
388
- - Associate files with principals via `principalType` and `principalId` (passed as query parameters on the upload endpoint)
389
- - Tag uploads with a `variant` query parameter (e.g., `"thumbnail"`, `"original"`)
390
- - Custom `createMetaLink` callback for full control over MetaLink creation
391
- - Graceful errors -- upload succeeds even if MetaLink creation fails
392
-
393
- #### Setup
394
-
395
- **1. Create Model:**
396
-
397
- ```typescript
398
- import { BaseMetaLinkModel } from '@venizia/ignis/static-asset';
399
- import { model } from '@venizia/ignis';
400
-
401
- @model({ type: 'entity' })
402
- export class FileMetaLinkModel extends BaseMetaLinkModel {
403
- // Inherits all fields from BaseMetaLinkModel
113
+ extra: {
114
+ parseMultipartBody: { storage: 'disk', uploadDir: './tmp/uploads' },
404
115
  }
405
116
  ```
406
117
 
407
- **2. Create Repository:**
118
+ ### Enable MetaLink database tracking
408
119
 
409
- ```typescript
410
- import { BaseMetaLinkRepository } from '@venizia/ignis/static-asset';
411
- import { repository } from '@venizia/ignis';
412
- import { FileMetaLinkModel } from './file-meta-link.model';
413
- import { PostgresDataSource } from '../datasources/postgres.datasource';
414
-
415
- @repository({ model: FileMetaLinkModel, dataSource: PostgresDataSource })
416
- export class FileMetaLinkRepository extends BaseMetaLinkRepository {
417
- // No constructor needed - dataSource auto-injected from @repository metadata
418
- }
419
- ```
420
-
421
- **3. Create Database Table:**
422
-
423
- The model has `skipMigrate: true`, so create the table manually:
424
-
425
- ```sql
426
- CREATE TABLE "MetaLink" (
427
- id TEXT PRIMARY KEY,
428
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
429
- modified_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
430
- bucket_name TEXT NOT NULL,
431
- object_name TEXT NOT NULL,
432
- link TEXT NOT NULL,
433
- mimetype TEXT NOT NULL,
434
- size INTEGER NOT NULL,
435
- etag TEXT,
436
- metadata JSONB,
437
- storage_type TEXT NOT NULL,
438
- is_synced BOOLEAN NOT NULL DEFAULT false,
439
- variant TEXT,
440
- principal_type TEXT,
441
- principal_id TEXT
442
- );
443
-
444
- CREATE INDEX "IDX_MetaLink_bucketName" ON "MetaLink"(bucket_name);
445
- CREATE INDEX "IDX_MetaLink_objectName" ON "MetaLink"(object_name);
446
- CREATE INDEX "IDX_MetaLink_storageType" ON "MetaLink"(storage_type);
447
- CREATE INDEX "IDX_MetaLink_isSynced" ON "MetaLink"(is_synced);
448
- ```
449
-
450
- **4. Configure Component:**
451
-
452
- ```typescript
453
- import { FileMetaLinkModel, FileMetaLinkRepository } from './your-models';
454
- import {
455
- StaticAssetComponent,
456
- StaticAssetComponentBindingKeys,
457
- StaticAssetStorageTypes,
458
- } from '@venizia/ignis/static-asset';
459
- import type { TStaticAssetsComponentOptions } from '@venizia/ignis/static-asset';
460
-
461
- export class Application extends BaseApplication {
462
- configureComponents(): void {
463
- this.repository(FileMetaLinkRepository);
464
-
465
- this.bind<TStaticAssetsComponentOptions>({
466
- key: StaticAssetComponentBindingKeys.STATIC_ASSET_COMPONENT_OPTIONS,
467
- }).toValue({
468
- uploads: {
469
- controller: {
470
- name: 'UploadController',
471
- basePath: '/uploads',
472
- isStrict: true,
473
- },
474
- storage: StaticAssetStorageTypes.MINIO,
475
- helper: new MinioHelper({ /* ... */ }),
476
- useMetaLink: true,
477
- metaLink: {
478
- model: FileMetaLinkModel,
479
- repository: this.getSync(FileMetaLinkRepository),
480
- },
481
- extra: {
482
- parseMultipartBody: { storage: 'memory' },
483
- },
484
- },
485
- });
486
-
487
- this.component(StaticAssetComponent);
488
- }
489
- }
490
- ```
491
-
492
- **5. Upload with Principal Association and Variant:**
493
-
494
- When MetaLink is enabled, you can associate uploaded files with a principal and variant by passing query parameters on the upload endpoint:
495
-
496
- ```typescript
497
- const formData = new FormData();
498
- formData.append('file', fileBlob, 'document.pdf');
499
-
500
- // Associate the upload with a user and tag as 'original' variant
501
- const response = await fetch(
502
- '/uploads/buckets/user-files/upload?principalType=user&principalId=42&variant=original',
503
- { method: 'POST', body: formData },
504
- );
505
- ```
506
-
507
- The `principalId` value is always stored as a string regardless of input type (coerced via `String()`).
508
-
509
- #### Custom MetaLink Creation
510
-
511
- You can provide a custom `createMetaLink` callback to fully control how MetaLink records are created:
120
+ Bind a repository over `BaseMetaLinkModel` (used directly, no subclass needed) and pass it as `metaLink.repository`.
512
121
 
513
122
  ```typescript
123
+ useMetaLink: true,
514
124
  metaLink: {
515
- model: FileMetaLinkModel,
516
- repository: this.getSync(FileMetaLinkRepository),
517
- createMetaLink: async ({ uploadResult, fileStat, query }) => {
518
- // Custom logic -- e.g., add extra fields, validate, transform
519
- return metaLinkRepo.create({
520
- data: {
521
- bucketName: uploadResult.bucketName,
522
- objectName: uploadResult.objectName,
523
- link: uploadResult.link,
524
- mimetype: fileStat.metadata?.['mimetype'],
525
- size: fileStat.size,
526
- etag: fileStat.etag,
527
- metadata: fileStat.metadata,
528
- storageType: 'minio',
529
- isSynced: true,
530
- principalId: query.principalId ? String(query.principalId) : undefined,
531
- principalType: query.principalType,
532
- variant: query.variant,
533
- // ... additional custom fields
534
- },
535
- });
536
- },
125
+ model: BaseMetaLinkModel,
126
+ repository: this.get<MetaLinkRepository>({ key: 'repositories.MetaLinkRepository' }),
537
127
  },
538
128
  ```
539
129
 
540
- When `createMetaLink` is not provided, the component uses a default implementation that stores all standard fields.
541
-
542
- #### Querying MetaLinks
130
+ See [Enable MetaLink tracking](./usage#enable-metalink-tracking) for the full repository/table/binding setup.
543
131
 
544
- ```typescript
545
- // Get all files in a bucket
546
- const files = await fileMetaLinkRepository.find({
547
- where: { bucketName: 'user-uploads' },
548
- });
549
-
550
- // Get files by mimetype
551
- const pdfs = await fileMetaLinkRepository.find({
552
- where: { mimetype: 'application/pdf' },
553
- });
554
-
555
- // Get files by storage type
556
- const minioFiles = await fileMetaLinkRepository.find({
557
- where: { storageType: 'minio' },
558
- });
559
-
560
- // Get files by principal
561
- const userFiles = await fileMetaLinkRepository.find({
562
- where: { principalType: 'user', principalId: '42' },
563
- });
564
-
565
- // Get files by variant
566
- const thumbnails = await fileMetaLinkRepository.find({
567
- where: { variant: 'thumbnail' },
568
- });
569
-
570
- // Get synced files only
571
- const syncedFiles = await fileMetaLinkRepository.find({
572
- where: { isSynced: true },
573
- });
574
- ```
132
+ ### Customize object names and links
575
133
 
576
- ### Quick Start Options
134
+ `normalizeNameFn` runs before the file is written to storage; `normalizeLinkFn` builds the `link` value returned to the client. Both are optional - omit either to keep the component's defaults.
577
135
 
578
- **Option 1: MinIO Only**
579
136
  ```typescript
580
- import {
581
- StaticAssetComponent,
582
- StaticAssetComponentBindingKeys,
583
- StaticAssetStorageTypes,
584
- } from '@venizia/ignis/static-asset';
585
-
586
- this.bind({
587
- key: StaticAssetComponentBindingKeys.STATIC_ASSET_COMPONENT_OPTIONS,
588
- }).toValue({
589
- cloudStorage: {
590
- controller: { name: 'CloudController', basePath: '/cloud' },
591
- storage: StaticAssetStorageTypes.MINIO,
592
- helper: new MinioHelper({ /* ... */ }),
593
- extra: { parseMultipartBody: { storage: 'memory' } },
594
- },
595
- });
596
- this.component(StaticAssetComponent);
597
- ```
598
-
599
- **Option 2: Local Disk Only**
600
- ```typescript
601
- this.bind({
602
- key: StaticAssetComponentBindingKeys.STATIC_ASSET_COMPONENT_OPTIONS,
603
- }).toValue({
604
- localStorage: {
605
- controller: { name: 'LocalController', basePath: '/files' },
606
- storage: StaticAssetStorageTypes.DISK,
607
- helper: new DiskHelper({ basePath: './uploads' }),
608
- extra: { parseMultipartBody: { storage: 'disk' } },
609
- },
610
- });
611
- this.component(StaticAssetComponent);
612
- ```
613
-
614
- **Option 3: Multiple Storage Backends (Recommended)**
615
- ```typescript
616
- this.bind({
617
- key: StaticAssetComponentBindingKeys.STATIC_ASSET_COMPONENT_OPTIONS,
618
- }).toValue({
619
- userUploads: {
620
- controller: { name: 'UploadsController', basePath: '/uploads' },
621
- storage: StaticAssetStorageTypes.MINIO,
622
- helper: new MinioHelper({ /* ... */ }),
623
- extra: {},
624
- },
625
- tempFiles: {
626
- controller: { name: 'TempController', basePath: '/temp' },
627
- storage: StaticAssetStorageTypes.DISK,
628
- helper: new DiskHelper({ basePath: './temp' }),
629
- extra: {},
630
- },
631
- publicAssets: {
632
- controller: { name: 'PublicController', basePath: '/public' },
633
- storage: StaticAssetStorageTypes.DISK,
634
- helper: new DiskHelper({ basePath: './public' }),
635
- extra: {},
636
- },
637
- });
638
- this.component(StaticAssetComponent);
639
- ```
640
-
641
- ### Custom Filename Normalization
642
-
643
- ```typescript
644
- {
645
- uploads: {
646
- controller: { name: 'UploadController', basePath: '/uploads' },
647
- storage: StaticAssetStorageTypes.MINIO,
648
- helper: new MinioHelper({ /* ... */ }),
649
- extra: {
650
- parseMultipartBody: { storage: 'memory' },
651
- normalizeNameFn: ({ originalName }) => {
652
- return `${Date.now()}_${originalName.toLowerCase().replace(/\s/g, '_')}`;
653
- },
654
- normalizeLinkFn: ({ bucketName, normalizeName }) => {
655
- return `/api/files/${bucketName}/${encodeURIComponent(normalizeName)}`;
656
- },
657
- },
658
- },
659
- }
137
+ extra: {
138
+ normalizeNameFn: ({ originalName, folderPath }) =>
139
+ `${Date.now()}_${originalName.toLowerCase().replace(/\s/g, '_')}`,
140
+ normalizeLinkFn: ({ bucketName, normalizeName }) =>
141
+ `/api/files/${bucketName}/${encodeURIComponent(normalizeName)}`,
142
+ },
660
143
  ```
661
144
 
662
- The `normalizeNameFn` receives only the `originalName` of the uploaded file.
663
-
664
- ## Binding Keys
665
-
666
- | Key | Constant | Type | Required | Default |
667
- |-----|----------|------|----------|---------|
668
- | `@app/static-asset-component/options` | `StaticAssetComponentBindingKeys.STATIC_ASSET_COMPONENT_OPTIONS` | `TStaticAssetsComponentOptions` | Yes | `{}` |
145
+ ## See also
669
146
 
670
- > [!NOTE]
671
- > The component provides an empty default binding. You must bind this key with your storage configuration before registering the component.
147
+ - [Usage & Examples](./usage) - task-oriented walkthroughs for every endpoint and MetaLink setup
148
+ - [Full Reference](./api) - controller factory, `IStorageHelper` interface, MetaLink schema, internals
149
+ - [Error Reference](./errors) - name validation rules and troubleshooting
150
+ - [Storage Helpers](/extensions/helpers/storage/) - `DiskHelper`, `MinioHelper`, `BaseStorageHelper` reference
151
+ - [Components Overview](/guides/core-concepts/components) - component system basics
672
152
 
673
- ## See Also
153
+ **Files:**
674
154
 
675
- - [Usage & Examples](./usage) - API Endpoints and Frontend Integration
676
- - [API Reference](./api) - Controller Factory, Storage Interface, MetaLink Schema
677
- - [Error Reference](./errors) - Name Validation and Troubleshooting
678
- - [Storage Helpers](/extensions/helpers/storage/) - DiskHelper, MinioHelper, BaseStorageHelper
679
- - [Request Utilities](/references/utilities/request) - File upload utilities
680
- - [Security Guidelines](/best-practices/security-guidelines) - File upload security
681
- - [Components Overview](/guides/core-concepts/components) - Component system basics
682
- - [Controllers](/guides/core-concepts/rest-controllers) - File upload endpoints
155
+ - [`packages/core-server/src/components/static-asset/component.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/static-asset/component.ts) - `StaticAssetComponent`
156
+ - [`packages/core-server/src/components/static-asset/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/static-asset/common/types.ts) - `TStaticAssetsComponentOptions`, `TStaticAssetExtraOptions`, `TMetaLinkConfig`
157
+ - [`packages/core-server/src/components/static-asset/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/static-asset/common/constants.ts) - `StaticAssetStorageTypes`
158
+ - [`packages/core-server/src/components/static-asset/controller/factory.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/static-asset/controller/factory.ts) - `AssetControllerFactory`
159
+ - [`packages/core-server/src/components/static-asset/models/base.model.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/static-asset/models/base.model.ts) - `BaseMetaLinkModel`
160
+ - [`packages/helpers/src/modules/storage/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/storage/base.ts) - `BaseStorageHelper`, `IStorageHelper`