@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.
- package/package.json +1 -1
- package/wiki/best-practices/architecture-decisions.md +0 -8
- package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
- package/wiki/best-practices/performance-optimization.md +3 -3
- package/wiki/best-practices/security-guidelines.md +2 -2
- package/wiki/best-practices/troubleshooting-tips.md +1 -1
- package/wiki/guides/core-concepts/components-guide.md +1 -1
- package/wiki/guides/core-concepts/components.md +2 -2
- package/wiki/guides/core-concepts/dependency-injection.md +1 -1
- package/wiki/guides/core-concepts/services.md +1 -1
- package/wiki/guides/tutorials/building-a-crud-api.md +1 -1
- package/wiki/guides/tutorials/ecommerce-api.md +2 -2
- package/wiki/guides/tutorials/realtime-chat.md +6 -6
- package/wiki/guides/tutorials/testing.md +1 -1
- package/wiki/references/base/bootstrapping.md +0 -2
- package/wiki/references/base/components.md +2 -2
- package/wiki/references/base/controllers.md +0 -1
- package/wiki/references/base/datasources.md +1 -1
- package/wiki/references/base/dependency-injection.md +1 -1
- package/wiki/references/base/filter-system/quick-reference.md +0 -14
- package/wiki/references/base/middlewares.md +0 -8
- package/wiki/references/base/providers.md +0 -9
- package/wiki/references/base/services.md +0 -1
- package/wiki/references/components/authentication/api.md +444 -0
- package/wiki/references/components/authentication/errors.md +177 -0
- package/wiki/references/components/authentication/index.md +571 -0
- package/wiki/references/components/authentication/usage.md +781 -0
- package/wiki/references/components/health-check.md +292 -103
- package/wiki/references/components/index.md +14 -12
- package/wiki/references/components/mail/api.md +505 -0
- package/wiki/references/components/mail/errors.md +176 -0
- package/wiki/references/components/mail/index.md +535 -0
- package/wiki/references/components/mail/usage.md +404 -0
- package/wiki/references/components/request-tracker.md +229 -25
- package/wiki/references/components/socket-io/api.md +1051 -0
- package/wiki/references/components/socket-io/errors.md +119 -0
- package/wiki/references/components/socket-io/index.md +410 -0
- package/wiki/references/components/socket-io/usage.md +322 -0
- package/wiki/references/components/static-asset/api.md +261 -0
- package/wiki/references/components/static-asset/errors.md +89 -0
- package/wiki/references/components/static-asset/index.md +617 -0
- package/wiki/references/components/static-asset/usage.md +364 -0
- package/wiki/references/components/swagger.md +390 -110
- package/wiki/references/components/template/api-page.md +125 -0
- package/wiki/references/components/template/errors-page.md +100 -0
- package/wiki/references/components/template/index.md +104 -0
- package/wiki/references/components/template/setup-page.md +134 -0
- package/wiki/references/components/template/single-page.md +132 -0
- package/wiki/references/components/template/usage-page.md +127 -0
- package/wiki/references/components/websocket/api.md +508 -0
- package/wiki/references/components/websocket/errors.md +123 -0
- package/wiki/references/components/websocket/index.md +453 -0
- package/wiki/references/components/websocket/usage.md +475 -0
- package/wiki/references/helpers/cron/index.md +224 -0
- package/wiki/references/helpers/crypto/index.md +537 -0
- package/wiki/references/helpers/env/index.md +214 -0
- package/wiki/references/helpers/error/index.md +232 -0
- package/wiki/references/helpers/index.md +16 -15
- package/wiki/references/helpers/inversion/index.md +608 -0
- package/wiki/references/helpers/logger/index.md +600 -0
- package/wiki/references/helpers/network/api.md +986 -0
- package/wiki/references/helpers/network/index.md +620 -0
- package/wiki/references/helpers/queue/index.md +589 -0
- package/wiki/references/helpers/redis/index.md +495 -0
- package/wiki/references/helpers/socket-io/api.md +497 -0
- package/wiki/references/helpers/socket-io/index.md +513 -0
- package/wiki/references/helpers/storage/api.md +705 -0
- package/wiki/references/helpers/storage/index.md +583 -0
- package/wiki/references/helpers/template/index.md +66 -0
- package/wiki/references/helpers/template/single-page.md +126 -0
- package/wiki/references/helpers/testing/index.md +510 -0
- package/wiki/references/helpers/types/index.md +512 -0
- package/wiki/references/helpers/uid/index.md +272 -0
- package/wiki/references/helpers/websocket/api.md +736 -0
- package/wiki/references/helpers/websocket/index.md +574 -0
- package/wiki/references/helpers/worker-thread/index.md +470 -0
- package/wiki/references/quick-reference.md +3 -18
- package/wiki/references/utilities/jsx.md +1 -8
- package/wiki/references/utilities/statuses.md +0 -7
- package/wiki/references/components/authentication.md +0 -476
- package/wiki/references/components/mail.md +0 -687
- package/wiki/references/components/socket-io.md +0 -562
- package/wiki/references/components/static-asset.md +0 -1277
- package/wiki/references/helpers/cron.md +0 -108
- package/wiki/references/helpers/crypto.md +0 -132
- package/wiki/references/helpers/env.md +0 -83
- package/wiki/references/helpers/error.md +0 -97
- package/wiki/references/helpers/inversion.md +0 -176
- package/wiki/references/helpers/logger.md +0 -296
- package/wiki/references/helpers/network.md +0 -396
- package/wiki/references/helpers/queue.md +0 -150
- package/wiki/references/helpers/redis.md +0 -142
- package/wiki/references/helpers/socket-io.md +0 -932
- package/wiki/references/helpers/storage.md +0 -665
- package/wiki/references/helpers/testing.md +0 -133
- package/wiki/references/helpers/types.md +0 -167
- package/wiki/references/helpers/uid.md +0 -167
- package/wiki/references/helpers/worker-thread.md +0 -178
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
# Static Asset -- Usage & Examples
|
|
2
|
+
|
|
3
|
+
> API endpoint specifications, request/response details, and frontend integration examples.
|
|
4
|
+
|
|
5
|
+
## API Endpoints
|
|
6
|
+
|
|
7
|
+
The component dynamically generates REST endpoints for each configured storage backend. All backends expose the same API structure under their configured `basePath`.
|
|
8
|
+
|
|
9
|
+
| Method | Path | Description |
|
|
10
|
+
|--------|------|-------------|
|
|
11
|
+
| `GET` | <code v-pre>/{basePath}/buckets</code> | List all buckets |
|
|
12
|
+
| `GET` | <code v-pre>/{basePath}/buckets/:bucketName</code> | Get bucket by name |
|
|
13
|
+
| `POST` | <code v-pre>/{basePath}/buckets/:bucketName</code> | Create a bucket |
|
|
14
|
+
| `DELETE` | <code v-pre>/{basePath}/buckets/:bucketName</code> | Delete a bucket |
|
|
15
|
+
| `POST` | <code v-pre>/{basePath}/buckets/:bucketName/upload</code> | Upload files |
|
|
16
|
+
| `GET` | <code v-pre>/{basePath}/buckets/:bucketName/objects</code> | List objects |
|
|
17
|
+
| `GET` | <code v-pre>/{basePath}/buckets/:bucketName/objects/:objectName</code> | Stream file |
|
|
18
|
+
| `GET` | <code v-pre>/{basePath}/buckets/:bucketName/objects/:objectName/download</code> | Download file |
|
|
19
|
+
| `DELETE` | <code v-pre>/{basePath}/buckets/:bucketName/objects/:objectName</code> | Delete object |
|
|
20
|
+
| `PUT` | <code v-pre>/{basePath}/buckets/:bucketName/objects/:objectName/meta-links</code> | Sync MetaLink (MetaLink only) |
|
|
21
|
+
|
|
22
|
+
#### GET <code v-pre>/{basePath}/buckets</code>
|
|
23
|
+
**Response `200`:**
|
|
24
|
+
```json
|
|
25
|
+
[
|
|
26
|
+
{ "name": "my-bucket", "creationDate": "2025-01-01T00:00:00.000Z" }
|
|
27
|
+
]
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
#### GET <code v-pre>/{basePath}/buckets/:bucketName</code>
|
|
31
|
+
**Parameters:**
|
|
32
|
+
- `bucketName` (path): Bucket name
|
|
33
|
+
|
|
34
|
+
**Validation:** Bucket name validated with `isValidName()`. Returns 400 `"Invalid bucket name"` if invalid.
|
|
35
|
+
|
|
36
|
+
**Response `200`:**
|
|
37
|
+
```json
|
|
38
|
+
{ "name": "my-bucket", "creationDate": "2025-01-01T00:00:00.000Z" }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Returns `null` when the bucket does not exist. The response schema is nullable.
|
|
42
|
+
|
|
43
|
+
#### POST <code v-pre>/{basePath}/buckets/:bucketName</code>
|
|
44
|
+
**Parameters:**
|
|
45
|
+
- `bucketName` (path): Name of the new bucket
|
|
46
|
+
|
|
47
|
+
**Validation:** Bucket name validated with `isValidName()`. Returns 400 `"Invalid bucket name"` if invalid.
|
|
48
|
+
|
|
49
|
+
**Response `200`:**
|
|
50
|
+
```json
|
|
51
|
+
{ "name": "my-bucket", "creationDate": "2025-12-13T00:00:00.000Z" }
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Returns `null` if bucket creation fails (e.g., already exists). The response schema is nullable.
|
|
55
|
+
|
|
56
|
+
#### DELETE <code v-pre>/{basePath}/buckets/:bucketName</code>
|
|
57
|
+
**Parameters:**
|
|
58
|
+
- `bucketName` (path): Bucket to delete
|
|
59
|
+
|
|
60
|
+
**Validation:** Bucket name validated with `isValidName()`. Returns 400 `"Invalid bucket name"` if invalid.
|
|
61
|
+
|
|
62
|
+
**Response `200`:**
|
|
63
|
+
```json
|
|
64
|
+
{ "isDeleted": true }
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The `isDeleted` field is a boolean indicating whether the bucket was successfully removed from storage.
|
|
68
|
+
|
|
69
|
+
#### POST <code v-pre>/{basePath}/buckets/:bucketName/upload</code>
|
|
70
|
+
**Parameters:**
|
|
71
|
+
- `bucketName` (path): Target bucket name
|
|
72
|
+
|
|
73
|
+
**Query Parameters:**
|
|
74
|
+
- `principalType` (optional, string): Type of the principal to associate with the uploaded files (e.g., `"user"`, `"service"`)
|
|
75
|
+
- `principalId` (optional, string or number): ID of the principal. Always coerced to a string via `String()` before storage regardless of input type
|
|
76
|
+
|
|
77
|
+
**Validation:** Bucket name validated with `isValidName()`. Returns 400 `"Invalid bucket name"` if invalid.
|
|
78
|
+
|
|
79
|
+
**Request Body:** `multipart/form-data` with file fields. The request body is parsed using the `MultipartBodySchema` Zod schema:
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
const MultipartBodySchema = z.object({
|
|
83
|
+
files: z.union([z.instanceof(File), z.array(z.instanceof(File))]),
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
This accepts either a single `File` or an array of `File` objects. Each file can optionally include `folderPath` for organization.
|
|
88
|
+
|
|
89
|
+
**Response `200` (without MetaLink):**
|
|
90
|
+
```json
|
|
91
|
+
[
|
|
92
|
+
{
|
|
93
|
+
"bucketName": "my-bucket",
|
|
94
|
+
"objectName": "file.pdf",
|
|
95
|
+
"link": "/assets/buckets/my-bucket/objects/file.pdf"
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Response `200` (with MetaLink enabled):**
|
|
101
|
+
```json
|
|
102
|
+
[
|
|
103
|
+
{
|
|
104
|
+
"bucketName": "my-bucket",
|
|
105
|
+
"objectName": "file.pdf",
|
|
106
|
+
"link": "/assets/buckets/my-bucket/objects/file.pdf",
|
|
107
|
+
"metaLink": {
|
|
108
|
+
"id": "uuid",
|
|
109
|
+
"bucketName": "my-bucket",
|
|
110
|
+
"objectName": "file.pdf",
|
|
111
|
+
"link": "/assets/buckets/my-bucket/objects/file.pdf",
|
|
112
|
+
"mimetype": "application/pdf",
|
|
113
|
+
"size": 1024,
|
|
114
|
+
"etag": "abc123",
|
|
115
|
+
"metadata": {},
|
|
116
|
+
"storageType": "minio",
|
|
117
|
+
"isSynced": true,
|
|
118
|
+
"principalType": "user",
|
|
119
|
+
"principalId": "42",
|
|
120
|
+
"createdAt": "2025-12-15T03:00:00.000Z",
|
|
121
|
+
"modifiedAt": "2025-12-15T03:00:00.000Z"
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Response `200` (with MetaLink enabled, MetaLink creation failed):**
|
|
128
|
+
```json
|
|
129
|
+
[
|
|
130
|
+
{
|
|
131
|
+
"bucketName": "my-bucket",
|
|
132
|
+
"objectName": "file.pdf",
|
|
133
|
+
"link": "/assets/buckets/my-bucket/objects/file.pdf",
|
|
134
|
+
"metaLink": null,
|
|
135
|
+
"metaLinkError": "Database connection failed"
|
|
136
|
+
}
|
|
137
|
+
]
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
When MetaLink creation fails, the upload itself still succeeds. The response includes `metaLink: null` and a `metaLinkError` string describing the failure. The error is also logged via the controller's scoped logger.
|
|
141
|
+
|
|
142
|
+
**Example:**
|
|
143
|
+
```typescript
|
|
144
|
+
const formData = new FormData();
|
|
145
|
+
formData.append('file', fileBlob, 'document.pdf');
|
|
146
|
+
|
|
147
|
+
// Upload with principal association
|
|
148
|
+
const response = await fetch(
|
|
149
|
+
'/assets/buckets/uploads/upload?principalType=user&principalId=123',
|
|
150
|
+
{ method: 'POST', body: formData },
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const result = await response.json();
|
|
154
|
+
console.log(result[0].metaLink); // Database record (if MetaLink enabled)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### GET <code v-pre>/{basePath}/buckets/:bucketName/objects</code>
|
|
158
|
+
**Parameters:**
|
|
159
|
+
- `bucketName` (path): Bucket name
|
|
160
|
+
|
|
161
|
+
**Validation:** Bucket name validated with `isValidName()`. Returns 400 `"Invalid bucket name"` if invalid.
|
|
162
|
+
|
|
163
|
+
**Query Parameters:**
|
|
164
|
+
- `prefix` (optional, string): Filter objects by prefix (e.g., `"folder/"`)
|
|
165
|
+
- `recursive` (optional, string): Recursive listing. Parsed via strict string comparison `=== 'true'` -- only the exact string `"true"` enables recursion; any other truthy value (e.g., `"1"`, `"yes"`) does not
|
|
166
|
+
- `maxKeys` (optional, string): Maximum number of objects to return. Parsed as integer via `parseInt(value, 10)`
|
|
167
|
+
|
|
168
|
+
**Response `200`:**
|
|
169
|
+
```json
|
|
170
|
+
[
|
|
171
|
+
{
|
|
172
|
+
"name": "file1.pdf",
|
|
173
|
+
"size": 1024,
|
|
174
|
+
"lastModified": "2025-12-13T00:00:00.000Z",
|
|
175
|
+
"etag": "abc123",
|
|
176
|
+
"prefix": "folder/"
|
|
177
|
+
}
|
|
178
|
+
]
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
All fields in the `IObjectInfo` response are optional. The `prefix` field is present when listing non-recursively and the object is a directory prefix. When listing individual files, `name`, `size`, `lastModified`, and `etag` are typically populated.
|
|
182
|
+
|
|
183
|
+
#### GET <code v-pre>/{basePath}/buckets/:bucketName/objects/:objectName</code>
|
|
184
|
+
**Parameters:**
|
|
185
|
+
- `bucketName` (path): Bucket name
|
|
186
|
+
- `objectName` (path): Object name (URL-encoded)
|
|
187
|
+
|
|
188
|
+
**Validation:** Both bucket and object names validated with `isValidName()`. Returns 400 `"Invalid bucket name"` or `"Invalid object name"` respectively if either is invalid.
|
|
189
|
+
|
|
190
|
+
**Response:**
|
|
191
|
+
- Streams file content with appropriate headers
|
|
192
|
+
- `Content-Type`: From storage metadata or `application/octet-stream` as fallback
|
|
193
|
+
- `Content-Length`: File size in bytes
|
|
194
|
+
- `X-Content-Type-Options`: `nosniff`
|
|
195
|
+
- Additional whitelisted headers forwarded from storage metadata (see [Header Sanitization](./api#header-sanitization))
|
|
196
|
+
|
|
197
|
+
#### GET <code v-pre>/{basePath}/buckets/:bucketName/objects/:objectName/download</code>
|
|
198
|
+
**Parameters:**
|
|
199
|
+
- `bucketName` (path): Bucket name
|
|
200
|
+
- `objectName` (path): Object name (URL-encoded)
|
|
201
|
+
|
|
202
|
+
**Validation:** Both bucket and object names validated with `isValidName()`. Returns 400 `"Invalid bucket name"` or `"Invalid object name"` respectively if either is invalid.
|
|
203
|
+
|
|
204
|
+
**Response:**
|
|
205
|
+
- Streams file with download headers
|
|
206
|
+
- `Content-Disposition`: `attachment; filename="..."` (generated via `createContentDispositionHeader()`)
|
|
207
|
+
- `Content-Type`: From storage metadata or `application/octet-stream` as fallback
|
|
208
|
+
- `Content-Length`: File size in bytes
|
|
209
|
+
- `X-Content-Type-Options`: `nosniff`
|
|
210
|
+
- Additional whitelisted headers forwarded from storage metadata (see [Header Sanitization](./api#header-sanitization))
|
|
211
|
+
- Triggers browser download dialog
|
|
212
|
+
|
|
213
|
+
**Example:**
|
|
214
|
+
```typescript
|
|
215
|
+
const downloadUrl = `/assets/buckets/uploads/objects/${encodeURIComponent('document.pdf')}/download`;
|
|
216
|
+
window.open(downloadUrl, '_blank');
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
#### DELETE <code v-pre>/{basePath}/buckets/:bucketName/objects/:objectName</code>
|
|
220
|
+
**Parameters:**
|
|
221
|
+
- `bucketName` (path): Bucket name
|
|
222
|
+
- `objectName` (path): Object to delete (URL-encoded)
|
|
223
|
+
|
|
224
|
+
**Validation:** Both bucket and object names validated with `isValidName()`. Returns 400 `"Invalid bucket name"` or `"Invalid object name"` respectively if either is invalid.
|
|
225
|
+
|
|
226
|
+
**Behavior:**
|
|
227
|
+
- Deletes file from storage
|
|
228
|
+
- If MetaLink enabled, the MetaLink database record deletion is **fire-and-forget** -- the HTTP response returns immediately after the storage delete completes, without awaiting the database deletion
|
|
229
|
+
- MetaLink deletion errors are logged but do not fail the request
|
|
230
|
+
- MetaLink deletion uses `deleteAll({ where: { bucketName, objectName } })` to remove all matching records
|
|
231
|
+
|
|
232
|
+
**Response `200`:**
|
|
233
|
+
```json
|
|
234
|
+
{ "success": true }
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**Example:**
|
|
238
|
+
```typescript
|
|
239
|
+
const bucketName = 'user-uploads';
|
|
240
|
+
const objectName = 'document.pdf';
|
|
241
|
+
|
|
242
|
+
await fetch(`/assets/buckets/${bucketName}/objects/${encodeURIComponent(objectName)}`, {
|
|
243
|
+
method: 'DELETE',
|
|
244
|
+
});
|
|
245
|
+
// File deleted from storage
|
|
246
|
+
// MetaLink record deletion initiated (if enabled) but may complete after response
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
#### PUT <code v-pre>/{basePath}/buckets/:bucketName/objects/:objectName/meta-links</code>
|
|
250
|
+
**Availability:** Only registered when `useMetaLink: true`.
|
|
251
|
+
|
|
252
|
+
**Parameters:**
|
|
253
|
+
- `bucketName` (path): Bucket name
|
|
254
|
+
- `objectName` (path): Object name (URL-encoded)
|
|
255
|
+
|
|
256
|
+
**Validation:** Both bucket and object names validated with `isValidName()`. Returns 400 `"Invalid bucket name"` or `"Invalid object name"` respectively if either is invalid.
|
|
257
|
+
|
|
258
|
+
**Behavior:**
|
|
259
|
+
- Fetches current file metadata from storage via `helper.getStat()`
|
|
260
|
+
- Generates the file link using `normalizeLinkFn` (or the default link format <code v-pre>{basePath}/buckets/{bucket}/objects/{encodedName}</code>)
|
|
261
|
+
- If MetaLink exists (matched by `bucketName` + `objectName`): Updates with latest metadata via `updateById()`, then refetches via `findById()`
|
|
262
|
+
- If MetaLink doesn't exist: Creates new MetaLink record via `create()`
|
|
263
|
+
- Always sets `isSynced: true` to mark as synchronized
|
|
264
|
+
|
|
265
|
+
**Use Cases:**
|
|
266
|
+
- Manually sync files that exist in storage but not in database
|
|
267
|
+
- Update MetaLink metadata after file changes
|
|
268
|
+
- Rebuild MetaLink records after database restore
|
|
269
|
+
- Bulk synchronization operations
|
|
270
|
+
|
|
271
|
+
**Response `200` (MetaLink created or updated):**
|
|
272
|
+
```json
|
|
273
|
+
{
|
|
274
|
+
"success": true,
|
|
275
|
+
"metaLink": {
|
|
276
|
+
"id": "uuid",
|
|
277
|
+
"bucketName": "user-uploads",
|
|
278
|
+
"objectName": "document.pdf",
|
|
279
|
+
"link": "/assets/buckets/user-uploads/objects/document.pdf",
|
|
280
|
+
"mimetype": "application/pdf",
|
|
281
|
+
"size": 1048576,
|
|
282
|
+
"etag": "abc123",
|
|
283
|
+
"metadata": {},
|
|
284
|
+
"storageType": "minio",
|
|
285
|
+
"isSynced": true,
|
|
286
|
+
"principalType": null,
|
|
287
|
+
"principalId": null,
|
|
288
|
+
"createdAt": "2025-12-15T03:00:00.000Z",
|
|
289
|
+
"modifiedAt": "2025-12-15T03:00:00.000Z"
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
The response always wraps the MetaLink in a `{ success: boolean, metaLink: ... }` envelope. Both create and update flows return the same shape.
|
|
295
|
+
|
|
296
|
+
**Example:**
|
|
297
|
+
```typescript
|
|
298
|
+
// Sync a single file
|
|
299
|
+
const bucketName = 'user-uploads';
|
|
300
|
+
const objectName = 'document.pdf';
|
|
301
|
+
|
|
302
|
+
const response = await fetch(
|
|
303
|
+
`/assets/buckets/${bucketName}/objects/${encodeURIComponent(objectName)}/meta-links`,
|
|
304
|
+
{ method: 'PUT' }
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
const result = await response.json();
|
|
308
|
+
console.log('Success:', result.success); // true
|
|
309
|
+
console.log('Synced:', result.metaLink.isSynced); // true
|
|
310
|
+
|
|
311
|
+
// Bulk sync example: sync all files in storage
|
|
312
|
+
const objects = await fetch(`/assets/buckets/${bucketName}/objects`).then(r => r.json());
|
|
313
|
+
|
|
314
|
+
for (const obj of objects) {
|
|
315
|
+
await fetch(
|
|
316
|
+
`/assets/buckets/${bucketName}/objects/${encodeURIComponent(obj.name)}/meta-links`,
|
|
317
|
+
{ method: 'PUT' }
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Frontend Integration
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
// Upload file with principal association
|
|
326
|
+
async function uploadFile(file: File, principalType?: string, principalId?: string) {
|
|
327
|
+
const formData = new FormData();
|
|
328
|
+
formData.append('file', file);
|
|
329
|
+
|
|
330
|
+
const url = new URL('/assets/buckets/user-uploads/upload', window.location.origin);
|
|
331
|
+
if (principalType) url.searchParams.append('principalType', principalType);
|
|
332
|
+
if (principalId) url.searchParams.append('principalId', principalId);
|
|
333
|
+
|
|
334
|
+
const response = await fetch(url, {
|
|
335
|
+
method: 'POST',
|
|
336
|
+
body: formData,
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
const result = await response.json();
|
|
340
|
+
return result[0].link;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Download file
|
|
344
|
+
function downloadFile(bucketName: string, objectName: string) {
|
|
345
|
+
const url = `/assets/buckets/${bucketName}/objects/${encodeURIComponent(objectName)}/download`;
|
|
346
|
+
window.open(url, '_blank');
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// List files in bucket
|
|
350
|
+
async function listFiles(bucketName: string, prefix?: string, recursive?: boolean) {
|
|
351
|
+
const url = new URL(`/assets/buckets/${bucketName}/objects`, window.location.origin);
|
|
352
|
+
if (prefix) url.searchParams.append('prefix', prefix);
|
|
353
|
+
if (recursive) url.searchParams.append('recursive', 'true');
|
|
354
|
+
|
|
355
|
+
const response = await fetch(url);
|
|
356
|
+
return await response.json();
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## See Also
|
|
361
|
+
|
|
362
|
+
- [Setup & Configuration](./) - Quick Reference, Setup Steps, Configuration Options
|
|
363
|
+
- [API Reference](./api) - Controller Factory, Storage Interface, MetaLink Schema
|
|
364
|
+
- [Error Reference](./errors) - Name Validation and Troubleshooting
|