From 54831e5d676d2b25ccae30a7010175b84f426a62 Mon Sep 17 00:00:00 2001 From: Ivan Borshchov Date: Thu, 10 Oct 2024 11:29:30 +0300 Subject: [PATCH] Deploy website - based on c55a06d5d79e8d87f4a9c2d354d603bca138e156 --- 404.html | 2 +- assets/js/0f528969.6a8b9a79.js | 1 - assets/js/0f528969.72e0fac7.js | 1 + assets/js/4a260936.a8f76e67.js | 1 - assets/js/4a260936.c636fabf.js | 1 + assets/js/dbfc4782.1aa13a01.js | 1 - assets/js/dbfc4782.b2d59e40.js | 1 + ...ime~main.b942f566.js => runtime~main.9a4d0037.js} | 2 +- blog/ai-blog/index.html | 12 +++++++----- blog/archive/index.html | 2 +- blog/atom.xml | 10 ++++++---- blog/chatgpt-plugin/index.html | 2 +- blog/index.html | 12 +++++++----- blog/rss.xml | 10 ++++++---- blog/tags/chatgpt/index.html | 10 ++++++---- blog/tags/index.html | 2 +- blog/tags/nuxt/index.html | 10 ++++++---- blog/tags/plugin/index.html | 2 +- docs/api/index.html | 2 +- docs/api/plugins/audit-log/types/index.html | 2 +- .../types/type-aliases/PluginOptions/index.html | 2 +- docs/api/plugins/chat-gpt/types/index.html | 2 +- .../types/interfaces/PluginOptions/index.html | 2 +- .../plugins/email-password-reset/types/index.html | 2 +- .../types/interfaces/PluginOptions/index.html | 2 +- .../api/plugins/foreign-inline-list/types/index.html | 2 +- .../types/type-aliases/PluginOptions/index.html | 2 +- docs/api/plugins/import-export/types/index.html | 2 +- .../types/interfaces/PluginOptions/index.html | 2 +- docs/api/plugins/rich-editor/types/index.html | 2 +- .../types/interfaces/PluginOptions/index.html | 2 +- docs/api/plugins/two-factors-auth/types/index.html | 2 +- .../types/type-aliases/PluginOptions/index.html | 2 +- docs/api/plugins/upload/types/index.html | 2 +- .../types/type-aliases/PluginOptions/index.html | 2 +- .../AdminForthConfig/classes/Filters/index.html | 2 +- .../types/AdminForthConfig/classes/Sorts/index.html | 2 +- .../enumerations/ActionCheckSource/index.html | 2 +- .../enumerations/AdminForthDataTypes/index.html | 2 +- .../AdminForthFilterOperators/index.html | 2 +- .../enumerations/AdminForthMenuTypes/index.html | 2 +- .../enumerations/AdminForthResourcePages/index.html | 2 +- .../enumerations/AdminForthSortDirections/index.html | 2 +- .../enumerations/AllowedActionsEnum/index.html | 2 +- docs/api/types/AdminForthConfig/index.html | 2 +- .../interfaces/IAdminForth/index.html | 2 +- .../interfaces/IAdminForthAuth/index.html | 2 +- .../IAdminForthDataSourceConnector/index.html | 2 +- .../IAdminForthDataSourceConnectorBase/index.html | 2 +- .../index.html | 2 +- .../interfaces/IAdminForthFilter/index.html | 2 +- .../interfaces/IAdminForthHttpResponse/index.html | 2 +- .../interfaces/IAdminForthPlugin/index.html | 2 +- .../interfaces/IAdminForthSort/index.html | 2 +- .../interfaces/ICodeInjector/index.html | 2 +- .../interfaces/IConfigValidator/index.html | 2 +- .../interfaces/IExpressHttpServer/index.html | 2 +- .../interfaces/IHttpServer/index.html | 2 +- .../interfaces/IOperationalResource/index.html | 2 +- .../type-aliases/AdminForthBulkAction/index.html | 2 +- .../type-aliases/AdminForthColumnEnumItem/index.html | 2 +- .../AdminForthComponentDeclaration/index.html | 2 +- .../AdminForthComponentDeclarationFull/index.html | 2 +- .../type-aliases/AdminForthConfig/index.html | 2 +- .../type-aliases/AdminForthConfigMenuItem/index.html | 2 +- .../type-aliases/AdminForthDataSource/index.html | 2 +- .../AdminForthFieldComponents/index.html | 2 +- .../AdminForthForeignResource/index.html | 2 +- .../type-aliases/AdminForthResource/index.html | 2 +- .../type-aliases/AdminForthResourceColumn/index.html | 2 +- .../type-aliases/AdminUser/index.html | 2 +- .../AfterDataSourceResponseFunction/index.html | 2 +- .../type-aliases/AfterSaveFunction/index.html | 2 +- .../type-aliases/AllowedActionValue/index.html | 2 +- .../type-aliases/AllowedActions/index.html | 2 +- .../type-aliases/AllowedActionsResolved/index.html | 2 +- .../BeforeDataSourceRequestFunction/index.html | 2 +- .../BeforeLoginConfirmationFunction/index.html | 2 +- .../type-aliases/BeforeSaveFunction/index.html | 2 +- .../type-aliases/FDataFilter/index.html | 2 +- .../type-aliases/FDataSort/index.html | 2 +- .../type-aliases/ValidationObject/index.html | 2 +- .../FrontendAPI/enumerations/AlertVariant/index.html | 2 +- docs/api/types/FrontendAPI/index.html | 2 +- .../interfaces/FrontendAPIInterface/index.html | 2 +- .../FrontendAPI/type-aliases/AlertParams/index.html | 2 +- .../type-aliases/ConfirmParams/index.html | 2 +- docs/tutorial/Advanced/plugin-development/index.html | 2 +- docs/tutorial/Customization/alert/index.html | 2 +- docs/tutorial/Customization/branding/index.html | 2 +- docs/tutorial/Customization/bulkActions/index.html | 2 +- .../Customization/customFieldRendering/index.html | 2 +- docs/tutorial/Customization/customPages/index.html | 2 +- docs/tutorial/Customization/dataApi/index.html | 2 +- docs/tutorial/Customization/hooks/index.html | 2 +- .../tutorial/Customization/limitingAccess/index.html | 2 +- .../Customization/menuConfiguration/index.html | 2 +- .../tutorial/Customization/pageInjections/index.html | 2 +- docs/tutorial/Customization/security/index.html | 2 +- .../tutorial/Customization/virtualColumns/index.html | 2 +- docs/tutorial/Plugins/AuditLog/index.html | 2 +- docs/tutorial/Plugins/ForeignInlineList/index.html | 2 +- docs/tutorial/Plugins/RichEditor/index.html | 2 +- docs/tutorial/Plugins/TwoFactorsAuth/index.html | 2 +- docs/tutorial/Plugins/chat-gpt/index.html | 2 +- .../tutorial/Plugins/email-password-reset/index.html | 2 +- docs/tutorial/Plugins/import-export/index.html | 2 +- docs/tutorial/Plugins/upload/index.html | 2 +- docs/tutorial/deploy/index.html | 2 +- docs/tutorial/gettingStarted/index.html | 2 +- docs/tutorial/glossary/index.html | 2 +- docs/tutorial/helloWorld/index.html | 2 +- index.html | 2 +- search/index.html | 2 +- 114 files changed, 143 insertions(+), 131 deletions(-) delete mode 100644 assets/js/0f528969.6a8b9a79.js create mode 100644 assets/js/0f528969.72e0fac7.js delete mode 100644 assets/js/4a260936.a8f76e67.js create mode 100644 assets/js/4a260936.c636fabf.js delete mode 100644 assets/js/dbfc4782.1aa13a01.js create mode 100644 assets/js/dbfc4782.b2d59e40.js rename assets/js/{runtime~main.b942f566.js => runtime~main.9a4d0037.js} (97%) diff --git a/404.html b/404.html index 58fde02a5..9acd89f36 100644 --- a/404.html +++ b/404.html @@ -15,7 +15,7 @@ - + diff --git a/assets/js/0f528969.6a8b9a79.js b/assets/js/0f528969.6a8b9a79.js deleted file mode 100644 index ab84df4de..000000000 --- a/assets/js/0f528969.6a8b9a79.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkadminforth=self.webpackChunkadminforth||[]).push([[430],{999:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>i,default:()=>p,frontMatter:()=>o,metadata:()=>a,toc:()=>d});var r=t(4848),s=t(8453);const o={slug:"ai-blog",title:"AI-Assisted blog with AdminForth and Nuxt in a minutes",authors:"ivanb",tags:["nuxt","chatgpt"]},i=void 0,a={permalink:"/blog/ai-blog",source:"@site/blog/2024-10-01-ai-blog/index.md",title:"AI-Assisted blog with AdminForth and Nuxt in a minutes",description:"Many developers today are using copilots to write code faster and think less about syntax.",date:"2024-10-01T00:00:00.000Z",tags:[{inline:!0,label:"nuxt",permalink:"/blog/tags/nuxt"},{inline:!1,label:"ChatGPT",permalink:"/blog/tags/chatgpt",description:"ChatGPT is a conversational AI model that can generate human-like responses to text inputs."}],readingTime:17.685,hasTruncateMarker:!1,authors:[{name:"Ivan Borshcho",title:"Maintainer of AdminForth",url:"https://github.com/ivictbor",imageURL:"https://avatars.githubusercontent.com/u/1838656?v=4",key:"ivanb"}],frontMatter:{slug:"ai-blog",title:"AI-Assisted blog with AdminForth and Nuxt in a minutes",authors:"ivanb",tags:["nuxt","chatgpt"]},unlisted:!1,nextItem:{title:"Chat-GPT plugin to co-write texts and strings",permalink:"/blog/chatgpt-plugin"}},l={authorsImageUrls:[void 0]},d=[{value:"Prerequirements",id:"prerequirements",level:2},{value:"Step 1: Create a new AdminForth project",id:"step-1-create-a-new-adminforth-project",level:2},{value:"Step 2: Prepare environment",id:"step-2-prepare-environment",level:2},{value:"OpenAI",id:"openai",level:3},{value:"S3",id:"s3",level:3},{value:"Create .env file in project directory",id:"create-env-file-in-project-directory",level:3},{value:"Step 3: Initialize database",id:"step-3-initialize-database",level:2},{value:"Step 4: Setting up AdminForth",id:"step-4-setting-up-adminforth",level:2},{value:"Step 5: Create resources",id:"step-5-create-resources",level:2},{value:"Step 5: Create Nuxt project",id:"step-5-create-nuxt-project",level:2},{value:"Step 6: Deploy",id:"step-6-deploy",level:2},{value:"Deploy to EC2",id:"deploy-to-ec2",level:3},{value:"Add HTTPs and CDN",id:"add-https-and-cdn",level:3}];function c(e){const n={a:"a",blockquote:"blockquote",code:"code",h2:"h2",h3:"h3",img:"img",li:"li",ol:"ol",p:"p",pre:"pre",ul:"ul",...(0,s.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.p,{children:"Many developers today are using copilots to write code faster and think less about syntax."}),"\n",(0,r.jsx)(n.p,{children:"But what about writing plain text? For example blogs and micro-blogs? Sometimes you want to share your progress and thoughts but you are lazy for typing. Then you can give a try to AI-assisted blogging. Our Open-Source AdminForth framework has couple of new AI-capable plugins to write text and generate images."}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.img,{alt:"alt text",src:t(2397).A+"",width:"1999",height:"1499"})}),"\n",(0,r.jsxs)(n.p,{children:["You can also touch it live ",(0,r.jsx)(n.a,{href:"https://blog-demo.adminforth.dev/admin",children:"here"}),"."]}),"\n",(0,r.jsx)(n.p,{children:"For AI plugins are backed by OpenAI API, but their architecture allows to be easily extended for other AI providers once OpenAI competitors will reach the same or better level of quality."}),"\n",(0,r.jsx)(n.p,{children:"Here we will suggest you simple as 1-2-3 steps to build and host a blog with AI assistant which will help you to write posts."}),"\n",(0,r.jsx)(n.p,{children:"Our tech stack will include:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"https://nuxt.com/",children:"Nuxt.js"})," - SEO-friendly page rendering framework"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"https://adminforth.dev/",children:"AdminForth"})," - Admin panel framework for creating posts"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"https://adminforth.dev/docs/tutorial/Plugins/RichEditor/",children:"AdminForth RichEditor plugin"})," - WYSIWYG editor with AI assistant in Copilot style"]}),"\n",(0,r.jsx)(n.li,{children:"Node and typescript"}),"\n",(0,r.jsx)(n.li,{children:"Prisma for migrations"}),"\n",(0,r.jsx)(n.li,{children:"SQLite for database, though you can easily switch it to Postgres or MongoDB"}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"prerequirements",children:"Prerequirements"}),"\n",(0,r.jsxs)(n.p,{children:["We will use Node v20, if you not have it installed, we recommend ",(0,r.jsx)(n.a,{href:"https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script",children:"NVM"})]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"nvm install 20\nnvm alias default 20\nnvm use 20\n"})}),"\n",(0,r.jsx)(n.h2,{id:"step-1-create-a-new-adminforth-project",children:"Step 1: Create a new AdminForth project"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"mkdir ai-blog\ncd ai-blog\nnpm init -y\nnpm install adminforth @adminforth/upload @adminforth/rich-editor @adminforth/chat-gpt \\\n express slugify http-proxy @types/express typescript tsx @types/node --save-dev\nnpx --yes tsc --init --module ESNext --target ESNext\n"})}),"\n",(0,r.jsx)(n.h2,{id:"step-2-prepare-environment",children:"Step 2: Prepare environment"}),"\n",(0,r.jsx)(n.h3,{id:"openai",children:"OpenAI"}),"\n",(0,r.jsxs)(n.p,{children:["To allocate OpenAI API key, go to ",(0,r.jsx)(n.a,{href:"https://platform.openai.com/",children:"https://platform.openai.com/"}),", open Dashboard -> API keys -> Create new secret key."]}),"\n",(0,r.jsx)(n.h3,{id:"s3",children:"S3"}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsxs)(n.li,{children:["Go to ",(0,r.jsx)(n.a,{href:"https://aws.amazon.com",children:"https://aws.amazon.com"})," and login."]}),"\n",(0,r.jsxs)(n.li,{children:["Go to Services -> S3 and create a bucket. Put in bucket name e.g. ",(0,r.jsx)(n.code,{children:"my-ai-blog-bucket"}),'.\nFirst of all go to your bucket settings, Permissions, scroll down to Block public access (bucket settings for this bucket) and uncheck all checkboxes.\nGo to bucket settings, Permissions, Object ownership and select "ACLs Enabled" and "Bucket owner preferred" radio buttons.']}),"\n",(0,r.jsx)(n.li,{children:"Go to bucket settings, Permissions, scroll down to Cross-origin resource sharing (CORS) and put in the following configuration:"}),"\n"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-json",children:'[\n {\n "AllowedHeaders": [\n "*"\n ],\n "AllowedMethods": [\n "PUT"\n ],\n "AllowedOrigins": [\n "http://localhost:3500"\n ],\n "ExposeHeaders": []\n }\n]\n'})}),"\n",(0,r.jsxs)(n.blockquote,{children:["\n",(0,r.jsxs)(n.p,{children:["\u261d\ufe0f In AllowedOrigins add all your domains. For example if you will serve blog and admin on ",(0,r.jsx)(n.code,{children:"https://example.com/"})," you should add\n",(0,r.jsx)(n.code,{children:'"https://example.com"'})," to AllowedOrigins:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-json",children:'[\n "https://example.com",\n "http://localhost:3500"\n]\n'})}),"\n",(0,r.jsxs)(n.p,{children:["Every character matters, so don't forget to add ",(0,r.jsx)(n.code,{children:"http://"})," or ",(0,r.jsx)(n.code,{children:"https://"}),"!"]}),"\n"]}),"\n",(0,r.jsxs)(n.ol,{start:"4",children:["\n",(0,r.jsxs)(n.li,{children:["Go to Services -> IAM and create a new user. Put in user name e.g. ",(0,r.jsx)(n.code,{children:"my-ai-blog-bucket"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:["Attach existing policies directly -> ",(0,r.jsx)(n.code,{children:"AmazonS3FullAccess"}),". Go to your user -> ",(0,r.jsx)(n.code,{children:"Add permissions"})," -> ",(0,r.jsx)(n.code,{children:"Attach policies directly"})," -> ",(0,r.jsx)(n.code,{children:"AmazonS3FullAccess"})]}),"\n",(0,r.jsxs)(n.li,{children:["Go to Security credentials and create a new access key. Save ",(0,r.jsx)(n.code,{children:"Access key ID"})," and ",(0,r.jsx)(n.code,{children:"Secret access key"}),"."]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"create-env-file-in-project-directory",children:"Create .env file in project directory"}),"\n",(0,r.jsxs)(n.p,{children:["Create ",(0,r.jsx)(n.code,{children:".env"})," file with the following content:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",metastring:'title=".env"',children:"DATABASE_URL=file:./db/db.sqlite\nADMINFORTH_SECRET=123\nOPENAI_API_KEY=...\nAWS_ACCESS_KEY_ID=your_access_key_id\nAWS_SECRET_ACCESS_KEY=your_secret_access_key\nAWS_S3_BUCKET=my-ai-blog-bucket\nAWS_S3_REGION=us-east-1\n"})}),"\n",(0,r.jsx)(n.h2,{id:"step-3-initialize-database",children:"Step 3: Initialize database"}),"\n",(0,r.jsxs)(n.p,{children:["Create ",(0,r.jsx)(n.code,{children:"./schema.prisma"})," and put next content there:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-yaml",metastring:'title="./schema.prisma" ',children:'generator client {\n provider = "prisma-client-js"\n}\n\ndatasource db {\n provider = "sqlite"\n url = env("DATABASE_URL")\n}\n\nmodel User {\n id String @id\n createdAt DateTime \n email String @unique\n avatar String?\n publicName String?\n passwordHash String\n posts Post[]\n}\n\nmodel Post {\n id String @id\n createdAt DateTime \n title String\n slug String\n picture String?\n content String\n published Boolean \n author User? @relation(fields: [authorId], references: [id])\n authorId String?\n contentImages ContentImage[]\n}\n\nmodel ContentImage {\n id String @id\n createdAt DateTime \n img String\n postId String\n resourceId String\n post Post @relation(fields: [postId], references: [id])\n}\n'})}),"\n",(0,r.jsxs)(n.p,{children:["Create database using ",(0,r.jsx)(n.code,{children:"prisma migrate"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"npx -y prisma migrate dev --name init\n"})}),"\n",(0,r.jsx)(n.h2,{id:"step-4-setting-up-adminforth",children:"Step 4: Setting up AdminForth"}),"\n",(0,r.jsxs)(n.p,{children:["Open ",(0,r.jsx)(n.code,{children:"package.json"}),", set ",(0,r.jsx)(n.code,{children:"type"})," to ",(0,r.jsx)(n.code,{children:"module"})," and add ",(0,r.jsx)(n.code,{children:"start"})," script:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-json",metastring:'title="./package.json"',children:'{\n ...\n//diff-add\n "type": "module",\n "scripts": {\n ...\n//diff-add\n "start": "NODE_ENV=development tsx watch --env-file=.env index.ts",\n//diff-add\n "startLive": "NODE_ENV=production APP_PORT=80 tsx index.ts"\n },\n}\n'})}),"\n",(0,r.jsxs)(n.p,{children:["Create ",(0,r.jsx)(n.code,{children:"index.ts"})," file in root directory with following content:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",metastring:'title="./index.ts"',children:"import express from 'express';\nimport AdminForth, { Filters, Sorts } from 'adminforth';\nimport userResource from './res/user.js';\nimport postResource from './res/posts.js';\nimport contentImageResource from './res/content-image.js';\nimport httpProxy from 'http-proxy';\n\ndeclare var process : {\n env: {\n DATABASE_URL: string\n NODE_ENV: string,\n AWS_S3_BUCKET: string,\n AWS_S3_REGION: string,\n }\n argv: string[]\n}\n\nexport const admin = new AdminForth({\n baseUrl: '/admin',\n auth: {\n usersResourceId: 'user', // resource to get user during login\n usernameField: 'email', // field where username is stored, should exist in resource\n passwordHashField: 'passwordHash',\n },\n customization: {\n brandName: 'My Admin',\n datesFormat: 'D MMM',\n timeFormat: 'HH:mm',\n emptyFieldPlaceholder: '-',\n styles: {\n colors: {\n light: {\n // color for links, icons etc.\n primary: 'rgb(47 37 227)',\n // color for sidebar and text\n sidebar: {main:'#EFF5F7', text:'#333'},\n },\n }\n }\n },\n dataSources: [{\n id: 'maindb',\n url: process.env.DATABASE_URL?.replace('file:', 'sqlite://'),\n }],\n resources: [\n userResource,\n postResource,\n contentImageResource,\n ],\n menu: [\n {\n homepage: true,\n label: 'Posts',\n icon: 'flowbite:home-solid',\n resourceId: 'post',\n },\n { type: 'gap' },\n { type: 'divider' },\n { type: 'heading', label: 'SYSTEM' },\n {\n label: 'Users',\n icon: 'flowbite:user-solid',\n resourceId: 'user',\n }\n ],\n});\n\n\nif (import.meta.url === `file://${process.argv[1]}`) {\n // if script is executed directly e.g. node index.ts or npm start\n\n const app = express()\n app.use(express.json());\n const port = 3500;\n\n // needed to compile SPA. Call it here or from a build script e.g. in Docker build time to reduce downtime\n if (process.env.NODE_ENV === 'development') {\n await admin.bundleNow({ hotReload: true });\n }\n console.log('Bundling AdminForth done. For faster serving consider calling bundleNow() from a build script.');\n\n // api to server recent posts\n app.get('/api/posts', async (req, res) => {\n const { offset = 0, limit = 100, slug = null } = req.query;\n const posts = await admin.resource('post').list(\n [Filters.EQ('published', true), ...(slug ? [Filters.LIKE('slug', slug)] : [])],\n limit,\n offset,\n Sorts.DESC('createdAt'),\n );\n const authorIds = [...new Set(posts.map((p: any) => p.authorId))];\n const authors = (await admin.resource('user').list(Filters.IN('id', authorIds)))\n .reduce((acc: any, a: any) => {acc[a.id] = a; return acc;}, {});\n posts.forEach((p: any) => {\n const author = authors[p.authorId];\n p.author = { \n publicName: author.publicName, \n avatar: `https://${process.env.AWS_S3_BUCKET}.s3.${process.env.AWS_S3_REGION}.amazonaws.com/${author.avatar}`\n };\n p.picture = `https://${process.env.AWS_S3_BUCKET}.s3.${process.env.AWS_S3_REGION}.amazonaws.com/${p.picture}`;\n });\n res.json(posts);\n });\n\n // here we proxy all non-/admin requests to nuxt instance http://localhost:3000\n // this is done for demo purposes, in production you should do this using high-performance reverse proxy like traefik or nginx\n app.use((req, res, next) => {\n if (!req.url.startsWith('/admin')) {\n const proxy = httpProxy.createProxyServer();\n proxy.on('error', function (err, req, res) {\n res.send(`No response from Nuxt at http://localhost:3000, did you start it? ${err}`)\n });\n proxy.web(req, res, { target: 'http://localhost:3000' });\n } else {\n next();\n }\n });\n\n // serve after you added all api\n admin.express.serve(app)\n\n admin.discoverDatabases().then(async () => {\n if (!await admin.resource('user').get([Filters.IN('email', 'adminforth')])) {\n await admin.resource('user').create({\n email: 'adminforth@adminforth.dev',\n passwordHash: await AdminForth.Utils.generatePasswordHash('adminforth'),\n });\n }\n });\n\n app.listen(port, () => {\n console.log(`\\n\u26a1 AdminForth is available at http://localhost:${port}\\n`)\n });\n}\n"})}),"\n",(0,r.jsx)(n.h2,{id:"step-5-create-resources",children:"Step 5: Create resources"}),"\n",(0,r.jsxs)(n.p,{children:["Create ",(0,r.jsx)(n.code,{children:"res"})," folder. Create ",(0,r.jsx)(n.code,{children:"./res/user.ts"})," file with following content:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",metastring:'title="./res/users.ts"',children:"import AdminForth, { AdminForthDataTypes } from 'adminforth';\nimport { randomUUID } from 'crypto';\nimport UploadPlugin from '@adminforth/upload';\n\nexport default {\n dataSource: 'maindb',\n table: 'user',\n label: 'Users',\n recordLabel: (r: any) => `\ud83d\udc64 ${r.email}`,\n columns: [\n {\n name: 'id',\n primaryKey: true,\n fillOnCreate: () => randomUUID(),\n showIn: ['list', 'filter', 'show'],\n },\n {\n name: 'email',\n required: true,\n isUnique: true,\n enforceLowerCase: true,\n validation: [\n AdminForth.Utils.EMAIL_VALIDATOR,\n ],\n type: AdminForthDataTypes.STRING,\n },\n {\n name: 'createdAt',\n type: AdminForthDataTypes.DATETIME,\n showIn: ['list', 'filter', 'show'],\n fillOnCreate: () => (new Date()).toISOString(),\n },\n {\n name: 'password',\n virtual: true,\n required: { create: true },\n editingNote: { edit: 'Leave empty to keep password unchanged' },\n minLength: 8,\n type: AdminForthDataTypes.STRING,\n showIn: ['create', 'edit'],\n masked: true,\n validation: [\n // request to have at least 1 digit, 1 upper case, 1 lower case\n AdminForth.Utils.PASSWORD_VALIDATORS.UP_LOW_NUM,\n ],\n },\n { name: 'passwordHash', backendOnly: true, showIn: [] },\n { \n name: 'publicName',\n type: AdminForthDataTypes.STRING,\n },\n { name: 'avatar' },\n ],\n hooks: {\n create: {\n beforeSave: async ({ record, adminUser, resource }) => {\n record.passwordHash = await AdminForth.Utils.generatePasswordHash(record.password);\n return { ok: true };\n }\n },\n edit: {\n beforeSave: async ({ record, adminUser, resource }) => {\n if (record.password) {\n record.passwordHash = await AdminForth.Utils.generatePasswordHash(record.password);\n }\n return { ok: true }\n },\n },\n }\n plugins: [\n new UploadPlugin({\n pathColumnName: 'avatar',\n s3Bucket: process.env.AWS_S3_BUCKET,\n s3Region: process.env.AWS_S3_REGION,\n allowedFileExtensions: ['jpg', 'jpeg', 'png', 'gif', 'webm','webp'],\n maxFileSize: 1024 * 1024 * 20, // 20MB\n s3AccessKeyId: process.env.AWS_ACCESS_KEY_ID,\n s3SecretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,\n s3ACL: 'public-read', // ACL which will be set to uploaded file\n s3Path: (\n { originalFilename, originalExtension }: {originalFilename: string, originalExtension: string }\n ) => `user-avatars/${new Date().getFullYear()}/${randomUUID()}/${originalFilename}.${originalExtension}`,\n generation: {\n provider: 'openai-dall-e',\n countToGenerate: 2,\n openAiOptions: {\n model: 'dall-e-3',\n size: '1024x1024',\n apiKey: process.env.OPENAI_API_KEY,\n },\n },\n }),\n ],\n}\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Create ",(0,r.jsx)(n.code,{children:"posts.ts"})," file in res directory with following content:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",metastring:'title="./res/post.ts"',children:"import { AdminUser, AdminForthDataTypes } from 'adminforth';\nimport { randomUUID } from 'crypto';\nimport UploadPlugin from '@adminforth/upload';\nimport RichEditorPlugin from '@adminforth/rich-editor';\nimport ChatGptPlugin from '@adminforth/chat-gpt';\nimport slugify from 'slugify';\n\nexport default {\n table: 'post',\n dataSource: 'maindb',\n label: 'Posts',\n recordLabel: (r: any) => `\ud83d\udcdd ${r.title}`,\n columns: [\n {\n name: 'id',\n primaryKey: true,\n fillOnCreate: () => randomUUID(),\n showIn: ['filter', 'show'],\n },\n {\n name: 'title',\n required: true,\n showIn: ['list', 'create', 'edit', 'filter', 'show'],\n maxLength: 255,\n minLength: 3,\n type: AdminForthDataTypes.STRING,\n },\n {\n name: 'picture',\n showIn: ['list', 'create', 'edit', 'filter', 'show'],\n },\n {\n name: 'slug',\n showIn: ['filter', 'show'],\n },\n {\n name: 'content',\n showIn: ['create', 'edit', 'filter', 'show'],\n type: AdminForthDataTypes.RICHTEXT,\n },\n {\n name: 'createdAt',\n showIn: ['list', 'filter', 'show',],\n fillOnCreate: () => (new Date()).toISOString(),\n },\n {\n name: 'published',\n required: true,\n },\n {\n name: 'authorId',\n foreignResource: {\n resourceId: 'user',\n },\n showIn: ['filter', 'show'],\n fillOnCreate: ({ adminUser }: { adminUser: AdminUser }) => {\n return adminUser.dbUser.id;\n }\n }\n ],\n hooks: {\n create: {\n beforeSave: async ({ record, adminUser }: { record: any, adminUser: AdminUser }) => {\n record.slug = slugify(record.title, { lower: true });\n return { ok: true };\n },\n },\n edit: {\n beforeSave: async ({ record, adminUser }: { record: any, adminUser: AdminUser }) => {\n if (record.title) {\n record.slug = slugify(record.title, { lower: true });\n }\n return { ok: true };\n },\n },\n },\n plugins: [\n new UploadPlugin({\n pathColumnName: 'picture',\n s3Bucket: process.env.AWS_S3_BUCKET,\n s3Region: process.env.AWS_S3_REGION,\n allowedFileExtensions: ['jpg', 'jpeg', 'png', 'gif', 'webm','webp'],\n maxFileSize: 1024 * 1024 * 20, // 20MB\n s3AccessKeyId: process.env.AWS_ACCESS_KEY_ID,\n s3SecretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,\n s3ACL: 'public-read', // ACL which will be set to uploaded file\n s3Path: (\n { originalFilename, originalExtension }: {originalFilename: string, originalExtension: string }\n ) => `post-previews/${new Date().getFullYear()}/${randomUUID()}/${originalFilename}.${originalExtension}`,\n generation: {\n provider: 'openai-dall-e',\n countToGenerate: 2,\n openAiOptions: {\n model: 'dall-e-3',\n size: '1792x1024',\n apiKey: process.env.OPENAI_API_KEY,\n },\n fieldsForContext: ['title'],\n },\n }),\n new RichEditorPlugin({\n htmlFieldName: 'content',\n completion: {\n provider: 'openai-chat-gpt',\n params: {\n apiKey: process.env.OPENAI_API_KEY,\n model: 'gpt-4o',\n },\n expert: {\n debounceTime: 250,\n }\n }, \n attachments: {\n attachmentResource: 'contentImage',\n attachmentFieldName: 'img',\n attachmentRecordIdFieldName: 'postId',\n attachmentResourceIdFieldName: 'resourceId',\n },\n }),\n new ChatGptPlugin({\n openAiApiKey: process.env.OPENAI_API_KEY,\n model: 'gpt-4o',\n fieldName: 'title',\n expert: {\n debounceTime: 250,\n }\n }),\n ]\n}\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Also create ",(0,r.jsx)(n.code,{children:"content-image.ts"})," file in ",(0,r.jsx)(n.code,{children:"res"})," directory with following content:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",metastring:'title="./res/content-image.ts"',children:"\nimport { AdminForthDataTypes } from 'adminforth';\nimport { randomUUID } from 'crypto';\nimport UploadPlugin from '@adminforth/upload';\n\nexport default {\n table: 'contentImage',\n dataSource: 'maindb',\n label: 'Content Images',\n recordLabel: (r: any) => `\ud83d\uddbc\ufe0f ${r.img}`,\n columns: [\n {\n name: 'id',\n primaryKey: true,\n fillOnCreate: () => randomUUID(),\n },\n {\n name: 'createdAt',\n type: AdminForthDataTypes.DATETIME,\n fillOnCreate: () => (new Date()).toISOString(),\n },\n {\n name: 'img',\n type: AdminForthDataTypes.STRING,\n required: true,\n },\n {\n name: 'postId',\n foreignResource: {\n resourceId: 'post',\n },\n showIn: ['list', 'filter', 'show'],\n },\n {\n name: 'resourceId',\n }\n ],\n plugins: [\n new UploadPlugin({\n pathColumnName: 'img',\n s3Bucket: process.env.AWS_S3_BUCKET,\n s3Region: process.env.AWS_S3_REGION,\n allowedFileExtensions: ['jpg', 'jpeg', 'png', 'gif', 'webm','webp'],\n maxFileSize: 1024 * 1024 * 20, // 20MB\n s3AccessKeyId: process.env.AWS_ACCESS_KEY_ID,\n s3SecretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,\n s3ACL: 'public-read', // ACL which will be set to uploaded file\n s3Path: (\n { originalFilename, originalExtension }: {originalFilename: string, originalExtension: string }\n ) => `post-content/${new Date().getFullYear()}/${randomUUID()}/${originalFilename}.${originalExtension}`,\n }),\n ],\n}\n"})}),"\n",(0,r.jsx)(n.p,{children:"Now you can start your admin panel:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"npm start\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Open ",(0,r.jsx)(n.code,{children:"http://localhost:3500/admin"})," in your browser and login with ",(0,r.jsx)(n.code,{children:"adminforth@adminforth.dev"})," and ",(0,r.jsx)(n.code,{children:"adminforth"})," credentials.\nSet up your avatar (you can generate it with AI) and public name in user settings."]}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.img,{alt:"alt text",src:t(9871).A+"",width:"3670",height:"2588"})}),"\n",(0,r.jsx)(n.h2,{id:"step-5-create-nuxt-project",children:"Step 5: Create Nuxt project"}),"\n",(0,r.jsx)(n.p,{children:"Now let's initialize our seo-facing frontend:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"npx nuxi@latest init seo\ncd seo\nnpm install -D sass-embedded\nnpm run dev\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Edit ",(0,r.jsx)(n.code,{children:"app.vue"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-html",metastring:'title="./seo/app.vue"',children:'\n\n\n\n'})}),"\n",(0,r.jsxs)(n.p,{children:["Add folder ",(0,r.jsx)(n.code,{children:"pages"})," and create ",(0,r.jsx)(n.code,{children:"index.vue"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-html",metastring:'title="./seo/pages/index.vue"',children:'\n\n\n\n +AI-Assisted blog with AdminForth and Nuxt in a minutes | Vue & Node admin panel framework @@ -15,14 +15,14 @@ - + -
Skip to main content

AI-Assisted blog with AdminForth and Nuxt in a minutes

· 18 min read
Ivan Borshcho
Maintainer of AdminForth

Many developers today are using copilots to write code faster and think less about syntax.

-

But what about writing plain text? For example blogs and micro-blogs? Sometimes you want to share your progress and thoughts but you are lazy for typing. Then you can give a try to AI-assisted blogging. Our Open-Source AdminForth framework has couple of new AI-capable plugins to write text and generate images.

+

AI-Assisted blog with AdminForth and Nuxt in a minutes

· 18 min read
Ivan Borshcho
Maintainer of AdminForth

Many developers today are using copilots to write code faster and relax their minds for some routine tasks.

+

But what about writing plain text? For example blogs and micro-blogs: sometimes you want to share your progress but you are lazy for typing. Then you can give a try to AI-assisted blogging. Our Open-Source AdminForth framework has couple of new AI-capable plugins to write text and generate images.

alt text

-

You can also touch it live here.

+

You can also touch a blog which we will create in live.

For AI plugins are backed by OpenAI API, but their architecture allows to be easily extended for other AI providers once OpenAI competitors will reach the same or better level of quality.

Here we will suggest you simple as 1-2-3 steps to build and host a blog with AI assistant which will help you to write posts.

Our tech stack will include:

@@ -107,6 +107,8 @@

Step 6: Deploy

We will dockerize app to make it easy to deploy with many ways. We will wrap both Node.js adminforth app and Nuxt.js app into single container for simplicity using supervisor. However you can split them into two containers and deploy them separately e.g. using docker compose.

Please note that in this demo example we routing requests to Nuxt.js app from AdminForth app using http-proxy. While this will work fine, it might give slower serving then if you would route traffik using dedicated reverse proxies like traefik or nginx.

+

Create bundleNow.ts file in root project directory:

+
./bundleNow.ts
import { admin } from './index.js';

await admin.bundleNow({ hotReload: false});
console.log('Bundling AdminForth done.');

Create Dockerfile in root project directory:

./Dockerfile
FROM node:20-alpine
EXPOSE 3500
WORKDIR /app
RUN apk add --no-cache supervisor
COPY package.json package-lock.json ./
RUN npm ci
COPY seo/package.json seo/package-lock.json seo/
RUN cd seo && npm ci
COPY . .

RUN npx tsx bundleNow.ts
RUN cd seo && npm run build

RUN cat > /etc/supervisord.conf <<EOF
[supervisord]
nodaemon=true

[program:app]
command=npm run startLive
directory=/app
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stderr_logfile=/dev/stderr

[program:seo]
command=sh -c "cd seo && node .output/server/index.mjs"
directory=/app
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stderr_logfile=/dev/stderr

[program:prisma]
command=npx --yes prisma migrate dev --name init
directory=/app
autostart=true
stdout_logfile=/dev/stdout
stderr_logfile=/dev/stderr

EOF


CMD ["supervisord", "-c", "/etc/supervisord.conf"]

Create .dockerignore file in root project directory:

diff --git a/blog/archive/index.html b/blog/archive/index.html index 1eefe7205..a3ef01bf7 100644 --- a/blog/archive/index.html +++ b/blog/archive/index.html @@ -15,7 +15,7 @@ - + diff --git a/blog/atom.xml b/blog/atom.xml index 94e058daa..1229f7f32 100644 --- a/blog/atom.xml +++ b/blog/atom.xml @@ -12,11 +12,11 @@ https://adminforth.dev/blog/ai-blog/ 2024-10-01T00:00:00.000Z - - Many developers today are using copilots to write code faster and think less about syntax.

-

But what about writing plain text? For example blogs and micro-blogs? Sometimes you want to share your progress and thoughts but you are lazy for typing. Then you can give a try to AI-assisted blogging. Our Open-Source AdminForth framework has couple of new AI-capable plugins to write text and generate images.

+ + Many developers today are using copilots to write code faster and relax their minds for some routine tasks.

+

But what about writing plain text? For example blogs and micro-blogs: sometimes you want to share your progress but you are lazy for typing. Then you can give a try to AI-assisted blogging. Our Open-Source AdminForth framework has couple of new AI-capable plugins to write text and generate images.

alt text

-

You can also touch it live here.

+

You can also touch a blog which we will create in live.

For AI plugins are backed by OpenAI API, but their architecture allows to be easily extended for other AI providers once OpenAI competitors will reach the same or better level of quality.

Here we will suggest you simple as 1-2-3 steps to build and host a blog with AI assistant which will help you to write posts.

Our tech stack will include:

@@ -101,6 +101,8 @@ Set up your avatar (you can generate it with AI) and public name in user setting

We will dockerize app to make it easy to deploy with many ways. We will wrap both Node.js adminforth app and Nuxt.js app into single container for simplicity using supervisor. However you can split them into two containers and deploy them separately e.g. using docker compose.

Please note that in this demo example we routing requests to Nuxt.js app from AdminForth app using http-proxy. While this will work fine, it might give slower serving then if you would route traffik using dedicated reverse proxies like traefik or nginx.

+

Create bundleNow.ts file in root project directory:

+
./bundleNow.ts
import { admin } from './index.js';

await admin.bundleNow({ hotReload: false});
console.log('Bundling AdminForth done.');

Create Dockerfile in root project directory:

./Dockerfile
FROM node:20-alpine
EXPOSE 3500
WORKDIR /app
RUN apk add --no-cache supervisor
COPY package.json package-lock.json ./
RUN npm ci
COPY seo/package.json seo/package-lock.json seo/
RUN cd seo && npm ci
COPY . .

RUN npx tsx bundleNow.ts
RUN cd seo && npm run build

RUN cat > /etc/supervisord.conf <<EOF
[supervisord]
nodaemon=true

[program:app]
command=npm run startLive
directory=/app
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stderr_logfile=/dev/stderr

[program:seo]
command=sh -c "cd seo && node .output/server/index.mjs"
directory=/app
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stderr_logfile=/dev/stderr

[program:prisma]
command=npx --yes prisma migrate dev --name init
directory=/app
autostart=true
stdout_logfile=/dev/stdout
stderr_logfile=/dev/stderr

EOF


CMD ["supervisord", "-c", "/etc/supervisord.conf"]

Create .dockerignore file in root project directory:

diff --git a/blog/chatgpt-plugin/index.html b/blog/chatgpt-plugin/index.html index 3a2e6e310..525fca3fc 100644 --- a/blog/chatgpt-plugin/index.html +++ b/blog/chatgpt-plugin/index.html @@ -15,7 +15,7 @@ - + diff --git a/blog/index.html b/blog/index.html index 072784c48..98b10e60d 100644 --- a/blog/index.html +++ b/blog/index.html @@ -3,7 +3,7 @@ -Blog | Vue & Node admin panel framework +Blog | Vue & Node admin panel framework @@ -15,14 +15,14 @@ - + -

AI-Assisted blog with AdminForth and Nuxt in a minutes

· 18 min read
Ivan Borshcho
Maintainer of AdminForth

Many developers today are using copilots to write code faster and think less about syntax.

-

But what about writing plain text? For example blogs and micro-blogs? Sometimes you want to share your progress and thoughts but you are lazy for typing. Then you can give a try to AI-assisted blogging. Our Open-Source AdminForth framework has couple of new AI-capable plugins to write text and generate images.

+

AI-Assisted blog with AdminForth and Nuxt in a minutes

· 18 min read
Ivan Borshcho
Maintainer of AdminForth

Many developers today are using copilots to write code faster and relax their minds for some routine tasks.

+

But what about writing plain text? For example blogs and micro-blogs: sometimes you want to share your progress but you are lazy for typing. Then you can give a try to AI-assisted blogging. Our Open-Source AdminForth framework has couple of new AI-capable plugins to write text and generate images.

alt text

-

You can also touch it live here.

+

You can also touch a blog which we will create in live.

For AI plugins are backed by OpenAI API, but their architecture allows to be easily extended for other AI providers once OpenAI competitors will reach the same or better level of quality.

Here we will suggest you simple as 1-2-3 steps to build and host a blog with AI assistant which will help you to write posts.

Our tech stack will include:

@@ -107,6 +107,8 @@

Step 6: Deploy

We will dockerize app to make it easy to deploy with many ways. We will wrap both Node.js adminforth app and Nuxt.js app into single container for simplicity using supervisor. However you can split them into two containers and deploy them separately e.g. using docker compose.

Please note that in this demo example we routing requests to Nuxt.js app from AdminForth app using http-proxy. While this will work fine, it might give slower serving then if you would route traffik using dedicated reverse proxies like traefik or nginx.

+

Create bundleNow.ts file in root project directory:

+
./bundleNow.ts
import { admin } from './index.js';

await admin.bundleNow({ hotReload: false});
console.log('Bundling AdminForth done.');

Create Dockerfile in root project directory:

./Dockerfile
FROM node:20-alpine
EXPOSE 3500
WORKDIR /app
RUN apk add --no-cache supervisor
COPY package.json package-lock.json ./
RUN npm ci
COPY seo/package.json seo/package-lock.json seo/
RUN cd seo && npm ci
COPY . .

RUN npx tsx bundleNow.ts
RUN cd seo && npm run build

RUN cat > /etc/supervisord.conf <<EOF
[supervisord]
nodaemon=true

[program:app]
command=npm run startLive
directory=/app
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stderr_logfile=/dev/stderr

[program:seo]
command=sh -c "cd seo && node .output/server/index.mjs"
directory=/app
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stderr_logfile=/dev/stderr

[program:prisma]
command=npx --yes prisma migrate dev --name init
directory=/app
autostart=true
stdout_logfile=/dev/stdout
stderr_logfile=/dev/stderr

EOF


CMD ["supervisord", "-c", "/etc/supervisord.conf"]

Create .dockerignore file in root project directory:

diff --git a/blog/rss.xml b/blog/rss.xml index ee4054811..142956af3 100644 --- a/blog/rss.xml +++ b/blog/rss.xml @@ -13,11 +13,11 @@ https://adminforth.dev/blog/ai-blog/ https://adminforth.dev/blog/ai-blog/ Tue, 01 Oct 2024 00:00:00 GMT - - Many developers today are using copilots to write code faster and think less about syntax.

-

But what about writing plain text? For example blogs and micro-blogs? Sometimes you want to share your progress and thoughts but you are lazy for typing. Then you can give a try to AI-assisted blogging. Our Open-Source AdminForth framework has couple of new AI-capable plugins to write text and generate images.

+ + Many developers today are using copilots to write code faster and relax their minds for some routine tasks.

+

But what about writing plain text? For example blogs and micro-blogs: sometimes you want to share your progress but you are lazy for typing. Then you can give a try to AI-assisted blogging. Our Open-Source AdminForth framework has couple of new AI-capable plugins to write text and generate images.

alt text

-

You can also touch it live here.

+

You can also touch a blog which we will create in live.

For AI plugins are backed by OpenAI API, but their architecture allows to be easily extended for other AI providers once OpenAI competitors will reach the same or better level of quality.

Here we will suggest you simple as 1-2-3 steps to build and host a blog with AI assistant which will help you to write posts.

Our tech stack will include:

@@ -102,6 +102,8 @@ Set up your avatar (you can generate it with AI) and public name in user setting

We will dockerize app to make it easy to deploy with many ways. We will wrap both Node.js adminforth app and Nuxt.js app into single container for simplicity using supervisor. However you can split them into two containers and deploy them separately e.g. using docker compose.

Please note that in this demo example we routing requests to Nuxt.js app from AdminForth app using http-proxy. While this will work fine, it might give slower serving then if you would route traffik using dedicated reverse proxies like traefik or nginx.

+

Create bundleNow.ts file in root project directory:

+
./bundleNow.ts
import { admin } from './index.js';

await admin.bundleNow({ hotReload: false});
console.log('Bundling AdminForth done.');

Create Dockerfile in root project directory:

./Dockerfile
FROM node:20-alpine
EXPOSE 3500
WORKDIR /app
RUN apk add --no-cache supervisor
COPY package.json package-lock.json ./
RUN npm ci
COPY seo/package.json seo/package-lock.json seo/
RUN cd seo && npm ci
COPY . .

RUN npx tsx bundleNow.ts
RUN cd seo && npm run build

RUN cat > /etc/supervisord.conf <<EOF
[supervisord]
nodaemon=true

[program:app]
command=npm run startLive
directory=/app
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stderr_logfile=/dev/stderr

[program:seo]
command=sh -c "cd seo && node .output/server/index.mjs"
directory=/app
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stderr_logfile=/dev/stderr

[program:prisma]
command=npx --yes prisma migrate dev --name init
directory=/app
autostart=true
stdout_logfile=/dev/stdout
stderr_logfile=/dev/stderr

EOF


CMD ["supervisord", "-c", "/etc/supervisord.conf"]

Create .dockerignore file in root project directory:

diff --git a/blog/tags/chatgpt/index.html b/blog/tags/chatgpt/index.html index 8f59fb78e..34f1cf2a9 100644 --- a/blog/tags/chatgpt/index.html +++ b/blog/tags/chatgpt/index.html @@ -15,14 +15,14 @@ - + -

2 posts tagged with "ChatGPT"

ChatGPT is a conversational AI model that can generate human-like responses to text inputs.

View All Tags

AI-Assisted blog with AdminForth and Nuxt in a minutes

· 18 min read
Ivan Borshcho
Maintainer of AdminForth

Many developers today are using copilots to write code faster and think less about syntax.

-

But what about writing plain text? For example blogs and micro-blogs? Sometimes you want to share your progress and thoughts but you are lazy for typing. Then you can give a try to AI-assisted blogging. Our Open-Source AdminForth framework has couple of new AI-capable plugins to write text and generate images.

+

2 posts tagged with "ChatGPT"

ChatGPT is a conversational AI model that can generate human-like responses to text inputs.

View All Tags

AI-Assisted blog with AdminForth and Nuxt in a minutes

· 18 min read
Ivan Borshcho
Maintainer of AdminForth

Many developers today are using copilots to write code faster and relax their minds for some routine tasks.

+

But what about writing plain text? For example blogs and micro-blogs: sometimes you want to share your progress but you are lazy for typing. Then you can give a try to AI-assisted blogging. Our Open-Source AdminForth framework has couple of new AI-capable plugins to write text and generate images.

alt text

-

You can also touch it live here.

+

You can also touch a blog which we will create in live.

For AI plugins are backed by OpenAI API, but their architecture allows to be easily extended for other AI providers once OpenAI competitors will reach the same or better level of quality.

Here we will suggest you simple as 1-2-3 steps to build and host a blog with AI assistant which will help you to write posts.

Our tech stack will include:

@@ -107,6 +107,8 @@

Step 6: Deploy

We will dockerize app to make it easy to deploy with many ways. We will wrap both Node.js adminforth app and Nuxt.js app into single container for simplicity using supervisor. However you can split them into two containers and deploy them separately e.g. using docker compose.

Please note that in this demo example we routing requests to Nuxt.js app from AdminForth app using http-proxy. While this will work fine, it might give slower serving then if you would route traffik using dedicated reverse proxies like traefik or nginx.

+

Create bundleNow.ts file in root project directory:

+
./bundleNow.ts
import { admin } from './index.js';

await admin.bundleNow({ hotReload: false});
console.log('Bundling AdminForth done.');

Create Dockerfile in root project directory:

./Dockerfile
FROM node:20-alpine
EXPOSE 3500
WORKDIR /app
RUN apk add --no-cache supervisor
COPY package.json package-lock.json ./
RUN npm ci
COPY seo/package.json seo/package-lock.json seo/
RUN cd seo && npm ci
COPY . .

RUN npx tsx bundleNow.ts
RUN cd seo && npm run build

RUN cat > /etc/supervisord.conf <<EOF
[supervisord]
nodaemon=true

[program:app]
command=npm run startLive
directory=/app
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stderr_logfile=/dev/stderr

[program:seo]
command=sh -c "cd seo && node .output/server/index.mjs"
directory=/app
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stderr_logfile=/dev/stderr

[program:prisma]
command=npx --yes prisma migrate dev --name init
directory=/app
autostart=true
stdout_logfile=/dev/stdout
stderr_logfile=/dev/stderr

EOF


CMD ["supervisord", "-c", "/etc/supervisord.conf"]

Create .dockerignore file in root project directory:

diff --git a/blog/tags/index.html b/blog/tags/index.html index b2d1df6cd..80c597d15 100644 --- a/blog/tags/index.html +++ b/blog/tags/index.html @@ -15,7 +15,7 @@ - + diff --git a/blog/tags/nuxt/index.html b/blog/tags/nuxt/index.html index 44f5bf3a2..1fa89bda5 100644 --- a/blog/tags/nuxt/index.html +++ b/blog/tags/nuxt/index.html @@ -15,14 +15,14 @@ - + -

One post tagged with "nuxt"

View All Tags

AI-Assisted blog with AdminForth and Nuxt in a minutes

· 18 min read
Ivan Borshcho
Maintainer of AdminForth

Many developers today are using copilots to write code faster and think less about syntax.

-

But what about writing plain text? For example blogs and micro-blogs? Sometimes you want to share your progress and thoughts but you are lazy for typing. Then you can give a try to AI-assisted blogging. Our Open-Source AdminForth framework has couple of new AI-capable plugins to write text and generate images.

+

One post tagged with "nuxt"

View All Tags

AI-Assisted blog with AdminForth and Nuxt in a minutes

· 18 min read
Ivan Borshcho
Maintainer of AdminForth

Many developers today are using copilots to write code faster and relax their minds for some routine tasks.

+

But what about writing plain text? For example blogs and micro-blogs: sometimes you want to share your progress but you are lazy for typing. Then you can give a try to AI-assisted blogging. Our Open-Source AdminForth framework has couple of new AI-capable plugins to write text and generate images.

alt text

-

You can also touch it live here.

+

You can also touch a blog which we will create in live.

For AI plugins are backed by OpenAI API, but their architecture allows to be easily extended for other AI providers once OpenAI competitors will reach the same or better level of quality.

Here we will suggest you simple as 1-2-3 steps to build and host a blog with AI assistant which will help you to write posts.

Our tech stack will include:

@@ -107,6 +107,8 @@

Step 6: Deploy

We will dockerize app to make it easy to deploy with many ways. We will wrap both Node.js adminforth app and Nuxt.js app into single container for simplicity using supervisor. However you can split them into two containers and deploy them separately e.g. using docker compose.

Please note that in this demo example we routing requests to Nuxt.js app from AdminForth app using http-proxy. While this will work fine, it might give slower serving then if you would route traffik using dedicated reverse proxies like traefik or nginx.

+

Create bundleNow.ts file in root project directory:

+
./bundleNow.ts
import { admin } from './index.js';

await admin.bundleNow({ hotReload: false});
console.log('Bundling AdminForth done.');

Create Dockerfile in root project directory:

./Dockerfile
FROM node:20-alpine
EXPOSE 3500
WORKDIR /app
RUN apk add --no-cache supervisor
COPY package.json package-lock.json ./
RUN npm ci
COPY seo/package.json seo/package-lock.json seo/
RUN cd seo && npm ci
COPY . .

RUN npx tsx bundleNow.ts
RUN cd seo && npm run build

RUN cat > /etc/supervisord.conf <<EOF
[supervisord]
nodaemon=true

[program:app]
command=npm run startLive
directory=/app
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stderr_logfile=/dev/stderr

[program:seo]
command=sh -c "cd seo && node .output/server/index.mjs"
directory=/app
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stderr_logfile=/dev/stderr

[program:prisma]
command=npx --yes prisma migrate dev --name init
directory=/app
autostart=true
stdout_logfile=/dev/stdout
stderr_logfile=/dev/stderr

EOF


CMD ["supervisord", "-c", "/etc/supervisord.conf"]

Create .dockerignore file in root project directory:

diff --git a/blog/tags/plugin/index.html b/blog/tags/plugin/index.html index d8470e9e8..1ae08349b 100644 --- a/blog/tags/plugin/index.html +++ b/blog/tags/plugin/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/index.html b/docs/api/index.html index 220506403..98582a192 100644 --- a/docs/api/index.html +++ b/docs/api/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/plugins/audit-log/types/index.html b/docs/api/plugins/audit-log/types/index.html index ed336dd5e..e6d692fb8 100644 --- a/docs/api/plugins/audit-log/types/index.html +++ b/docs/api/plugins/audit-log/types/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/plugins/audit-log/types/type-aliases/PluginOptions/index.html b/docs/api/plugins/audit-log/types/type-aliases/PluginOptions/index.html index fbb9db5e9..f07946af6 100644 --- a/docs/api/plugins/audit-log/types/type-aliases/PluginOptions/index.html +++ b/docs/api/plugins/audit-log/types/type-aliases/PluginOptions/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/plugins/chat-gpt/types/index.html b/docs/api/plugins/chat-gpt/types/index.html index 0caf323e3..720fbe6fb 100644 --- a/docs/api/plugins/chat-gpt/types/index.html +++ b/docs/api/plugins/chat-gpt/types/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/plugins/chat-gpt/types/interfaces/PluginOptions/index.html b/docs/api/plugins/chat-gpt/types/interfaces/PluginOptions/index.html index 8ca9ab1f0..e93cbbd68 100644 --- a/docs/api/plugins/chat-gpt/types/interfaces/PluginOptions/index.html +++ b/docs/api/plugins/chat-gpt/types/interfaces/PluginOptions/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/plugins/email-password-reset/types/index.html b/docs/api/plugins/email-password-reset/types/index.html index 9643bdb34..c26ef1526 100644 --- a/docs/api/plugins/email-password-reset/types/index.html +++ b/docs/api/plugins/email-password-reset/types/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/plugins/email-password-reset/types/interfaces/PluginOptions/index.html b/docs/api/plugins/email-password-reset/types/interfaces/PluginOptions/index.html index 63ad3e61e..e8f4af420 100644 --- a/docs/api/plugins/email-password-reset/types/interfaces/PluginOptions/index.html +++ b/docs/api/plugins/email-password-reset/types/interfaces/PluginOptions/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/plugins/foreign-inline-list/types/index.html b/docs/api/plugins/foreign-inline-list/types/index.html index c5accf6d1..e1d0092c6 100644 --- a/docs/api/plugins/foreign-inline-list/types/index.html +++ b/docs/api/plugins/foreign-inline-list/types/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/plugins/foreign-inline-list/types/type-aliases/PluginOptions/index.html b/docs/api/plugins/foreign-inline-list/types/type-aliases/PluginOptions/index.html index 4323eea0a..674d7b775 100644 --- a/docs/api/plugins/foreign-inline-list/types/type-aliases/PluginOptions/index.html +++ b/docs/api/plugins/foreign-inline-list/types/type-aliases/PluginOptions/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/plugins/import-export/types/index.html b/docs/api/plugins/import-export/types/index.html index 169585565..98b2624ca 100644 --- a/docs/api/plugins/import-export/types/index.html +++ b/docs/api/plugins/import-export/types/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/plugins/import-export/types/interfaces/PluginOptions/index.html b/docs/api/plugins/import-export/types/interfaces/PluginOptions/index.html index 44155bbac..37653b64b 100644 --- a/docs/api/plugins/import-export/types/interfaces/PluginOptions/index.html +++ b/docs/api/plugins/import-export/types/interfaces/PluginOptions/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/plugins/rich-editor/types/index.html b/docs/api/plugins/rich-editor/types/index.html index 503747ca9..02cd2cca3 100644 --- a/docs/api/plugins/rich-editor/types/index.html +++ b/docs/api/plugins/rich-editor/types/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/plugins/rich-editor/types/interfaces/PluginOptions/index.html b/docs/api/plugins/rich-editor/types/interfaces/PluginOptions/index.html index 496e473b2..ef30f0383 100644 --- a/docs/api/plugins/rich-editor/types/interfaces/PluginOptions/index.html +++ b/docs/api/plugins/rich-editor/types/interfaces/PluginOptions/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/plugins/two-factors-auth/types/index.html b/docs/api/plugins/two-factors-auth/types/index.html index ab6f05e48..d69023c77 100644 --- a/docs/api/plugins/two-factors-auth/types/index.html +++ b/docs/api/plugins/two-factors-auth/types/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/plugins/two-factors-auth/types/type-aliases/PluginOptions/index.html b/docs/api/plugins/two-factors-auth/types/type-aliases/PluginOptions/index.html index fe33a0695..09859d933 100644 --- a/docs/api/plugins/two-factors-auth/types/type-aliases/PluginOptions/index.html +++ b/docs/api/plugins/two-factors-auth/types/type-aliases/PluginOptions/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/plugins/upload/types/index.html b/docs/api/plugins/upload/types/index.html index 7d17e8fa3..e3c52dc74 100644 --- a/docs/api/plugins/upload/types/index.html +++ b/docs/api/plugins/upload/types/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/plugins/upload/types/type-aliases/PluginOptions/index.html b/docs/api/plugins/upload/types/type-aliases/PluginOptions/index.html index ebcd4d307..f1cf89785 100644 --- a/docs/api/plugins/upload/types/type-aliases/PluginOptions/index.html +++ b/docs/api/plugins/upload/types/type-aliases/PluginOptions/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/classes/Filters/index.html b/docs/api/types/AdminForthConfig/classes/Filters/index.html index 1514ba302..259eb39fb 100644 --- a/docs/api/types/AdminForthConfig/classes/Filters/index.html +++ b/docs/api/types/AdminForthConfig/classes/Filters/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/classes/Sorts/index.html b/docs/api/types/AdminForthConfig/classes/Sorts/index.html index c51464596..571bc0f7c 100644 --- a/docs/api/types/AdminForthConfig/classes/Sorts/index.html +++ b/docs/api/types/AdminForthConfig/classes/Sorts/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/enumerations/ActionCheckSource/index.html b/docs/api/types/AdminForthConfig/enumerations/ActionCheckSource/index.html index b09057583..733131545 100644 --- a/docs/api/types/AdminForthConfig/enumerations/ActionCheckSource/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/ActionCheckSource/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/enumerations/AdminForthDataTypes/index.html b/docs/api/types/AdminForthConfig/enumerations/AdminForthDataTypes/index.html index d1f7755e9..2320be6aa 100644 --- a/docs/api/types/AdminForthConfig/enumerations/AdminForthDataTypes/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/AdminForthDataTypes/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/enumerations/AdminForthFilterOperators/index.html b/docs/api/types/AdminForthConfig/enumerations/AdminForthFilterOperators/index.html index b0a403bb5..229375827 100644 --- a/docs/api/types/AdminForthConfig/enumerations/AdminForthFilterOperators/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/AdminForthFilterOperators/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/enumerations/AdminForthMenuTypes/index.html b/docs/api/types/AdminForthConfig/enumerations/AdminForthMenuTypes/index.html index 048c46822..14c784924 100644 --- a/docs/api/types/AdminForthConfig/enumerations/AdminForthMenuTypes/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/AdminForthMenuTypes/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/enumerations/AdminForthResourcePages/index.html b/docs/api/types/AdminForthConfig/enumerations/AdminForthResourcePages/index.html index bef12e72b..4143af3ac 100644 --- a/docs/api/types/AdminForthConfig/enumerations/AdminForthResourcePages/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/AdminForthResourcePages/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/enumerations/AdminForthSortDirections/index.html b/docs/api/types/AdminForthConfig/enumerations/AdminForthSortDirections/index.html index 6b9b6feef..779f4a461 100644 --- a/docs/api/types/AdminForthConfig/enumerations/AdminForthSortDirections/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/AdminForthSortDirections/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/enumerations/AllowedActionsEnum/index.html b/docs/api/types/AdminForthConfig/enumerations/AllowedActionsEnum/index.html index 06e8aea1f..339a3b359 100644 --- a/docs/api/types/AdminForthConfig/enumerations/AllowedActionsEnum/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/AllowedActionsEnum/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/index.html b/docs/api/types/AdminForthConfig/index.html index 5f554e247..9943dc535 100644 --- a/docs/api/types/AdminForthConfig/index.html +++ b/docs/api/types/AdminForthConfig/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/IAdminForth/index.html b/docs/api/types/AdminForthConfig/interfaces/IAdminForth/index.html index 52986a542..a50bbe214 100644 --- a/docs/api/types/AdminForthConfig/interfaces/IAdminForth/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/IAdminForth/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/IAdminForthAuth/index.html b/docs/api/types/AdminForthConfig/interfaces/IAdminForthAuth/index.html index 2ee85166c..f65bdecae 100644 --- a/docs/api/types/AdminForthConfig/interfaces/IAdminForthAuth/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/IAdminForthAuth/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/IAdminForthDataSourceConnector/index.html b/docs/api/types/AdminForthConfig/interfaces/IAdminForthDataSourceConnector/index.html index c3f4be811..56f001383 100644 --- a/docs/api/types/AdminForthConfig/interfaces/IAdminForthDataSourceConnector/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/IAdminForthDataSourceConnector/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/IAdminForthDataSourceConnectorBase/index.html b/docs/api/types/AdminForthConfig/interfaces/IAdminForthDataSourceConnectorBase/index.html index ada998028..2fb2a6141 100644 --- a/docs/api/types/AdminForthConfig/interfaces/IAdminForthDataSourceConnectorBase/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/IAdminForthDataSourceConnectorBase/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/IAdminForthDataSourceConnectorConstructor/index.html b/docs/api/types/AdminForthConfig/interfaces/IAdminForthDataSourceConnectorConstructor/index.html index 3fe994856..1be084082 100644 --- a/docs/api/types/AdminForthConfig/interfaces/IAdminForthDataSourceConnectorConstructor/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/IAdminForthDataSourceConnectorConstructor/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/IAdminForthFilter/index.html b/docs/api/types/AdminForthConfig/interfaces/IAdminForthFilter/index.html index ff1b2107f..18c55d35d 100644 --- a/docs/api/types/AdminForthConfig/interfaces/IAdminForthFilter/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/IAdminForthFilter/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/IAdminForthHttpResponse/index.html b/docs/api/types/AdminForthConfig/interfaces/IAdminForthHttpResponse/index.html index ce82a5332..48d36f2ea 100644 --- a/docs/api/types/AdminForthConfig/interfaces/IAdminForthHttpResponse/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/IAdminForthHttpResponse/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/IAdminForthPlugin/index.html b/docs/api/types/AdminForthConfig/interfaces/IAdminForthPlugin/index.html index e5fb20bce..293624f61 100644 --- a/docs/api/types/AdminForthConfig/interfaces/IAdminForthPlugin/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/IAdminForthPlugin/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/IAdminForthSort/index.html b/docs/api/types/AdminForthConfig/interfaces/IAdminForthSort/index.html index a5950a289..34aa5f4c3 100644 --- a/docs/api/types/AdminForthConfig/interfaces/IAdminForthSort/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/IAdminForthSort/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/ICodeInjector/index.html b/docs/api/types/AdminForthConfig/interfaces/ICodeInjector/index.html index f9cb37455..16caab7ad 100644 --- a/docs/api/types/AdminForthConfig/interfaces/ICodeInjector/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/ICodeInjector/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/IConfigValidator/index.html b/docs/api/types/AdminForthConfig/interfaces/IConfigValidator/index.html index c4be33e98..a85a91444 100644 --- a/docs/api/types/AdminForthConfig/interfaces/IConfigValidator/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/IConfigValidator/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/IExpressHttpServer/index.html b/docs/api/types/AdminForthConfig/interfaces/IExpressHttpServer/index.html index 706191302..8d6be811f 100644 --- a/docs/api/types/AdminForthConfig/interfaces/IExpressHttpServer/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/IExpressHttpServer/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/IHttpServer/index.html b/docs/api/types/AdminForthConfig/interfaces/IHttpServer/index.html index 3130d3508..40d5af28b 100644 --- a/docs/api/types/AdminForthConfig/interfaces/IHttpServer/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/IHttpServer/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/IOperationalResource/index.html b/docs/api/types/AdminForthConfig/interfaces/IOperationalResource/index.html index d920f4a5a..8018f288a 100644 --- a/docs/api/types/AdminForthConfig/interfaces/IOperationalResource/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/IOperationalResource/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthBulkAction/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthBulkAction/index.html index d3b5adda0..237ba76fb 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthBulkAction/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthBulkAction/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthColumnEnumItem/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthColumnEnumItem/index.html index 2646655a9..34190f406 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthColumnEnumItem/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthColumnEnumItem/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclaration/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclaration/index.html index 2961a40b6..b6faed1e3 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclaration/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclaration/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclarationFull/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclarationFull/index.html index 2cba46c47..aa5832e8d 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclarationFull/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclarationFull/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfig/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfig/index.html index a718f9699..04ba16d68 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfig/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfig/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfigMenuItem/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfigMenuItem/index.html index 348a6243d..f8adc5e89 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfigMenuItem/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfigMenuItem/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthDataSource/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthDataSource/index.html index 21e787fbb..de12bcb7d 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthDataSource/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthDataSource/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthFieldComponents/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthFieldComponents/index.html index df3b704b0..8326e913d 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthFieldComponents/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthFieldComponents/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthForeignResource/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthForeignResource/index.html index 64e570656..775413421 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthForeignResource/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthForeignResource/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthResource/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthResource/index.html index 570b8d59a..75bdfb5de 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthResource/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthResource/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthResourceColumn/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthResourceColumn/index.html index 62248fc42..a8c179a94 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthResourceColumn/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthResourceColumn/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminUser/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminUser/index.html index 7c0c669db..f7cc6f919 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminUser/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminUser/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AfterDataSourceResponseFunction/index.html b/docs/api/types/AdminForthConfig/type-aliases/AfterDataSourceResponseFunction/index.html index fa587f42d..5e2a801b1 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AfterDataSourceResponseFunction/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AfterDataSourceResponseFunction/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AfterSaveFunction/index.html b/docs/api/types/AdminForthConfig/type-aliases/AfterSaveFunction/index.html index 1d99d3ab4..d6a412f58 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AfterSaveFunction/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AfterSaveFunction/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AllowedActionValue/index.html b/docs/api/types/AdminForthConfig/type-aliases/AllowedActionValue/index.html index 8fff737c7..9eebd2db8 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AllowedActionValue/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AllowedActionValue/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AllowedActions/index.html b/docs/api/types/AdminForthConfig/type-aliases/AllowedActions/index.html index 8d5cd5de0..623937d52 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AllowedActions/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AllowedActions/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AllowedActionsResolved/index.html b/docs/api/types/AdminForthConfig/type-aliases/AllowedActionsResolved/index.html index 4c94c6f2b..aa03aa94f 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AllowedActionsResolved/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AllowedActionsResolved/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/BeforeDataSourceRequestFunction/index.html b/docs/api/types/AdminForthConfig/type-aliases/BeforeDataSourceRequestFunction/index.html index 3f382bb8d..871eaea4f 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/BeforeDataSourceRequestFunction/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/BeforeDataSourceRequestFunction/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/BeforeLoginConfirmationFunction/index.html b/docs/api/types/AdminForthConfig/type-aliases/BeforeLoginConfirmationFunction/index.html index ae8d707c4..573deaa9a 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/BeforeLoginConfirmationFunction/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/BeforeLoginConfirmationFunction/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/BeforeSaveFunction/index.html b/docs/api/types/AdminForthConfig/type-aliases/BeforeSaveFunction/index.html index 051020c43..691f16cdc 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/BeforeSaveFunction/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/BeforeSaveFunction/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/FDataFilter/index.html b/docs/api/types/AdminForthConfig/type-aliases/FDataFilter/index.html index 435b0797b..29ee8fd8e 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/FDataFilter/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/FDataFilter/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/FDataSort/index.html b/docs/api/types/AdminForthConfig/type-aliases/FDataSort/index.html index 3d897c135..445886b2a 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/FDataSort/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/FDataSort/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/ValidationObject/index.html b/docs/api/types/AdminForthConfig/type-aliases/ValidationObject/index.html index 8e51cc4df..7d4c95bad 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/ValidationObject/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/ValidationObject/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/FrontendAPI/enumerations/AlertVariant/index.html b/docs/api/types/FrontendAPI/enumerations/AlertVariant/index.html index ccd99e6cf..0cfdbf1d5 100644 --- a/docs/api/types/FrontendAPI/enumerations/AlertVariant/index.html +++ b/docs/api/types/FrontendAPI/enumerations/AlertVariant/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/FrontendAPI/index.html b/docs/api/types/FrontendAPI/index.html index 8b114c1b8..debe31f33 100644 --- a/docs/api/types/FrontendAPI/index.html +++ b/docs/api/types/FrontendAPI/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/FrontendAPI/interfaces/FrontendAPIInterface/index.html b/docs/api/types/FrontendAPI/interfaces/FrontendAPIInterface/index.html index 81ac8c110..a8a050159 100644 --- a/docs/api/types/FrontendAPI/interfaces/FrontendAPIInterface/index.html +++ b/docs/api/types/FrontendAPI/interfaces/FrontendAPIInterface/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/FrontendAPI/type-aliases/AlertParams/index.html b/docs/api/types/FrontendAPI/type-aliases/AlertParams/index.html index 196d2ee47..e2f7914eb 100644 --- a/docs/api/types/FrontendAPI/type-aliases/AlertParams/index.html +++ b/docs/api/types/FrontendAPI/type-aliases/AlertParams/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/FrontendAPI/type-aliases/ConfirmParams/index.html b/docs/api/types/FrontendAPI/type-aliases/ConfirmParams/index.html index 6410e7434..95fa8cd03 100644 --- a/docs/api/types/FrontendAPI/type-aliases/ConfirmParams/index.html +++ b/docs/api/types/FrontendAPI/type-aliases/ConfirmParams/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Advanced/plugin-development/index.html b/docs/tutorial/Advanced/plugin-development/index.html index 488f83c12..3afac3746 100644 --- a/docs/tutorial/Advanced/plugin-development/index.html +++ b/docs/tutorial/Advanced/plugin-development/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/alert/index.html b/docs/tutorial/Customization/alert/index.html index 913525dcc..9df866dd8 100644 --- a/docs/tutorial/Customization/alert/index.html +++ b/docs/tutorial/Customization/alert/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/branding/index.html b/docs/tutorial/Customization/branding/index.html index 061b8dcd0..f59873814 100644 --- a/docs/tutorial/Customization/branding/index.html +++ b/docs/tutorial/Customization/branding/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/bulkActions/index.html b/docs/tutorial/Customization/bulkActions/index.html index 5fbf11b6a..0649642cf 100644 --- a/docs/tutorial/Customization/bulkActions/index.html +++ b/docs/tutorial/Customization/bulkActions/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/customFieldRendering/index.html b/docs/tutorial/Customization/customFieldRendering/index.html index 64f95e354..d34adea95 100644 --- a/docs/tutorial/Customization/customFieldRendering/index.html +++ b/docs/tutorial/Customization/customFieldRendering/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/customPages/index.html b/docs/tutorial/Customization/customPages/index.html index 216e86e2e..883f78649 100644 --- a/docs/tutorial/Customization/customPages/index.html +++ b/docs/tutorial/Customization/customPages/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/dataApi/index.html b/docs/tutorial/Customization/dataApi/index.html index 0a3f67949..ccbccc3b5 100644 --- a/docs/tutorial/Customization/dataApi/index.html +++ b/docs/tutorial/Customization/dataApi/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/hooks/index.html b/docs/tutorial/Customization/hooks/index.html index 908c6c5e5..d834b3212 100644 --- a/docs/tutorial/Customization/hooks/index.html +++ b/docs/tutorial/Customization/hooks/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/limitingAccess/index.html b/docs/tutorial/Customization/limitingAccess/index.html index 12491f8b1..3c1832c5b 100644 --- a/docs/tutorial/Customization/limitingAccess/index.html +++ b/docs/tutorial/Customization/limitingAccess/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/menuConfiguration/index.html b/docs/tutorial/Customization/menuConfiguration/index.html index c12bd3058..b960a1671 100644 --- a/docs/tutorial/Customization/menuConfiguration/index.html +++ b/docs/tutorial/Customization/menuConfiguration/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/pageInjections/index.html b/docs/tutorial/Customization/pageInjections/index.html index c52b5f724..6db6abe10 100644 --- a/docs/tutorial/Customization/pageInjections/index.html +++ b/docs/tutorial/Customization/pageInjections/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/security/index.html b/docs/tutorial/Customization/security/index.html index 00b6122c1..7cf5c3086 100644 --- a/docs/tutorial/Customization/security/index.html +++ b/docs/tutorial/Customization/security/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/virtualColumns/index.html b/docs/tutorial/Customization/virtualColumns/index.html index d2ffd0de2..184ffb4f2 100644 --- a/docs/tutorial/Customization/virtualColumns/index.html +++ b/docs/tutorial/Customization/virtualColumns/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Plugins/AuditLog/index.html b/docs/tutorial/Plugins/AuditLog/index.html index d03b000fb..b7b1505f4 100644 --- a/docs/tutorial/Plugins/AuditLog/index.html +++ b/docs/tutorial/Plugins/AuditLog/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Plugins/ForeignInlineList/index.html b/docs/tutorial/Plugins/ForeignInlineList/index.html index a91254b36..c4ee2c01d 100644 --- a/docs/tutorial/Plugins/ForeignInlineList/index.html +++ b/docs/tutorial/Plugins/ForeignInlineList/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Plugins/RichEditor/index.html b/docs/tutorial/Plugins/RichEditor/index.html index f505676a4..3251b77c9 100644 --- a/docs/tutorial/Plugins/RichEditor/index.html +++ b/docs/tutorial/Plugins/RichEditor/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Plugins/TwoFactorsAuth/index.html b/docs/tutorial/Plugins/TwoFactorsAuth/index.html index 98295c104..93c8444ac 100644 --- a/docs/tutorial/Plugins/TwoFactorsAuth/index.html +++ b/docs/tutorial/Plugins/TwoFactorsAuth/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Plugins/chat-gpt/index.html b/docs/tutorial/Plugins/chat-gpt/index.html index 646c7a660..be20aedb8 100644 --- a/docs/tutorial/Plugins/chat-gpt/index.html +++ b/docs/tutorial/Plugins/chat-gpt/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Plugins/email-password-reset/index.html b/docs/tutorial/Plugins/email-password-reset/index.html index 069b0551d..b7dbeccd5 100644 --- a/docs/tutorial/Plugins/email-password-reset/index.html +++ b/docs/tutorial/Plugins/email-password-reset/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Plugins/import-export/index.html b/docs/tutorial/Plugins/import-export/index.html index 7bc367597..7345508a6 100644 --- a/docs/tutorial/Plugins/import-export/index.html +++ b/docs/tutorial/Plugins/import-export/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Plugins/upload/index.html b/docs/tutorial/Plugins/upload/index.html index f9c923565..8368dd54b 100644 --- a/docs/tutorial/Plugins/upload/index.html +++ b/docs/tutorial/Plugins/upload/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/deploy/index.html b/docs/tutorial/deploy/index.html index 21f30ab37..418cc3362 100644 --- a/docs/tutorial/deploy/index.html +++ b/docs/tutorial/deploy/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/gettingStarted/index.html b/docs/tutorial/gettingStarted/index.html index 924e566bb..8a118a283 100644 --- a/docs/tutorial/gettingStarted/index.html +++ b/docs/tutorial/gettingStarted/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/glossary/index.html b/docs/tutorial/glossary/index.html index 028fe0cda..3352ec5b7 100644 --- a/docs/tutorial/glossary/index.html +++ b/docs/tutorial/glossary/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/helloWorld/index.html b/docs/tutorial/helloWorld/index.html index d681e9aa3..f88a51fad 100644 --- a/docs/tutorial/helloWorld/index.html +++ b/docs/tutorial/helloWorld/index.html @@ -15,7 +15,7 @@ - + diff --git a/index.html b/index.html index 57d432445..7afd89ff2 100644 --- a/index.html +++ b/index.html @@ -15,7 +15,7 @@ - + diff --git a/search/index.html b/search/index.html index b8c80e87a..6599fe0c1 100644 --- a/search/index.html +++ b/search/index.html @@ -15,7 +15,7 @@ - +