-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path1e563bf9.2ac2b423.js
1 lines (1 loc) · 12.5 KB
/
1e563bf9.2ac2b423.js
1
"use strict";(self.webpackChunkadminforth=self.webpackChunkadminforth||[]).push([[7184],{4926:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>s,default:()=>p,frontMatter:()=>d,metadata:()=>o,toc:()=>a});const o=JSON.parse('{"id":"tutorial/deploy","title":"Deploy in Docker","description":"In general you can already run your index.ts file which we created in Getting Started","source":"@site/docs/tutorial/04-deploy.md","sourceDirName":"tutorial","slug":"/tutorial/deploy","permalink":"/docs/tutorial/deploy","draft":false,"unlisted":false,"tags":[],"version":"current","sidebarPosition":4,"frontMatter":{},"sidebar":"tutorialSidebar","previous":{"title":"Websocket","permalink":"/docs/tutorial/Customization/websocket"},"next":{"title":"AuditLog","permalink":"/docs/tutorial/Plugins/AuditLog"}}');var r=t(4848),i=t(8453);const d={},s="Deploy in Docker",l={},a=[{value:"Building SPA in Docker build time",id:"building-spa-in-docker-build-time",level:2},{value:"Building the image",id:"building-the-image",level:2},{value:"Adding SSL (https) to AdminForth",id:"adding-ssl-https-to-adminforth",level:2},{value:"Subpath deployment",id:"subpath-deployment",level:2}];function c(e){const n={a:"a",blockquote:"blockquote",code:"code",h1:"h1",h2:"h2",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",...(0,i.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"deploy-in-docker",children:"Deploy in Docker"})}),"\n",(0,r.jsxs)(n.p,{children:["In general you can already run your ",(0,r.jsx)(n.code,{children:"index.ts"})," file which we created in ",(0,r.jsx)(n.a,{href:"/docs/tutorial/gettingStarted",children:"Getting Started"}),"\nwith ",(0,r.jsx)(n.code,{children:"ts-node"})," command in any node environment."]}),"\n",(0,r.jsx)(n.p,{children:"It will start the server on configured HTTP port and you can use any proxy like Traefik/Nginx to expose it to the internet and add SSL Layer."}),"\n",(0,r.jsx)(n.h2,{id:"building-spa-in-docker-build-time",children:"Building SPA in Docker build time"}),"\n",(0,r.jsxs)(n.p,{children:["In current index.ts file you might use call to ",(0,r.jsx)(n.code,{children:"bundleNow"})," method which starts building internal SPA bundle when ",(0,r.jsx)(n.code,{children:"index.ts"})," started\nexecuting. SPA building generally takes from 10 seconds to minute depending on the external modules you will add into AdminForth and extended functionality you will create."]}),"\n",(0,r.jsx)(n.p,{children:"To fully exclude this bundle time we recommend doing bundling in build time."}),"\n",(0,r.jsxs)(n.p,{children:["Create file ",(0,r.jsx)(n.code,{children:"bundleNow.ts"})," in the root directory of your project:"]}),"\n",(0,r.jsx)(n.p,{children:"and put the following code:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",metastring:"title='./bundleNow.ts'",children:"import { admin } from './index.js';\n\nawait admin.bundleNow({ hotReload: false});\nconsole.log('Bundling AdminForth done.');\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Now completely Remove bundleNow call from ",(0,r.jsx)(n.code,{children:"index.ts"})," file:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",metastring:"title='./index.ts'",children:"//diff-remove\n // needed to compile SPA. Call it here or from a build script e.g. in Docker build time to reduce downtime\n//diff-remove\n await admin.bundleNow({ hotReload: process.env.NODE_ENV === 'development'});\n//diff-remove\n console.log('Bundling AdminForth done. For faster serving consider calling bundleNow() from a build script.');\n//diff-add\n if (process.env.NODE_ENV === 'development') {\n//diff-add\n await admin.bundleNow({ hotReload: true });\n//diff-add\n console.log('Bundling AdminForth done');\n//diff-add\n }\n"})}),"\n",(0,r.jsxs)(n.p,{children:["In root directory create file ",(0,r.jsx)(n.code,{children:".dockerignore"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",metastring:"title='./.dockerignore'",children:"node_modules\n*.sqlite\n"})}),"\n",(0,r.jsxs)(n.p,{children:["In root directory create file ",(0,r.jsx)(n.code,{children:"Dockerfile"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-Dockerfile",children:'# use the same node version which you used during dev\nFROM node:20-alpine\nWORKDIR /code/\nADD package.json package-lock.json /code/\nRUN npm ci\nADD . /code/\nRUN --mount=type=cache,target=/tmp npx tsx bundleNow.ts\nCMD ["npm", "run", "migrateLiveAndStart"]\n'})}),"\n",(0,r.jsxs)(n.p,{children:["Add ",(0,r.jsx)(n.code,{children:"bundleNow"})," and ",(0,r.jsx)(n.code,{children:"startLive"})," to ",(0,r.jsx)(n.code,{children:"package.json"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",metastring:"title='./package.json'",children:'{\n "type": "module",\n "scripts": {\n "env": "dotenvx run -f .env.local -f .env --overload --",\n "start": "npm run env -- tsx watch index.ts",\n ...\n//diff-add\n "migrateLiveAndStart": "npx --yes prisma migrate deploy && tsx index.ts"\n },\n}\n'})}),"\n",(0,r.jsx)(n.h2,{id:"building-the-image",children:"Building the image"}),"\n",(0,r.jsx)(n.p,{children:"Now you can build your image:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"docker build -t myadminapp .\n"})}),"\n",(0,r.jsx)(n.p,{children:"And run container with:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"docker run -p 3500:3500 \\\n -e NODE_ENV=production \\\n -e ADMINFORTH_SECRET=CHANGEME \\\n -e DATABASE_FILE=/code/db/db.sqlite \\\n -e DATABASE_FILE_URL=file:/code/db/db.sqlite \\\n -v $(pwd)/db:/code/db \\\n myadminapp\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Now open your browser and go to ",(0,r.jsx)(n.code,{children:"http://localhost:3500"})," to see your AdminForth application running in Docker container."]}),"\n",(0,r.jsx)(n.h2,{id:"adding-ssl-https-to-adminforth",children:"Adding SSL (https) to AdminForth"}),"\n",(0,r.jsx)(n.p,{children:"There are lots of ways today to put your application behind SSL gateway. You might simply put AdminForth instance behind free Cloudflare CDN,\nchange 3500 port to 80 and Cloudflare will automatically add SSL layer and faster CDN for your application."}),"\n",(0,r.jsx)(n.p,{children:"However as a bonus here we will give you independent way to add free LetsEncrypt SSL layer to your AdminForth application."}),"\n",(0,r.jsxs)(n.p,{children:["First move all contents of your root folder (which contains index.ts and other files) to ",(0,r.jsx)(n.code,{children:"app"})," folder:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"mkdir app\nmv {.,}* app\n"})}),"\n",(0,r.jsxs)(n.p,{children:["In root directory create file ",(0,r.jsx)(n.code,{children:"compose.yml"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-yaml",metastring:"title='./compose.yml'",children:'version: \'3.8\'\n\nservices:\n traefik:\n image: "traefik:v2.5"\n command:\n - "--api.insecure=true"\n - "--providers.docker=true"\n - "--entrypoints.web.address=:80"\n - "--entrypoints.websecure.address=:443"\n - "--certificatesresolvers.myresolver.acme.httpchallenge=true"\n - "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"\n - "--certificatesresolvers.myresolver.acme.email=demo@devforth.io" # \u261d\ufe0f replace with your email\n - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"\n ports:\n - "80:80"\n - "443:443"\n volumes:\n - "/var/run/docker.sock:/var/run/docker.sock:ro"\n - "./letsencrypt:/letsencrypt"\n labels:\n - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"\n - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"\n - "traefik.http.routers.http-catchall.entrypoints=web"\n - "traefik.http.routers.http-catchall.middlewares=redirect-to-https"\n - "traefik.http.routers.http-catchall.tls=false"\n\n adminforth:\n build: ./app\n environment:\n - NODE_ENV=production\n - ADMINFORTH_SECRET=!CHANGEME! # \u261d\ufe0f replace with your secret\n - DATABASE_FILE=/code/db/db.sqlite\n - DATABASE_FILE_URL=file:/code/db/db.sqlite\n labels:\n - "traefik.enable=true"\n - "traefik.http.routers.adminforth.tls=true"\n - "traefik.http.routers.adminforth.tls.certresolver=myresolver"\n - "traefik.http.routers.adminforth.rule=PathPrefix(`/`)"\n - "traefik.http.services.adminforth.loadbalancer.server.port=3500"\n - "traefik.http.routers.adminforth.priority=1"\n # needed only if you are using SQLite\n volumes:\n - db:/code/db\n\n# needed only if you are using SQLite\nvolumes:\n db:\n\nnetworks:\n default:\n driver: bridge\n'})}),"\n",(0,r.jsx)(n.p,{children:"Now pull this compose file and all directories to your server and run:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"docker compose -p stack-my-app -f compose.yml up -d --build --remove-orphans --wait\n"})}),"\n",(0,r.jsxs)(n.blockquote,{children:["\n",(0,r.jsx)(n.p,{children:"\u261d\ufe0f You can also test this compose stack locally on your machine but SSL will not work,\nso locally you can ignore Chrome warning about SSL and test your AdminForth application."}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"subpath-deployment",children:"Subpath deployment"}),"\n",(0,r.jsxs)(n.p,{children:["If you want to deploy your AdminForth application to a sub-folder like ",(0,r.jsx)(n.code,{children:"https://mydomain.com/admin"})," you\nshould do the following:"]}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsxs)(n.li,{children:["Open ",(0,r.jsx)(n.code,{children:"index.ts"})," file and change ",(0,r.jsx)(n.code,{children:"ADMIN_BASE_URL"})," constant to your subpath:"]}),"\n"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",metastring:"title='./index.ts'",children:"//diff-remove\nconst ADMIN_BASE_URL = '';\n//diff-add\nconst ADMIN_BASE_URL = '/admin/';\n"})}),"\n",(0,r.jsxs)(n.ol,{start:"2",children:["\n",(0,r.jsxs)(n.li,{children:["Open ",(0,r.jsx)(n.code,{children:"compose.yml"})," file and change ",(0,r.jsx)(n.code,{children:"traefik.http.routers.adminforth.rule"})," to your subpath:"]}),"\n"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-yml",metastring:"title='./compose.yml'",children:' ...\n - "traefik.http.routers.adminforth.tls.certresolver=myresolver"\n//diff-remove\n - "traefik.http.routers.adminforth.rule=PathPrefix(`/`)"\n//diff-add\n - "traefik.http.routers.adminforth.rule=PathPrefix(`/admin/`)"\n'})}),"\n",(0,r.jsx)(n.p,{children:"Redeploy compose."}),"\n",(0,r.jsxs)(n.p,{children:["Now you can access your AdminForth application by going to ",(0,r.jsx)(n.code,{children:"https://mydomain.com/admin"}),"."]}),"\n",(0,r.jsxs)(n.p,{children:["If you want to automate the deployment process with CI follow ",(0,r.jsx)(n.a,{href:"https://devforth.io/blog/onlogs-open-source-simplified-web-logs-viewer-for-dockers/",children:"our docker - traefik guide"})]}),"\n",(0,r.jsx)(n.h1,{id:"nginx-version",children:"Nginx version"}),"\n",(0,r.jsx)(n.p,{children:"If you are using Nginx instead of traefik, here is siple proxy pass config:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{children:'server {\n listen 80;\n server_name demo.adminforth.dev;\n\n charset utf-8;\n client_max_body_size 75M;\n\n gzip on;\n gzip_disable "msie6";\n gzip_vary on;\n gzip_proxied any;\n gzip_comp_level 8;\n gzip_buffers 16 8k;\n gzip_http_version 1.1;\n gzip_min_length 2000;\n gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon;\n\n location / {\n proxy_read_timeout 220s;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header Host $http_host;\n proxy_redirect off;\n proxy_pass http://127.0.0.1:3500;\n }\n}\n'})})]})}function p(e={}){const{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(c,{...e})}):c(e)}},8453:(e,n,t)=>{t.d(n,{R:()=>d,x:()=>s});var o=t(6540);const r={},i=o.createContext(r);function d(e){const n=o.useContext(i);return o.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function s(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:d(e.components),o.createElement(i.Provider,{value:n},e.children)}}}]);