From 1e9c4bcb9aab058630d04482ba6b929d50e85df7 Mon Sep 17 00:00:00 2001 From: Baptiste Arnaud Date: Wed, 14 Jun 2023 08:59:38 +0200 Subject: [PATCH] :package: Add Cloudron package (#550) Closes #30 --- Dockerfile | 4 +- apps/builder/package.json | 6 +-- apps/docs/docs/self-hosting/cloudron.mdx | 64 +++++++++++++++++++++++ apps/landing-page/package.json | 4 +- apps/viewer/package.json | 6 +-- packages/cloudron/.env.docker | 12 +++++ packages/cloudron/CloudronManifest.json | 27 ++++++++++ packages/cloudron/Dockerfile | 33 ++++++++++++ packages/cloudron/env.default.sh | 13 +++++ packages/cloudron/start.sh | 52 ++++++++++++++++++ packages/cloudron/supervisor/builder.conf | 11 ++++ packages/cloudron/supervisor/viewer.conf | 11 ++++ packages/cloudron/supervisord.conf | 8 +++ scripts/builder-entrypoint.sh | 2 +- scripts/{env.sh => inject-runtime-env.sh} | 5 +- scripts/viewer-entrypoint.sh | 2 +- 16 files changed, 245 insertions(+), 15 deletions(-) create mode 100644 apps/docs/docs/self-hosting/cloudron.mdx create mode 100644 packages/cloudron/.env.docker create mode 100644 packages/cloudron/CloudronManifest.json create mode 100644 packages/cloudron/Dockerfile create mode 100644 packages/cloudron/env.default.sh create mode 100644 packages/cloudron/start.sh create mode 100644 packages/cloudron/supervisor/builder.conf create mode 100644 packages/cloudron/supervisor/viewer.conf create mode 100644 packages/cloudron/supervisord.conf rename scripts/{env.sh => inject-runtime-env.sh} (98%) diff --git a/Dockerfile b/Dockerfile index 78426d7fe..504267983 100644 --- a/Dockerfile +++ b/Dockerfile @@ -39,9 +39,9 @@ COPY --from=builder /app/apps/${SCOPE}/public ./apps/${SCOPE}/public COPY --from=builder --chown=node:node /app/apps/${SCOPE}/.next/standalone ./ COPY --from=builder --chown=node:node /app/apps/${SCOPE}/.next/static ./apps/${SCOPE}/.next/static -COPY scripts/env.sh scripts/${SCOPE}-entrypoint.sh ./ +COPY scripts/inject-runtime-env.sh scripts/${SCOPE}-entrypoint.sh ./ RUN chmod +x ./${SCOPE}-entrypoint.sh \ - && chmod +x ./env.sh + && chmod +x ./inject-runtime-env.sh ENTRYPOINT ./${SCOPE}-entrypoint.sh EXPOSE 3000 diff --git a/apps/builder/package.json b/apps/builder/package.json index 2ee2ed498..a4a26381f 100644 --- a/apps/builder/package.json +++ b/apps/builder/package.json @@ -3,10 +3,10 @@ "version": "0.1.0", "license": "AGPL-3.0-or-later", "scripts": { - "dev": "cross-env ENVSH_ENV=.env.local bash ../../scripts/env.sh next dev -p 3000", - "build": "cross-env ENVSH_ENV=.env.local bash ../../scripts/env.sh next build", + "dev": "cross-env ENVSH_ENV=.env.local bash ../../scripts/inject-runtime-env.sh next dev -p 3000", + "build": "cross-env ENVSH_ENV=.env.local bash ../../scripts/inject-runtime-env.sh next build", "build:docker": "next build", - "build:env": "cd ../.. && cross-env ENVSH_ENV=./apps/builder/.env.docker ENVSH_OUTPUT=./apps/builder/public/__env.js bash scripts/env.sh", + "build:env": "cd ../.. && cross-env ENVSH_ENV=./apps/builder/.env.docker ENVSH_OUTPUT=./apps/builder/public/__env.js bash scripts/inject-runtime-env.sh", "start": "next start", "lint": "next lint", "test": "pnpm playwright test", diff --git a/apps/docs/docs/self-hosting/cloudron.mdx b/apps/docs/docs/self-hosting/cloudron.mdx new file mode 100644 index 000000000..a3b3a47a7 --- /dev/null +++ b/apps/docs/docs/self-hosting/cloudron.mdx @@ -0,0 +1,64 @@ +--- +sidebar_position: 3 +--- + +import { SponsorButton } from '../../src/js/SponsorButton.jsx' + +# Cloudron + +:::note +The easiest way to get started with Typebot is with [the official managed service in the Cloud](https://app.typebot.io). You'll have high availability, backups, security, and maintenance all managed for you by me, Baptiste, Typebot's founder. + +The cloud version can save a substantial amount of developer time and resources. For most sites this ends up being the best value option and the revenue goes to funding the maintenance and further development of Typebot. So you’ll be supporting open source software and getting a great service! +::: + +## Requirements + +You need a server with [Cloudron](https://www.cloudron.io/) installed and a machine with the [Cloudron CLI](https://docs.cloudron.io/packaging/cli/) installed. + +## Installation + +### 1. Download the compose file + +On the machine that has the `cloudron` CLI, download the latest `CloudronManifest.json` file: + +```sh + wget https://raw.githubusercontent.com/baptisteArno/typebot.io/latest/packages/cloudron/CloudronManifest.json +``` + +### 2. Install the app + +Install the app: + +```sh +cloudron install --image baptistearno/typebot-cloudron:latest +``` + +## Configuration + +You can further configure the app by opening the app File Manager in Cloudron and edit the `env.sh` file. + +There, you can add any environment variable you want, like: + +```sh +export GITHUB_CLIENT_ID="your_github_client_id" +export GITHUB_CLIENT_SECRET="your_github_client_secret" +``` + +Then restart the app to apply the changes. + +## Update + +To update the app, run: + +```sh +cloudron update --app the_name_of_your_app --image baptistearno/typebot-cloudron:latest +``` + +:::note +If you're self-hosting Typebot, [sponsoring me](https://github.com/sponsors/baptisteArno) is a great way to give back to the community and to contribute to the long-term sustainability of the project. + + + +Thank you for supporting independent creators of Free Open Source Software! +::: diff --git a/apps/landing-page/package.json b/apps/landing-page/package.json index 76b0a1bd0..dc6b611ec 100644 --- a/apps/landing-page/package.json +++ b/apps/landing-page/package.json @@ -2,10 +2,10 @@ "name": "landing-page", "version": "1.0.0", "scripts": { - "dev": "cross-env ENVSH_ENV=.env.local bash ../../scripts/env.sh next dev -p 3002", + "dev": "cross-env ENVSH_ENV=.env.local bash ../../scripts/inject-runtime-env.sh next dev -p 3002", "start": "next start", "build": "next build", - "build:env": "cd ../.. && cross-env ENVSH_ENV=./apps/landing-page/.env.docker ENVSH_OUTPUT=./apps/landing-page/public/__env.js bash scripts/env.sh", + "build:env": "cd ../.. && cross-env ENVSH_ENV=./apps/landing-page/.env.docker ENVSH_OUTPUT=./apps/landing-page/public/__env.js bash scripts/inject-runtime-env.sh", "lint": "next lint", "analyze": "cross-env ANALYZE=true next build" }, diff --git a/apps/viewer/package.json b/apps/viewer/package.json index 09c05aee4..c99b9052d 100644 --- a/apps/viewer/package.json +++ b/apps/viewer/package.json @@ -3,10 +3,10 @@ "license": "AGPL-3.0-or-later", "version": "0.1.0", "scripts": { - "dev": "cross-env ENVSH_ENV=.env.local bash ../../scripts/env.sh next dev -p 3001", - "build": "cross-env ENVSH_ENV=.env.local bash ../../scripts/env.sh next build", + "dev": "cross-env ENVSH_ENV=.env.local bash ../../scripts/inject-runtime-env.sh next dev -p 3001", + "build": "cross-env ENVSH_ENV=.env.local bash ../../scripts/inject-runtime-env.sh next build", "build:docker": "next build", - "build:env": "cd ../.. && cross-env ENVSH_ENV=./apps/viewer/.env.docker ENVSH_OUTPUT=./apps/viewer/public/__env.js bash scripts/env.sh", + "build:env": "cd ../.. && cross-env ENVSH_ENV=./apps/viewer/.env.docker ENVSH_OUTPUT=./apps/viewer/public/__env.js bash scripts/inject-runtime-env.sh", "start": "next start -p 3001", "lint": "next lint", "test": "pnpm playwright test", diff --git a/packages/cloudron/.env.docker b/packages/cloudron/.env.docker new file mode 100644 index 000000000..b4ee669d8 --- /dev/null +++ b/packages/cloudron/.env.docker @@ -0,0 +1,12 @@ +# Don't edit this file +NEXT_PUBLIC_VIEWER_URL= +NEXT_PUBLIC_SMTP_FROM= +NEXT_PUBLIC_GOOGLE_API_KEY= +NEXT_PUBLIC_GIPHY_API_KEY= +NEXT_PUBLIC_STRIPE_PUBLIC_KEY= +NEXT_PUBLIC_SENTRY_DSN= +NEXT_PUBLIC_VIEWER_INTERNAL_URL= +NEXT_PUBLIC_E2E_TEST= +NEXT_PUBLIC_VERCEL_VIEWER_PROJECT_NAME= +NEXT_PUBLIC_UNSPLASH_APP_NAME= +NEXT_PUBLIC_UNSPLASH_ACCESS_KEY= diff --git a/packages/cloudron/CloudronManifest.json b/packages/cloudron/CloudronManifest.json new file mode 100644 index 000000000..828aba3d8 --- /dev/null +++ b/packages/cloudron/CloudronManifest.json @@ -0,0 +1,27 @@ +{ + "id": "io.typebot", + "title": "Typebot", + "author": "Baptiste Arnaud ", + "description": "Typebot is a visual chatbot builder that helps you create chatbots for your website without coding.", + "tagline": "Build advanced chatbots visually", + "version": "2.14.0", + "healthCheckPath": "/", + "httpPort": 3000, + "httpPorts": { + "TYPEBOT_VIEWER_HOST": { + "title": "Typebot Viewer", + "description": "The Typebot viewer where your users can interact with your bots", + "containerPort": 3001, + "defaultValue": "bot" + } + }, + "addons": { + "localstorage": {}, + "postgresql": {}, + "sendmail": {} + }, + "manifestVersion": 2, + "website": "https://typebot.io", + "contactEmail": "support@typebot.io", + "memoryLimit": 1073741824 +} diff --git a/packages/cloudron/Dockerfile b/packages/cloudron/Dockerfile new file mode 100644 index 000000000..f2d0c2c6b --- /dev/null +++ b/packages/cloudron/Dockerfile @@ -0,0 +1,33 @@ +FROM cloudron/base:4.1.0 AS base + +FROM baptistearno/typebot-builder:latest AS typebot-builder + +FROM baptistearno/typebot-viewer:latest AS typebot-viewer + +FROM base AS runner +RUN mkdir -p /app/code +WORKDIR /app/code +ENV NODE_ENV production +RUN apt-get -qy update \ + && apt-get -qy --no-install-recommends install \ + openssl \ + && apt-get autoremove -yq \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* +COPY --from=typebot-builder /app ./builder/ +COPY --from=typebot-viewer /app ./viewer/ +RUN builder/node_modules/.bin/prisma generate --schema=builder/packages/prisma/postgresql/schema.prisma +RUN viewer/node_modules/.bin/prisma generate --schema=viewer/packages/prisma/postgresql/schema.prisma +COPY ./packages/cloudron/start.sh ./packages/cloudron/env.default.sh ./ +COPY ./packages/cloudron/.env.docker ./scripts/inject-runtime-env.sh /run/ +RUN touch /run/builder_runtime_env.js && touch /run/viewer_runtime_env.js +RUN ln -s /run/builder_runtime_env.js builder/apps/builder/public/__env.js && ln -s /run/viewer_runtime_env.js viewer/apps/viewer/public/__env.js +RUN chmod +x ./start.sh && chmod +x ./env.default.sh && chmod +x /run/inject-runtime-env.sh +ADD ./packages/cloudron/supervisor/* /etc/supervisor/conf.d/ +ADD ./packages/cloudron/supervisord.conf /etc/supervisor/ + +CMD [ "/app/code/start.sh" ] + +EXPOSE 3000 +EXPOSE 3001 + diff --git a/packages/cloudron/env.default.sh b/packages/cloudron/env.default.sh new file mode 100644 index 000000000..bb04b8d74 --- /dev/null +++ b/packages/cloudron/env.default.sh @@ -0,0 +1,13 @@ +# Make sure to change this to your own random string of 32 characters (https://docs.typebot.io/self-hosting/docker#2-add-the-required-configuration) +export ENCRYPTION_SECRET="2GKg2i0oqWTkfc8lipjRE2weLg3R+UuI" +export NEXT_PUBLIC_VIEWER_URL="https://$TYPEBOT_VIEWER_HOST" +export DATABASE_URL="$CLOUDRON_POSTGRESQL_URL" +export NEXTAUTH_URL="$CLOUDRON_APP_ORIGIN" +export SMTP_USERNAME="$CLOUDRON_MAIL_SMTP_USERNAME" +export SMTP_PASSWORD="$CLOUDRON_MAIL_SMTP_PASSWORD" +export SMTP_HOST="$CLOUDRON_MAIL_SMTP_SERVER" +export SMTP_PORT="$CLOUDRON_MAIL_SMTP_PORT" +export NEXT_PUBLIC_SMTP_FROM="$CLOUDRON_MAIL_FROM" + +# For more configuration options, see https://docs.typebot.io/self-hosting/configuration + diff --git a/packages/cloudron/start.sh b/packages/cloudron/start.sh new file mode 100644 index 000000000..e426445ff --- /dev/null +++ b/packages/cloudron/start.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +set -eu +chown -R cloudron:cloudron /app/data + +echo "Waiting for postgres to become ready...." + +PG_READY="pg_isready -h $CLOUDRON_POSTGRESQL_HOST -p $CLOUDRON_POSTGRESQL_PORT" + +until $PG_READY +do + sleep 2; +done + +echo "Database ready to accept connections." + +if [[ ! -f "/app/data/env.sh" ]]; then + echo "Creating env.sh file at /app/data/env.sh" + cp ./env.default.sh /app/data/env.sh +else + echo "Skipping env.sh file creation. /app/data/env.sh exists." +fi + +echo "Sourcing env.sh file..." +source /app/data/env.sh + +echo 'Injecting environment variables into frontend...' +ENVSH_NO_RECREATE=true ENVSH_ENV=/run/.env.docker ENVSH_OUTPUT=/run/builder_runtime_env.js /run/inject-runtime-env.sh +ENVSH_NO_RECREATE=true ENVSH_ENV=/run/.env.docker ENVSH_OUTPUT=/run/viewer_runtime_env.js /run/inject-runtime-env.sh + +echo 'Checking if required environment variables are set and valid...' + +if [ -z "$DATABASE_URL" ]; then + echo "DATABASE_URL is not set. Exiting..." + exit 1 +fi + +if [ ${#ENCRYPTION_SECRET} -ne 32 ] && [ ${#ENCRYPTION_SECRET} -ne 80 ]; then + echo "ENCRYPTION_SECRET is not 32 characters long. Exiting... (To generate a valid secret: https://docs.typebot.io/self-hosting/docker#2-add-the-required-configuration)" + exit 1 +fi + +if [ -z "$NEXTAUTH_URL" ]; then + echo "NEXTAUTH_URL is not set. Exiting..." + exit 1 +fi + +./builder/node_modules/.bin/prisma migrate deploy --schema=builder/packages/prisma/postgresql/schema.prisma; + +echo "==> Starting supervisor" +exec /usr/bin/supervisord --configuration /etc/supervisor/supervisord.conf + diff --git a/packages/cloudron/supervisor/builder.conf b/packages/cloudron/supervisor/builder.conf new file mode 100644 index 000000000..4fe9495d3 --- /dev/null +++ b/packages/cloudron/supervisor/builder.conf @@ -0,0 +1,11 @@ +[program:builder] +priority=5 +directory=/app/code +command=node /app/code/builder/apps/builder/server.js +user=cloudron +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/packages/cloudron/supervisor/viewer.conf b/packages/cloudron/supervisor/viewer.conf new file mode 100644 index 000000000..cd8b8e6d3 --- /dev/null +++ b/packages/cloudron/supervisor/viewer.conf @@ -0,0 +1,11 @@ +[program:viewer] +priority=5 +directory=/app/code +environment=PORT=3001 +command=node /app/code/viewer/apps/viewer/server.js +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/packages/cloudron/supervisord.conf b/packages/cloudron/supervisord.conf new file mode 100644 index 000000000..585985a13 --- /dev/null +++ b/packages/cloudron/supervisord.conf @@ -0,0 +1,8 @@ +[supervisord] +nodaemon=true +user=root +logfile=/tmp/supervisord.log +pidfile=/tmp/supervisord.pid + +[include] +files = /etc/supervisor/conf.d/*.conf \ No newline at end of file diff --git a/scripts/builder-entrypoint.sh b/scripts/builder-entrypoint.sh index cd43b2884..2e6e9e8e4 100644 --- a/scripts/builder-entrypoint.sh +++ b/scripts/builder-entrypoint.sh @@ -1,6 +1,6 @@ #!/bin/bash -ENVSH_ENV=./apps/builder/.env.production ENVSH_OUTPUT=./apps/builder/public/__env.js bash env.sh +ENVSH_ENV=./apps/builder/.env.production ENVSH_OUTPUT=./apps/builder/public/__env.js bash inject-runtime-env.sh echo 'Checking if required environment variables are set and valid...' diff --git a/scripts/env.sh b/scripts/inject-runtime-env.sh similarity index 98% rename from scripts/env.sh rename to scripts/inject-runtime-env.sh index 8cb09dddf..3f0eeb613 100644 --- a/scripts/env.sh +++ b/scripts/inject-runtime-env.sh @@ -93,9 +93,8 @@ if [[ "$OSTYPE" == "darwin"* ]]; then ENVSH_SED="gsed" fi -# Recreate config file -rm -f "$ENVSH_OUTPUT" -touch "$ENVSH_OUTPUT" +# Empty config file +> "$ENVSH_OUTPUT" # Create an array from inline variables matched_envs=$(env | grep ${ENVSH_PREFIX}) diff --git a/scripts/viewer-entrypoint.sh b/scripts/viewer-entrypoint.sh index b9101518a..1b29946f6 100644 --- a/scripts/viewer-entrypoint.sh +++ b/scripts/viewer-entrypoint.sh @@ -1,6 +1,6 @@ #!/bin/bash -ENVSH_ENV=./apps/viewer/.env.production ENVSH_OUTPUT=./apps/viewer/public/__env.js bash env.sh +ENVSH_ENV=./apps/viewer/.env.production ENVSH_OUTPUT=./apps/viewer/public/__env.js bash inject-runtime-env.sh ./node_modules/.bin/prisma generate --schema=packages/prisma/postgresql/schema.prisma;