Skip to content

Commit 1d1231a

Browse files
authored
Merge pull request #207 from devforth/next
Next
2 parents 22f6876 + 48f6dd2 commit 1d1231a

File tree

13 files changed

+241
-90
lines changed

13 files changed

+241
-90
lines changed

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,17 +67,20 @@ cd adminforth
6767
npm ci
6868
npm run build
6969

70-
# this will install all official plugins and link adminforth package, if plugin installed it will git pull and npm ci
71-
npm run install-plugins
7270

73-
# same for official adapters
74-
npm run install-adapters
7571
```
7672

7773
To run dev demo:
7874
```sh
7975
cd dev-demo
8076
cp .env.sample .env
77+
78+
# this will install all official plugins and link adminforth package, if plugin installed it will git pull and npm ci
79+
npm run install-plugins
80+
81+
# same for official adapters
82+
npm run install-adapters
83+
8184
npm ci
8285
npm run migrate
8386
npm start

adapters/install-adapters.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env bash
2-
ADAPTERS="adminforth-completion-adapter-open-ai-chat-gpt adminforth-email-adapter-aws-ses adminforth-google-oauth-adapter adminforth-github-oauth-adapter adminforth-facebook-oauth-adapter adminforth-keycloak-oauth-adapter adminforth-microsoft-oauth-adapter adminforth-image-generation-adapter-openai"
2+
ADAPTERS="adminforth-completion-adapter-open-ai-chat-gpt adminforth-email-adapter-aws-ses adminforth-google-oauth-adapter adminforth-github-oauth-adapter adminforth-facebook-oauth-adapter adminforth-keycloak-oauth-adapter adminforth-microsoft-oauth-adapter adminforth-image-generation-adapter-openai adminforth-storage-adapter-amazon-s3 adminforth-storage-adapter-local"
33

44
# for each
55
install_adapter() {

adminforth/documentation/docs/tutorial/05-Plugins/05-upload.md

Lines changed: 116 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ This plugin allows you to upload files to Amazon S3 bucket.
66

77
```
88
npm i @adminforth/upload --save
9+
npm i @adminforth/storage-adapter-amazon-s3 --save
910
```
1011

1112
## S3
@@ -58,6 +59,16 @@ AWS_ACCESS_KEY_ID=your_access_key_id
5859
AWS_SECRET_ACCESS_KEY=your_secret_access_key
5960
```
6061
62+
8. Add credentials in your `.env.local` file:
63+
64+
```ts title=".env.local"
65+
...
66+
//diff-add
67+
AWS_REGION=your_bucket_region
68+
//diff-add
69+
AWS_BUCKET_NAME=your_bucket_name
70+
```
71+
6172
Now add a column for storing the path to the file in the database, add this statement to the `./schema.prisma`:
6273

6374
```ts title="./schema.prisma"
@@ -89,6 +100,8 @@ Add column to `aparts` resource configuration:
89100
//diff-add
90101
import UploadPlugin from '@adminforth/upload';
91102
//diff-add
103+
import AdminForthAdapterS3Storage from '@adminforth/storage-adapter-amazon-s3'
104+
//diff-add
92105
import { v4 as uuid } from 'uuid';
93106

94107
export const admin = new AdminForth({
@@ -108,27 +121,31 @@ export const admin = new AdminForth({
108121
],
109122
plugins: [
110123
...
111-
//diff-add
124+
//diff-add
112125
new UploadPlugin({
113-
//diff-add
126+
//diff-add
127+
storageAdapter: new AdminForthAdapterS3Storage({
128+
//diff-add
129+
bucket: process.env.AWS_BUCKET_NAME,
130+
//diff-add
131+
region: process.env.AWS_REGION,
132+
//diff-add
133+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
134+
//diff-add
135+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
136+
//diff-add
137+
}),
138+
//diff-add
114139
pathColumnName: 'apartment_image',
115-
//diff-add
116-
s3Bucket: 'my-bucket', // ❗ Your bucket name
117-
//diff-add
118-
s3Region: 'us-east-1', // ❗ Selected region
119-
//diff-add
120-
s3AccessKeyId: process.env.AWS_ACCESS_KEY_ID,
121-
//diff-add
122-
s3SecretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
123-
//diff-add
140+
//diff-add
124141
allowedFileExtensions: ['jpg', 'jpeg', 'png', 'gif', 'webm', 'webp'],
125-
//diff-add
142+
//diff-add
126143
maxFileSize: 1024 * 1024 * 20, // 20 MB
127-
//diff-add
128-
s3Path: ({originalFilename, originalExtension, contentType}) =>
129-
//diff-add
144+
//diff-add
145+
filePath: ({originalFilename, originalExtension, contentType}) =>
146+
//diff-add
130147
`aparts/${new Date().getFullYear()}/${uuid()}-${originalFilename}.${originalExtension}`,
131-
//diff-add
148+
//diff-add
132149
})
133150
//diff-add
134151
]
@@ -153,13 +170,13 @@ which are generated by plugin:
153170
![alt text](Upload.png)
154171
155172
156-
> ☝ When upload feature is used on record which already exists in database (from 'edit' page), s3path callback will
173+
> ☝ When upload feature is used on record which already exists in database (from 'edit' page), filePath callback will
157174
> receive additional parameter `record` with all values of record. Generally we don't recommend denormalizing any state
158175
> of record into s3 path (and instead store links to unique path on s3 in the record field, like in example above).
159176
> But if you are 100% sure this kind of sate will be static, you might link to it:
160177
>
161178
> ```ts
162-
> s3Path: ({originalExtension, record}) => `game_images/${record.static_game_code}.${originalExtension}`
179+
> filePath: ({originalExtension, record}) => `game_images/${record.static_game_code}.${originalExtension}`
163180
> ```
164181
>
165182
> ! Please note that when upload is done from create view, record will be `undefined`.
@@ -196,27 +213,29 @@ Then you can change ACL in plugin configuration:
196213
197214
```ts title="./index.ts"
198215

199-
new UploadPlugin({
200-
pathColumnName: 'apartment_image',
201-
s3Bucket: 'my-bucket',
202-
s3Region: 'us-east-1',
203-
s3AccessKeyId: process.env.AWS_ACCESS_KEY_ID,
204-
s3SecretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
216+
new UploadPlugin({
217+
storageAdapter: new AdminForthAdapterS3Storage({
218+
bucket: process.env.AWS_BUCKET_NAME,
219+
region: process.env.AWS_REGION,
220+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
221+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
205222
//diff-add
206223
s3ACL: 'public-read',
207-
allowedFileExtensions: ['jpg', 'jpeg', 'png', 'gif', 'webm'],
208-
maxFileSize: 1024 * 1024 * 20, // 5MB
209-
s3Path: ({originalFilename, originalExtension, contentType}) =>
210-
`aparts/${new Date().getFullYear()}/${uuid()}-${originalFilename}.${originalExtension}`,
211-
})
224+
}),
225+
pathColumnName: 'apartment_image',
226+
allowedFileExtensions: ['jpg', 'jpeg', 'png', 'gif', 'webm', 'webp'],
227+
maxFileSize: 1024 * 1024 * 20, // 20 MB
228+
filePath: ({originalFilename, originalExtension, contentType}) =>
229+
`aparts/${new Date().getFullYear()}/${uuid()}-${originalFilename}.${originalExtension}`,
230+
})
212231

213232
```
214233
215-
Now every uploaded file will be public so in your custom app you can easily concatenate bucket URL with `s3Path` to get public URL:
234+
Now every uploaded file will be public so in your custom app you can easily concatenate bucket URL with `filePath` to get public URL:
216235
217236
```ts
218-
export async function getPublicUrl(s3Path: string): string {
219-
return `https://my-bucket.s3.${region}.amazonaws.com/${s3Path}`
237+
export async function getPublicUrl(filePath: string): string {
238+
return `https://my-bucket.s3.${region}.amazonaws.com/${filePath}`
220239
}
221240
```
222241
@@ -226,7 +245,7 @@ For preview in AdminForth plugin will still use presigned URLs, but you can chan
226245

227246
preview: {
228247
//diff-add
229-
previewUrl: ({s3Path}) => `https://my-bucket.s3.us-east-1.amazonaws.com/${s3Path}`,
248+
previewUrl: ({filePath}) => `https://my-bucket.s3.us-east-1.amazonaws.com/${filePath}`,
230249
}
231250
```
232251
> Make sure that you change "my-bucket" and "us-east-1" to your own settings.
@@ -240,10 +259,10 @@ If for example your domain is `my-domain.com` and you bucket has name `static.my
240259

241260
preview: {
242261
//diff-remove
243-
previewUrl: ({s3Path}) => `https://my-bucket.s3.us-east-1.amazonaws.com/${s3Path}`,
262+
previewUrl: ({filePath}) => `https://my-bucket.s3.us-east-1.amazonaws.com/${filePath}`,
244263

245264
//diff-add
246-
previewUrl: ({s3Path}) => `https://static.my-domain.com/${s3Path}`,
265+
previewUrl: ({filePath}) => `https://static.my-domain.com/${filePath}`,
247266
}
248267
```
249268
@@ -339,6 +358,59 @@ generation: {
339358
}
340359
```
341360
361+
`attachFiles` function can return an array of URLs to images which will be used as input for image generation.
362+
URLs can be absolute HTTP URLs (should be public in this case) or data-URLs (`data:image/png;base64,<base64 content of image>`).
363+
364+
For example you can use `getKeyAsDataURL` function from any storage adapter to get image as data-URL:
365+
366+
```ts title="./apartments.ts"
367+
368+
import { StorageAdapter } from 'adminforth'
369+
370+
let sourceAdapter: StorageAdapter = null;
371+
372+
columns: [
373+
...
374+
{
375+
name: 'source_image',
376+
},
377+
{
378+
name: 'destination_image',
379+
}
380+
],
381+
plugins: [
382+
...
383+
new UploadPlugin({
384+
pathColumnName: 'apartment_source',
385+
storageAdapter: (sourcesAdapter = new AdminForthStorageAdapterLocalFilesystem({
386+
fileSystemFolder: "./db/uploads",
387+
mode: "public",
388+
signingSecret: process.env.ADMINFORTH_SECRET, // secret used to generate presigned URLs
389+
}), sourcesAdapter),
390+
}),
391+
new UploadPlugin({
392+
pathColumnName: 'apartment_image',
393+
storageAdapter: new AdminForthAdapterLocalFilesystem({
394+
fileSystemFolder: "./db/uploads",
395+
mode: "public",
396+
signingSecret: process.env.ADMINFORTH_SECRET, // secret used to generate presigned URLs
397+
}),
398+
generation: {
399+
attachFiles: ({ record }) => {
400+
// get picture stored in apartment_source column as data-URL
401+
return [sourceAdapter.getKeyAsDataURL(record.apartment_source)];
402+
},
403+
generationPrompt: "Remove text from the image",
404+
countToGenerate: 3,
405+
outputSize: '1024x1024',
406+
}
407+
})
408+
]
409+
```
410+
411+
With thus setup you can upload image to `apartment_source` column, save entity and then generate new image by clicking on `Generate` button in the `apartment_image` to remove any text from the image.
412+
413+
342414
### Rate limits
343415
344416
You can set rate limits for image generation per IP address:
@@ -364,14 +436,17 @@ You can set the maximum width for the preview image in the `./resources/apartmen
364436
```ts title="./resources/apartments.ts"
365437
...
366438
new UploadPlugin({
439+
storageAdapter: new AdminForthAdapterS3Storage({
440+
bucket: process.env.AWS_BUCKET_NAME,
441+
region: process.env.AWS_REGION,
442+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
443+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
444+
s3ACL: "public-read",
445+
}),
367446
pathColumnName: 'apartment_image',
368-
s3Bucket: 'my-bucket', // ❗ Your bucket name
369-
s3Region: 'us-east-1', // ❗ Selected region
370-
s3AccessKeyId: process.env.AWS_ACCESS_KEY_ID,
371-
s3SecretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
372447
allowedFileExtensions: ['jpg', 'jpeg', 'png', 'gif', 'webm', 'webp'],
373-
maxFileSize: 1024 * 1024 * 20, // 20 MB
374-
s3Path: ({originalFilename, originalExtension, contentType}) =>
448+
maxFileSize: 5 * 1024 * 1024, // 5MB
449+
filePath: ({originalFilename, originalExtension, contentType}) =>
375450
`aparts/${new Date().getFullYear()}/${uuid()}-${originalFilename}.${originalExtension}`,
376451
preview: {
377452
// Global width settings (applies to all views if specific view settings not provided)

adminforth/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ class AdminForth implements IAdminForth {
125125
}
126126

127127
constructor(config: AdminForthInputConfig) {
128+
if (global.adminforth) {
129+
throw new Error('AdminForth instance already created in this process. '+
130+
'If you want to use multiple instances, consider using different process for each instance');
131+
}
128132
this.codeInjector = new CodeInjector(this);
129133
this.configValidator = new ConfigValidator(this, config);
130134
this.restApi = new AdminForthRestAPI(this);
@@ -145,6 +149,7 @@ class AdminForth implements IAdminForth {
145149

146150

147151
console.log(`${this.formatAdminForth()} v${ADMINFORTH_VERSION} initializing...`);
152+
global.adminforth = this;
148153
}
149154

150155
formatAdminForth() {

adminforth/modules/codeInjector.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ class CodeInjector implements ICodeInjector {
129129
});
130130
process.env.HEAVY_DEBUG && console.timeEnd(`npm ${command} done in`);
131131

132-
process.env.HEAVY_DEBUG && console.log(`🪲 npm ${command} output:`, out);
132+
// process.env.HEAVY_DEBUG && console.log(`🪲 npm ${command} output:`, out);
133133
if (err) {
134134
process.env.HEAVY_DEBUG && console.error(`🪲npm ${command} errors/warnings:`, err);
135135
}

adminforth/package-lock.json

Lines changed: 11 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

adminforth/package.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@
2020
"rollout-doc": "cd documentation && npm run build && npm run deploy",
2121
"docs": "typedoc",
2222
"--comment_postinstall": "postinstall executed after package installed in other project package and when we do npm ci in the package",
23-
"postinstall": "if test -d ./dist/spa/; then cd ./dist/spa/ && npm ci && echo 'installed spa dependencies'; fi",
24-
"install-plugins": "cd ../plugins && bash install-plugins.sh",
25-
"install-adapters": "cd ../adapters && bash install-adapters.sh"
23+
"postinstall": "if test -d ./dist/spa/; then cd ./dist/spa/ && npm ci && echo 'installed spa dependencies'; fi"
2624
},
2725
"release": {
2826
"plugins": [
@@ -62,7 +60,7 @@
6260
"@clickhouse/client": "^1.4.0",
6361
"@faker-js/faker": "^9.0.3",
6462
"@inquirer/prompts": "^7.4.1",
65-
"@types/express": "^5.0.0",
63+
"@types/express": "^4.17.21",
6664
"arg": "^5.0.2",
6765
"better-sqlite3": "^11.5.0",
6866
"chalk": "^5.4.1",

adminforth/servers/express.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ class ExpressServer implements IExpressHttpServer {
118118
}
119119
}
120120
this.expressApp.get(`${slashedPrefix}assets/*`, handler);
121+
process.env.HEAVY_DEBUG && console.log('®️ Registering SPA serve handler', `${slashedPrefix}assets/*`);
121122
this.expressApp.get(`${prefix}*`, handler);
122123

123124

0 commit comments

Comments
 (0)