@@ -46,6 +46,7 @@ npx adminforth create-app --app-name ai-blog
4646Add modules:
4747
4848``` bash
49+ cd ai-blog
4950npm i @adminforth/upload @adminforth/rich-editor @adminforth/text-complete
5051```
5152
@@ -169,7 +170,7 @@ model Post {
169170//diff-add
170171 published Boolean
171172//diff-add
172- author User ? @relation(fields : [authorId], references: [id])
173+ author adminuser ? @relation(fields : [authorId], references: [id])
173174//diff-add
174175 authorId String?
175176//diff-add
@@ -211,7 +212,7 @@ Open `index.ts` file in root directory and update it with the following content:
211212``` ts title="./index.ts"
212213import express from ' express' ;
213214import AdminForth , { Filters , Sorts } from ' adminforth' ;
214- import userResource from ' ./resources/user .js' ;
215+ import userResource from ' ./resources/adminuser .js' ;
215216import postResource from ' ./resources/posts.js' ;
216217import contentImageResource from ' ./resources/content-image.js' ;
217218import httpProxy from ' http-proxy' ;
@@ -231,7 +232,7 @@ export const admin = new AdminForth({
231232 auth: {
232233 usersResourceId: ' adminuser' , // resource to get user during login
233234 usernameField: ' email' , // field where username is stored, should exist in resource
234- passwordHashField: ' passwordHash ' ,
235+ passwordHashField: ' password_hash ' ,
235236 },
236237 customization: {
237238 brandName: ' My Admin' ,
@@ -289,7 +290,9 @@ if (import.meta.url === `file://${process.argv[1]}`) {
289290
290291 // api to server recent posts
291292 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 ;
293296 const posts = await admin .resource (' post' ).list (
294297 [Filters .EQ (' published' , true ), ... (slug ? [Filters .LIKE (' slug' , slug )] : [])],
295298 limit ,
@@ -331,13 +334,13 @@ if (import.meta.url === `file://${process.argv[1]}`) {
331334 if (! await admin .resource (' adminuser' ).get ([Filters .EQ (' email' , ' adminforth@adminforth.dev' )])) {
332335 await admin .resource (' adminuser' ).create ({
333336 email: ' adminforth@adminforth.dev' ,
334- passwordHash : await AdminForth .Utils .generatePasswordHash (' adminforth' ),
337+ password_hash : await AdminForth .Utils .generatePasswordHash (' adminforth' ),
335338 });
336339 }
337340 });
338341
339342 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 ` )
341344 });
342345}
343346```
@@ -377,7 +380,14 @@ export default {
377380 type: AdminForthDataTypes .STRING ,
378381 },
379382 {
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' ,
381391 type: AdminForthDataTypes .DATETIME ,
382392 showIn: {
383393 edit: false ,
@@ -403,9 +413,9 @@ export default {
403413 AdminForth .Utils .PASSWORD_VALIDATORS .UP_LOW_NUM ,
404414 ],
405415 },
406- { name: ' passwordHash ' , backendOnly: true , showIn: { all: false } },
416+ { name: ' password_hash ' , backendOnly: true , showIn: { all: false } },
407417 {
408- name: ' publicName ' ,
418+ name: ' public_name ' ,
409419 type: AdminForthDataTypes .STRING ,
410420 },
411421 { name: ' avatar' },
@@ -425,7 +435,7 @@ export default {
425435 return { ok: true }
426436 },
427437 },
428- }
438+ },
429439 plugins: [
430440 new UploadPlugin ({
431441 pathColumnName: ' avatar' ,
@@ -462,6 +472,8 @@ import UploadPlugin from '@adminforth/upload';
462472import RichEditorPlugin from ' @adminforth/rich-editor' ;
463473import ChatGptPlugin from ' @adminforth/chat-gpt' ;
464474import slugify from ' slugify' ;
475+ import CompletionAdapterOpenAIChatGPT from " @adminforth/completion-adapter-open-ai-chat-gpt" ;
476+ import ImageGenerationAdapterOpenAI from ' @adminforth/image-generation-adapter-openai' ;
465477
466478export default {
467479 table: ' post' ,
@@ -561,23 +573,23 @@ export default {
561573 { originalFilename , originalExtension }: {originalFilename: string , originalExtension: string }
562574 ) => ` post-previews/${new Date ().getFullYear ()}/${randomUUID ()}/${originalFilename }.${originalExtension } ` ,
563575 generation: {
564- provider: ' openai' ,
565576 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+ }),
571581 },
572582 }),
573583 new RichEditorPlugin ({
574584 htmlFieldName: ' content' ,
575585 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 ,
579588 model: ' gpt-4o' ,
580- },
589+ expert: {
590+ temperature: 0.7
591+ }
592+ }),
581593 expert: {
582594 debounceTime: 250 ,
583595 }
@@ -618,11 +630,19 @@ export default {
618630 {
619631 name: ' id' ,
620632 primaryKey: true ,
633+ showIn: {
634+ edit: false ,
635+ create: false ,
636+ },
621637 fillOnCreate : () => randomUUID (),
622638 },
623639 {
624640 name: ' createdAt' ,
625641 type: AdminForthDataTypes .DATETIME ,
642+ showIn: {
643+ edit: false ,
644+ create: false ,
645+ },
626646 fillOnCreate : () => (new Date ()).toISOString (),
627647 },
628648 {
@@ -951,7 +971,7 @@ Open `Dockerfile` in root project directory (`ai-blog`) and put in the following
951971FROM node:20-slim
952972EXPOSE 3500
953973WORKDIR /app
954- RUN apk add --no-cache supervisor
974+ RUN apt-get update && apt-get install -y supervisor
955975COPY package.json package-lock.json ./
956976RUN npm ci
957977COPY seo/package.json seo/package-lock.json seo/
@@ -972,6 +992,8 @@ autostart=true
972992autorestart=true
973993stdout_logfile=/dev/stdout
974994stderr_logfile=/dev/stderr
995+ stdout_logfile_maxbytes = 0
996+ stderr_logfile_maxbytes = 0
975997
976998[program:seo]
977999command=sh -c "cd seo && node .output/server/index.mjs"
@@ -980,13 +1002,17 @@ autostart=true
9801002autorestart=true
9811003stdout_logfile=/dev/stdout
9821004stderr_logfile=/dev/stderr
1005+ stdout_logfile_maxbytes = 0
1006+ stderr_logfile_maxbytes = 0
9831007
9841008[program:prisma]
9851009command=npm run migrate:prod
9861010directory=/app
9871011autostart=true
9881012stdout_logfile=/dev/stdout
9891013stderr_logfile=/dev/stderr
1014+ stdout_logfile_maxbytes = 0
1015+ stderr_logfile_maxbytes = 0
9901016
9911017EOF
9921018
@@ -1011,8 +1037,8 @@ terraform*
10111037Build and run your docker container locally:
10121038
10131039``` 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
10161042```
10171043
10181044Now you can open ` http://localhost ` in your browser and see your blog.
@@ -1088,7 +1114,7 @@ data "aws_subnet" "default_subnet" {
10881114}
10891115
10901116resource "aws_security_group" "instance_sg" {
1091- name = "my-ai-blog -instance-sg"
1117+ name = "my-aiblog -instance-sg"
10921118 vpc_id = data.aws_vpc.default.id
10931119
10941120 ingress {
@@ -1118,14 +1144,14 @@ resource "aws_security_group" "instance_sg" {
11181144}
11191145
11201146resource "aws_key_pair" "deployer" {
1121- key_name = "terraform-deployer-key"
1147+ key_name = "terraform-deployer-my-aiblog- key"
11221148 public_key = file("~/.ssh/id_rsa.pub") # Path to your public SSH key
11231149}
11241150
11251151
11261152resource "aws_instance" "docker_instance" {
11271153 ami = data.aws_ami.amazon_linux.id
1128- instance_type = "t3a.micro "
1154+ instance_type = "t3a.small "
11291155 subnet_id = data.aws_subnet.default_subnet.id
11301156 vpc_security_group_ids = [aws_security_group.instance_sg.id]
11311157 key_name = aws_key_pair.deployer.key_name
@@ -1164,13 +1190,13 @@ resource "null_resource" "wait_for_user_data" {
11641190
11651191 connection {
11661192 type = "ssh"
1167- user = "ubuntu "
1193+ user = "ec2-user "
11681194 private_key = file("~/.ssh/id_rsa")
11691195 host = aws_instance.docker_instance.public_ip
11701196 }
11711197 }
11721198
1173- depends_on = [aws_instance.app_instance ]
1199+ depends_on = [aws_instance.docker_instance ]
11741200}
11751201
11761202
0 commit comments