@@ -46,6 +46,7 @@ npx adminforth create-app --app-name ai-blog
46
46
Add modules:
47
47
48
48
``` bash
49
+ cd ai-blog
49
50
npm i @adminforth/upload @adminforth/rich-editor @adminforth/text-complete
50
51
```
51
52
@@ -169,7 +170,7 @@ model Post {
169
170
//diff-add
170
171
published Boolean
171
172
//diff-add
172
- author User ? @relation(fields : [authorId], references: [id])
173
+ author adminuser ? @relation(fields : [authorId], references: [id])
173
174
//diff-add
174
175
authorId String?
175
176
//diff-add
@@ -211,7 +212,7 @@ Open `index.ts` file in root directory and update it with the following content:
211
212
``` ts title="./index.ts"
212
213
import express from ' express' ;
213
214
import AdminForth , { Filters , Sorts } from ' adminforth' ;
214
- import userResource from ' ./resources/user .js' ;
215
+ import userResource from ' ./resources/adminuser .js' ;
215
216
import postResource from ' ./resources/posts.js' ;
216
217
import contentImageResource from ' ./resources/content-image.js' ;
217
218
import httpProxy from ' http-proxy' ;
@@ -231,7 +232,7 @@ export const admin = new AdminForth({
231
232
auth: {
232
233
usersResourceId: ' adminuser' , // resource to get user during login
233
234
usernameField: ' email' , // field where username is stored, should exist in resource
234
- passwordHashField: ' passwordHash ' ,
235
+ passwordHashField: ' password_hash ' ,
235
236
},
236
237
customization: {
237
238
brandName: ' My Admin' ,
@@ -289,7 +290,9 @@ if (import.meta.url === `file://${process.argv[1]}`) {
289
290
290
291
// api to server recent posts
291
292
app .get (' /api/posts' , async (req , res ) => {
292
- const { offset = 0 , limit = 100 , slug = null } = req .query ;
293
+ const offset = parseInt (req .query .offset as string ) || 0 ;
294
+ const limit = parseInt (req .query .limit as string ) || 100 ;
295
+ const slug = req .query .slug as string | null ;
293
296
const posts = await admin .resource (' post' ).list (
294
297
[Filters .EQ (' published' , true ), ... (slug ? [Filters .LIKE (' slug' , slug )] : [])],
295
298
limit ,
@@ -331,13 +334,13 @@ if (import.meta.url === `file://${process.argv[1]}`) {
331
334
if (
! await admin .
resource (
' adminuser' ).
get ([
Filters .
EQ (
' email' ,
' [email protected] ' )])) {
332
335
await admin .resource (' adminuser' ).create ({
333
336
334
- passwordHash : await AdminForth .Utils .generatePasswordHash (' adminforth' ),
337
+ password_hash : await AdminForth .Utils .generatePasswordHash (' adminforth' ),
335
338
});
336
339
}
337
340
});
338
341
339
342
admin .express .listen (port , () => {
340
- console .log (` \n ⚡ AdminForth is available at http://localhost:${port }\n ` )
343
+ console .log (` \n ⚡ AdminForth is available at http://localhost:${port }/admin \n ` )
341
344
});
342
345
}
343
346
```
@@ -377,7 +380,14 @@ export default {
377
380
type: AdminForthDataTypes .STRING ,
378
381
},
379
382
{
380
- name: ' createdAt' ,
383
+ name: ' role' ,
384
+ enum: [
385
+ { value: ' superadmin' , label: ' Super Admin' },
386
+ { value: ' user' , label: ' User' },
387
+ ]
388
+ },
389
+ {
390
+ name: ' created_at' ,
381
391
type: AdminForthDataTypes .DATETIME ,
382
392
showIn: {
383
393
edit: false ,
@@ -403,9 +413,9 @@ export default {
403
413
AdminForth .Utils .PASSWORD_VALIDATORS .UP_LOW_NUM ,
404
414
],
405
415
},
406
- { name: ' passwordHash ' , backendOnly: true , showIn: { all: false } },
416
+ { name: ' password_hash ' , backendOnly: true , showIn: { all: false } },
407
417
{
408
- name: ' publicName ' ,
418
+ name: ' public_name ' ,
409
419
type: AdminForthDataTypes .STRING ,
410
420
},
411
421
{ name: ' avatar' },
@@ -425,7 +435,7 @@ export default {
425
435
return { ok: true }
426
436
},
427
437
},
428
- }
438
+ },
429
439
plugins: [
430
440
new UploadPlugin ({
431
441
pathColumnName: ' avatar' ,
@@ -462,6 +472,8 @@ import UploadPlugin from '@adminforth/upload';
462
472
import RichEditorPlugin from ' @adminforth/rich-editor' ;
463
473
import ChatGptPlugin from ' @adminforth/chat-gpt' ;
464
474
import slugify from ' slugify' ;
475
+ import CompletionAdapterOpenAIChatGPT from " @adminforth/completion-adapter-open-ai-chat-gpt" ;
476
+ import ImageGenerationAdapterOpenAI from ' @adminforth/image-generation-adapter-openai' ;
465
477
466
478
export default {
467
479
table: ' post' ,
@@ -561,23 +573,23 @@ export default {
561
573
{ originalFilename , originalExtension }: {originalFilename: string , originalExtension: string }
562
574
) => ` post-previews/${new Date ().getFullYear ()}/${randomUUID ()}/${originalFilename }.${originalExtension } ` ,
563
575
generation: {
564
- provider: ' openai' ,
565
576
countToGenerate: 2 ,
566
- openAiOptions: {
567
- model: ' gpt-4o' ,
568
- apiKey: process .env .OPENAI_API_KEY ,
569
- },
570
- fieldsForContext: [' title' ],
577
+ adapter: new ImageGenerationAdapterOpenAI ({
578
+ openAiApiKey: process .env .OPENAI_API_KEY as string ,
579
+ model: ' gpt-image-1' ,
580
+ }),
571
581
},
572
582
}),
573
583
new RichEditorPlugin ({
574
584
htmlFieldName: ' content' ,
575
585
completion: {
576
- provider: ' openai-chat-gpt' ,
577
- params: {
578
- apiKey: process .env .OPENAI_API_KEY ,
586
+ adapter: new CompletionAdapterOpenAIChatGPT ({
587
+ openAiApiKey: process .env .OPENAI_API_KEY as string ,
579
588
model: ' gpt-4o' ,
580
- },
589
+ expert: {
590
+ temperature: 0.7
591
+ }
592
+ }),
581
593
expert: {
582
594
debounceTime: 250 ,
583
595
}
@@ -618,11 +630,19 @@ export default {
618
630
{
619
631
name: ' id' ,
620
632
primaryKey: true ,
633
+ showIn: {
634
+ edit: false ,
635
+ create: false ,
636
+ },
621
637
fillOnCreate : () => randomUUID (),
622
638
},
623
639
{
624
640
name: ' createdAt' ,
625
641
type: AdminForthDataTypes .DATETIME ,
642
+ showIn: {
643
+ edit: false ,
644
+ create: false ,
645
+ },
626
646
fillOnCreate : () => (new Date ()).toISOString (),
627
647
},
628
648
{
@@ -951,7 +971,7 @@ Open `Dockerfile` in root project directory (`ai-blog`) and put in the following
951
971
FROM node:20-slim
952
972
EXPOSE 3500
953
973
WORKDIR /app
954
- RUN apk add --no-cache supervisor
974
+ RUN apt-get update && apt-get install -y supervisor
955
975
COPY package.json package-lock.json ./
956
976
RUN npm ci
957
977
COPY seo/package.json seo/package-lock.json seo/
@@ -972,6 +992,8 @@ autostart=true
972
992
autorestart=true
973
993
stdout_logfile=/dev/stdout
974
994
stderr_logfile=/dev/stderr
995
+ stdout_logfile_maxbytes = 0
996
+ stderr_logfile_maxbytes = 0
975
997
976
998
[program:seo]
977
999
command=sh -c "cd seo && node .output/server/index.mjs"
@@ -980,13 +1002,17 @@ autostart=true
980
1002
autorestart=true
981
1003
stdout_logfile=/dev/stdout
982
1004
stderr_logfile=/dev/stderr
1005
+ stdout_logfile_maxbytes = 0
1006
+ stderr_logfile_maxbytes = 0
983
1007
984
1008
[program:prisma]
985
1009
command=npm run migrate:prod
986
1010
directory=/app
987
1011
autostart=true
988
1012
stdout_logfile=/dev/stdout
989
1013
stderr_logfile=/dev/stderr
1014
+ stdout_logfile_maxbytes = 0
1015
+ stderr_logfile_maxbytes = 0
990
1016
991
1017
EOF
992
1018
@@ -1011,8 +1037,8 @@ terraform*
1011
1037
Build and run your docker container locally:
1012
1038
1013
1039
``` bash
1014
- sudo docker build -t my-ai-blog .
1015
- sudo docker run -p80:3500 -v ./prodDb:/app/db --env-file .env -it --name my-ai-blog -d my-ai-blog
1040
+ docker build -t my-ai-blog .
1041
+ docker run -p80:3500 -v ./prodDb:/app/db --env-file .env -it --name my-ai-blog -d my-ai-blog
1016
1042
```
1017
1043
1018
1044
Now you can open ` http://localhost ` in your browser and see your blog.
@@ -1088,7 +1114,7 @@ data "aws_subnet" "default_subnet" {
1088
1114
}
1089
1115
1090
1116
resource "aws_security_group" "instance_sg" {
1091
- name = "my-ai-blog -instance-sg"
1117
+ name = "my-aiblog -instance-sg"
1092
1118
vpc_id = data.aws_vpc.default.id
1093
1119
1094
1120
ingress {
@@ -1118,14 +1144,14 @@ resource "aws_security_group" "instance_sg" {
1118
1144
}
1119
1145
1120
1146
resource "aws_key_pair" "deployer" {
1121
- key_name = "terraform-deployer-key"
1147
+ key_name = "terraform-deployer-my-aiblog- key"
1122
1148
public_key = file("~/.ssh/id_rsa.pub") # Path to your public SSH key
1123
1149
}
1124
1150
1125
1151
1126
1152
resource "aws_instance" "docker_instance" {
1127
1153
ami = data.aws_ami.amazon_linux.id
1128
- instance_type = "t3a.micro "
1154
+ instance_type = "t3a.small "
1129
1155
subnet_id = data.aws_subnet.default_subnet.id
1130
1156
vpc_security_group_ids = [aws_security_group.instance_sg.id]
1131
1157
key_name = aws_key_pair.deployer.key_name
@@ -1164,13 +1190,13 @@ resource "null_resource" "wait_for_user_data" {
1164
1190
1165
1191
connection {
1166
1192
type = "ssh"
1167
- user = "ubuntu "
1193
+ user = "ec2-user "
1168
1194
private_key = file("~/.ssh/id_rsa")
1169
1195
host = aws_instance.docker_instance.public_ip
1170
1196
}
1171
1197
}
1172
1198
1173
- depends_on = [aws_instance.app_instance ]
1199
+ depends_on = [aws_instance.docker_instance ]
1174
1200
}
1175
1201
1176
1202
0 commit comments