You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: adminforth/documentation/docs/tutorial/03-Customization/02-customFieldRendering.md
+91Lines changed: 91 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -233,6 +233,97 @@ Now you can use this component in the configuration of the resource:
233
233
}
234
234
```
235
235
236
+
### Custom record editing (updating other fields)
237
+
238
+
Sometimes a custom editor needs to update not only its own field, but also other fields of the record (for example, generate a slug from a title).
239
+
240
+
For this, custom `edit`/`create` components can emit an `update:recordFieldValue` event with the payload `{ fieldName, fieldValue }`. AdminForth will update the corresponding field in the record.
241
+
242
+
```html title='./custom/TitleWithSlugEditor.vue'
243
+
<template>
244
+
<div class="flex flex-col gap-2">
245
+
<Input
246
+
:model-value="record[column.name]"
247
+
:placeholder="$t('Title')"
248
+
@update:model-value="onTitleChange"
249
+
/>
250
+
<Input
251
+
:model-value="record.slug"
252
+
:placeholder="$t('Slug')"
253
+
readonly
254
+
/>
255
+
</div>
256
+
</template>
257
+
258
+
<script setup lang="ts">
259
+
importInputfrom"@/afcl/Input.vue";
260
+
import type {
261
+
AdminForthResourceColumnCommon,
262
+
AdminForthResourceCommon,
263
+
AdminUser,
264
+
} from"@/types/Common";
265
+
266
+
constprops= defineProps<{
267
+
column: AdminForthResourceColumnCommon;
268
+
record: any;
269
+
meta: any;
270
+
resource: AdminForthResourceCommon;
271
+
adminUser: AdminUser;
272
+
readonly: boolean;
273
+
}>();
274
+
275
+
constemit=defineEmits([
276
+
"update:value", // update current column value
277
+
"update:recordFieldValue", // update any other field in the record
278
+
]);
279
+
280
+
functionslugify(value:string) {
281
+
return value
282
+
?.toLowerCase()
283
+
.trim()
284
+
.replace(/[^a-z0-9]+/g, "-")
285
+
.replace(/(^-|-$)+/g, "");
286
+
}
287
+
288
+
functiononTitleChange(newTitle:string) {
289
+
// update current column value
290
+
emit("update:value", newTitle);
291
+
292
+
// update another field in the record (e.g. slug)
293
+
emit("update:recordFieldValue", {
294
+
fieldName:"slug",
295
+
fieldValue:slugify(newTitle),
296
+
});
297
+
}
298
+
</script>
299
+
```
300
+
301
+
And use it in the resource configuration for both `edit` and `create` views:
302
+
303
+
```ts title='./resources/apartments.ts'
304
+
{
305
+
...
306
+
resourceId:'aparts',
307
+
columns: [
308
+
...
309
+
{
310
+
name:'title',
311
+
components: {
312
+
edit:'@@/TitleWithSlugEditor.vue',
313
+
create:'@@/TitleWithSlugEditor.vue',
314
+
},
315
+
},
316
+
{
317
+
name:'slug',
318
+
// standard input; value will be kept in sync
319
+
...
320
+
},
321
+
...
322
+
],
323
+
...
324
+
}
325
+
```
326
+
236
327
### Custom inValidity inside of the custom create/edit components
237
328
238
329
Custom componets can emit `update:inValidity` event to parent to say that the field is invalid.
Copy file name to clipboardExpand all lines: adminforth/documentation/docs/tutorial/05-ListOfAdapters.md
+42Lines changed: 42 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -244,10 +244,52 @@ The RAM adapter is a simplest in-memory key-value storage. Stores data in proces
244
244
245
245
Pros:
246
246
* Simplest in use - does not reqauire any external daemon.
247
+
247
248
Cones:
248
249
* In production sutable for single-process installations only
249
250
250
251
252
+
### Redis adapter
253
+
254
+
```bash
255
+
npm i @adminforth/key-value-adapter-redis
256
+
```
257
+
258
+
Redis adapter uses in-memory RAM-based Redis database with O(1) get complexity. It is great fit for most of lightweight tasks which fit in RAM. Also capable with multi-process or replica-based installations as centralized storage. Please note that Redis daemon might be not persisted to disk during restarts without additional settings, so if persistence is critical for your task - you might need to set up it separately (for many tasks like rate-limits ephemeral data are fine
adapeter.set('test-key', 'test-value', 120); //expiry in 120 seconds
269
+
270
+
```
271
+
272
+
### LevelDB adapter
273
+
274
+
```bash
275
+
npm i @adminforth/key-value-adapter-leveldb
276
+
```
277
+
278
+
LebelDB uses disk storage with o(log(n)) get complexity. Good fit for large and/or persistent KV datasets which still require fast KV access but don't efficently fit into RAM. Please not that this is a single-process adapter only, so if you will run severall processes of admin - they will not be able to work with this adapter (>=2 processes which look at same level database might lead to unpredicted behaviour - exceptions or crashes).
279
+
280
+
You can use replicas with isolated disks, however in this case state will be also separated between replicas.
Copy file name to clipboardExpand all lines: adminforth/documentation/docs/tutorial/08-Plugins/05-upload.md
+67Lines changed: 67 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -305,6 +305,73 @@ new UploadPlugin({
305
305
> adminServeBaseUrl defines the public path prefix. If your AdminForth base URL is /admin, files will be accessible under /admin/static/source/<key>.
306
306
307
307
308
+
## API
309
+
310
+
### uploadFromBufferToNewRecord
311
+
312
+
In some cases you may want to upload a file directly from your backend (for example, a file generated by a background job or received from a webhook) without going through the browser. For this, the Upload plugin exposes the `uploadFromBufferToNewRecord` method.
313
+
314
+
You can code it as custom logic or you can simply reuse Upload plugin for this purpose as well.
315
+
316
+
This method uploads a file from a Node.js `Buffer`, automatically creates a record in the corresponding resource, and returns both the stored file path and a preview URL.
317
+
318
+
```tstitle="./some-backend-service.ts"
319
+
import { admin } from'./admin'; // your AdminForth instance
320
+
321
+
...
322
+
plugins: [
323
+
newUploadPlugin({
324
+
id: 'my_reports_plugin', // unique identifier for your plugin instance
- `uploadFromBufferToNewRecord` uses the configured storage adapter (S3, local, etc.) to store the file.
345
+
- It automatically creates a new record in the resource and stores the file path into the column defined by `pathColumnName`, together with any extra `recordAttributes` you pass.
346
+
- It returns an object `{ path, previewUrl }`, where `previewUrl` is the same URL that would be used for previews inside AdminForth.
347
+
348
+
> ⚠️ It is not recommended to upload large files from the backend using `uploadFromBuffer`, because the entire file must go through your server memory and network. For large uploads you should prefer frontend presigned uploads directly to storage. You can find an example of presigned upload flow using upload plugin in the Rich editor plugin source code (Rich editor actually uses Upload plugin to upload images in edited content).
349
+
350
+
351
+
### uploadFromBufferToExistingRecord
352
+
353
+
If you already have a record and just want to replace the file referenced in its `pathColumnName` field, you can use the `uploadFromBufferToExistingRecord` method. It uploads a file from a Node.js `Buffer`, updates the existing record, and returns the new file path and preview URL.
recordId: existingRecordId, // primary key of the record to update
360
+
filename: 'report.pdf',
361
+
contentType: 'application/pdf',
362
+
buffer, // Node.js Buffer with file content
363
+
adminUser, // current admin user or system user
364
+
extra: {}, // optional extra meta for your hooks / audit
365
+
});
366
+
```
367
+
368
+
- Uses the same storage adapter and validation rules as `uploadFromBufferToNewRecord` (file extension whitelist, `maxFileSize`, `filePath` callback, etc.).
369
+
- Does not create a new record – it only updates the existing one identified by `recordId`, replacing the value in `pathColumnName` with the new storage path.
370
+
- If the generated `filePath` would be the same as the current value in the record, it throws an error to help you avoid CDN/browser caching issues. To force a refresh, make sure your `filePath` callback produces a different key (for example, include a timestamp or random UUID).
371
+
372
+
> ⚠️ The same recommendation about large files applies here: avoid using `uploadFromBufferToExistingRecord` for very large uploads; prefer a presigned upload flow from the frontend instead.
0 commit comments