@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.
- package/README.md +14 -14
- package/content/best-practices/api-usage-examples.md +39 -19
- package/content/best-practices/architectural-patterns.md +24 -13
- package/content/best-practices/architecture-decisions.md +29 -16
- package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
- package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
- package/content/best-practices/code-style-standards/control-flow.md +33 -1
- package/content/best-practices/code-style-standards/documentation.md +28 -8
- package/content/best-practices/code-style-standards/function-patterns.md +15 -4
- package/content/best-practices/code-style-standards/index.md +7 -2
- package/content/best-practices/code-style-standards/naming-conventions.md +27 -3
- package/content/best-practices/code-style-standards/route-definitions.md +9 -5
- package/content/best-practices/code-style-standards/tooling.md +14 -1
- package/content/best-practices/code-style-standards/type-safety.md +39 -6
- package/content/best-practices/common-pitfalls.md +59 -35
- package/content/best-practices/contribution-workflow.md +8 -4
- package/content/best-practices/data-modeling.md +78 -62
- package/content/best-practices/deployment-strategies.md +56 -19
- package/content/best-practices/error-handling.md +247 -153
- package/content/best-practices/index.md +6 -0
- package/content/best-practices/performance-optimization.md +26 -13
- package/content/best-practices/security-guidelines.md +35 -9
- package/content/best-practices/testing-strategies.md +7 -2
- package/content/best-practices/troubleshooting-tips.md +27 -17
- package/content/extensions/components/api-reference.md +109 -323
- package/content/extensions/components/authentication/api.md +489 -602
- package/content/extensions/components/authentication/errors.md +135 -498
- package/content/extensions/components/authentication/index.md +89 -801
- package/content/extensions/components/authentication/usage.md +231 -955
- package/content/extensions/components/authorization/api.md +991 -644
- package/content/extensions/components/authorization/errors.md +204 -208
- package/content/extensions/components/authorization/getting-started.md +227 -0
- package/content/extensions/components/authorization/index.md +88 -795
- package/content/extensions/components/authorization/usage.md +219 -528
- package/content/extensions/components/health-check.md +77 -243
- package/content/extensions/components/index.md +24 -90
- package/content/extensions/components/mail/api.md +553 -285
- package/content/extensions/components/mail/errors.md +76 -62
- package/content/extensions/components/mail/index.md +111 -463
- package/content/extensions/components/mail/usage.md +139 -173
- package/content/extensions/components/request-tracker.md +70 -173
- package/content/extensions/components/socket-io/api.md +462 -785
- package/content/extensions/components/socket-io/errors.md +49 -51
- package/content/extensions/components/socket-io/index.md +69 -372
- package/content/extensions/components/socket-io/usage.md +212 -105
- package/content/extensions/components/static-asset/api.md +461 -141
- package/content/extensions/components/static-asset/errors.md +121 -53
- package/content/extensions/components/static-asset/index.md +84 -606
- package/content/extensions/components/static-asset/usage.md +184 -299
- package/content/extensions/components/template/index.md +3 -3
- package/content/extensions/components/websocket/api.md +311 -404
- package/content/extensions/components/websocket/errors.md +47 -56
- package/content/extensions/components/websocket/index.md +75 -407
- package/content/extensions/components/websocket/usage.md +120 -338
- package/content/extensions/helpers/cron/index.md +52 -160
- package/content/extensions/helpers/crypto/index.md +67 -480
- package/content/extensions/helpers/crypto/reference.md +528 -0
- package/content/extensions/helpers/env/index.md +64 -178
- package/content/extensions/helpers/error/index.md +286 -196
- package/content/extensions/helpers/index.md +61 -47
- package/content/extensions/helpers/inversion/index.md +76 -550
- package/content/extensions/helpers/inversion/reference.md +530 -0
- package/content/extensions/helpers/kafka/admin.md +23 -3
- package/content/extensions/helpers/kafka/compile-binary.md +83 -52
- package/content/extensions/helpers/kafka/consumer.md +65 -28
- package/content/extensions/helpers/kafka/examples.md +46 -253
- package/content/extensions/helpers/kafka/index.md +55 -617
- package/content/extensions/helpers/kafka/producer.md +156 -22
- package/content/extensions/helpers/kafka/schema-registry.md +59 -79
- package/content/extensions/helpers/logger/hf-logger.md +220 -0
- package/content/extensions/helpers/logger/index.md +78 -552
- package/content/extensions/helpers/logger/pino.md +105 -0
- package/content/extensions/helpers/logger/reference.md +937 -0
- package/content/extensions/helpers/network/api.md +276 -195
- package/content/extensions/helpers/network/index.md +87 -524
- package/content/extensions/helpers/queue/index.md +80 -897
- package/content/extensions/helpers/queue/reference.md +494 -0
- package/content/extensions/helpers/redis/index.md +86 -640
- package/content/extensions/helpers/redis/reference.md +784 -0
- package/content/extensions/helpers/secrets/index.md +136 -0
- package/content/extensions/helpers/socket-io/api.md +325 -204
- package/content/extensions/helpers/socket-io/index.md +79 -429
- package/content/extensions/helpers/storage/api.md +584 -464
- package/content/extensions/helpers/storage/index.md +80 -573
- package/content/extensions/helpers/types/index.md +75 -495
- package/content/extensions/helpers/types/reference.md +689 -0
- package/content/extensions/helpers/uid/index.md +176 -189
- package/content/extensions/helpers/websocket/api.md +366 -214
- package/content/extensions/helpers/websocket/index.md +68 -503
- package/content/extensions/helpers/worker-thread/index.md +62 -396
- package/content/extensions/helpers/worker-thread/reference.md +428 -0
- package/content/extensions/index.md +38 -39
- package/content/extensions/src-details/mcp-server.md +96 -548
- package/content/guides/core-concepts/persistent/datasources.md +2 -2
- package/content/guides/core-concepts/persistent/index.md +5 -1
- package/content/guides/core-concepts/persistent/models.md +1 -1
- package/content/guides/core-concepts/persistent/pglite.md +218 -0
- package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
- package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
- package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
- package/content/guides/core-concepts/persistent/sqlite.md +296 -0
- package/content/guides/core-concepts/persistent/transactions.md +12 -11
- package/content/guides/core-concepts/secrets-vault.md +177 -0
- package/content/guides/core-concepts/services.md +1 -1
- package/content/guides/get-started/5-minute-quickstart.md +59 -206
- package/content/guides/get-started/philosophy.md +135 -670
- package/content/guides/get-started/setup.md +53 -74
- package/content/guides/migrations/redis-helpers-migration.md +5 -4
- package/content/guides/migrations/scoped-rbac-migration.md +5 -5
- package/content/guides/migrations/unified-connectors-migration.md +8 -7
- package/content/guides/tutorials/ecommerce-api.md +3 -8
- package/content/guides/tutorials/realtime-chat.md +1 -1
- package/content/references/base/application.md +64 -22
- package/content/references/base/components.md +3 -3
- package/content/references/base/connectors.md +79 -136
- package/content/references/base/controllers.md +12 -12
- package/content/references/base/datasources-reference.md +600 -0
- package/content/references/base/datasources.md +84 -444
- package/content/references/base/dependency-injection.md +25 -46
- package/content/references/base/filter-system/application-usage.md +95 -123
- package/content/references/base/filter-system/array-operators.md +33 -43
- package/content/references/base/filter-system/comparison-operators.md +55 -63
- package/content/references/base/filter-system/default-filter.md +144 -350
- package/content/references/base/filter-system/fields-order-pagination.md +116 -148
- package/content/references/base/filter-system/index.md +114 -253
- package/content/references/base/filter-system/json-filtering.md +52 -181
- package/content/references/base/filter-system/list-operators.md +28 -44
- package/content/references/base/filter-system/logical-operators.md +69 -114
- package/content/references/base/filter-system/null-operators.md +41 -98
- package/content/references/base/filter-system/pattern-matching.md +48 -51
- package/content/references/base/filter-system/quick-reference.md +93 -196
- package/content/references/base/filter-system/range-operators.md +22 -38
- package/content/references/base/filter-system/tips.md +70 -133
- package/content/references/base/filter-system/use-cases.md +173 -232
- package/content/references/base/grpc-controllers.md +53 -17
- package/content/references/base/index.md +5 -3
- package/content/references/base/middlewares.md +43 -28
- package/content/references/base/models-reference.md +886 -0
- package/content/references/base/models.md +81 -1452
- package/content/references/base/providers.md +8 -8
- package/content/references/base/repositories/advanced.md +281 -419
- package/content/references/base/repositories/index.md +84 -644
- package/content/references/base/repositories/mixins.md +25 -21
- package/content/references/base/repositories/relations.md +194 -375
- package/content/references/base/repositories/soft-deletable.md +67 -55
- package/content/references/base/secrets.md +267 -0
- package/content/references/base/services.md +8 -6
- package/content/references/configuration/environment-variables.md +73 -21
- package/content/references/configuration/index.md +51 -31
- package/content/references/index.md +1 -1
- package/content/references/quick-reference.md +3 -16
- package/content/references/utilities/crypto.md +35 -76
- package/content/references/utilities/date.md +33 -73
- package/content/references/utilities/duration.md +85 -0
- package/content/references/utilities/index.md +6 -2
- package/content/references/utilities/jsx-reference.md +298 -0
- package/content/references/utilities/jsx.md +82 -525
- package/content/references/utilities/module.md +81 -61
- package/content/references/utilities/parse.md +34 -64
- package/content/references/utilities/performance.md +33 -58
- package/content/references/utilities/promise.md +28 -62
- package/content/references/utilities/request.md +58 -218
- package/content/references/utilities/retry.md +139 -0
- package/content/references/utilities/schema.md +43 -137
- package/content/references/utilities/statuses-reference.md +361 -0
- package/content/references/utilities/statuses.md +63 -667
- package/dist/mcp-server/index.js +0 -0
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
- package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
- package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
- package/package.json +24 -23
|
@@ -1,235 +1,95 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
7
|
+
# Static Asset Component
|
|
4
8
|
|
|
5
|
-
|
|
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**
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
46
|
+
## How it works
|
|
130
47
|
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
51
|
+
| `storage` | Required `helper` |
|
|
52
|
+
|-----------|--------------------|
|
|
53
|
+
| `'disk'` | `DiskHelper` |
|
|
54
|
+
| `'minio'` | `MinioHelper` |
|
|
55
|
+
| `'bun-s3'` | `BunS3Helper` |
|
|
145
56
|
|
|
146
|
-
|
|
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
|
-
|
|
62
|
+
## Common tasks
|
|
149
63
|
|
|
150
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
###
|
|
90
|
+
### Lock down individual routes
|
|
215
91
|
|
|
216
|
-
Each key
|
|
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
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
329
|
-
|
|
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
|
-
|
|
118
|
+
### Enable MetaLink database tracking
|
|
408
119
|
|
|
409
|
-
|
|
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:
|
|
516
|
-
repository: this.
|
|
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
|
-
|
|
541
|
-
|
|
542
|
-
#### Querying MetaLinks
|
|
130
|
+
See [Enable MetaLink tracking](./usage#enable-metalink-tracking) for the full repository/table/binding setup.
|
|
543
131
|
|
|
544
|
-
|
|
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
|
-
|
|
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
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
}
|
|
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
|
-
|
|
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
|
-
|
|
671
|
-
|
|
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
|
-
|
|
153
|
+
**Files:**
|
|
674
154
|
|
|
675
|
-
- [
|
|
676
|
-
- [
|
|
677
|
-
- [
|
|
678
|
-
- [
|
|
679
|
-
- [
|
|
680
|
-
- [
|
|
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`
|