From 652e0bc9c9d31f5caa472fc291d50b9cbce07674 Mon Sep 17 00:00:00 2001 From: Shubham Palriwala Date: Mon, 27 Nov 2023 15:58:33 +0530 Subject: [PATCH] feat: endpoint + telemetry for live self hosted instances (#1675) Signed-off-by: Neil Chauhan Co-authored-by: Neil Chauhan Co-authored-by: Matthias Nannt --- apps/web/Dockerfile | 44 +++++++----- apps/web/app/api/cron/ping/route.ts | 17 +++++ docker-compose.yml | 9 +-- docker/cronjobs | 3 + docker/docker-compose.yml | 100 ++++++++++++++-------------- 5 files changed, 101 insertions(+), 72 deletions(-) create mode 100644 apps/web/app/api/cron/ping/route.ts create mode 100644 docker/cronjobs diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile index a970ff3ecf..f821038c29 100644 --- a/apps/web/Dockerfile +++ b/apps/web/Dockerfile @@ -1,6 +1,13 @@ +# Installer stage: Building the application FROM node:18-alpine AS installer RUN corepack enable && corepack prepare pnpm@latest --activate +# Install Supercronic (cron for containers without super user privileges) +RUN apk add --no-cache curl \ + && curl -fsSLo /tmp/supercronic \ + "https://github.com/aptible/supercronic/releases/download/v0.2.27/supercronic-linux-amd64" \ + && chmod +x /tmp/supercronic + ARG DATABASE_URL ENV DATABASE_URL=$DATABASE_URL @@ -15,41 +22,42 @@ WORKDIR /app COPY . . RUN touch /app/apps/web/.env - RUN pnpm install # Build the project RUN pnpm post-install --filter=web... RUN pnpm turbo run build --filter=web... +# Runner stage: Setting up the runtime environment FROM node:18-alpine AS runner RUN corepack enable && corepack prepare pnpm@latest --activate -# Don't run production as root -RUN addgroup --system --gid 1001 nodejs -RUN adduser --system --uid 1001 nextjs -USER nextjs +RUN apk add --no-cache curl \ + # && addgroup --system --gid 1001 nodejs \ + && adduser --system --uid 1001 nextjs WORKDIR /home/nextjs +COPY --from=installer /tmp/supercronic /usr/local/bin/supercronic COPY --from=installer /app/apps/web/next.config.mjs . COPY --from=installer /app/apps/web/package.json . - -# Automatically leverage output traces to reduce image size -# https://nextjs.org/docs/advanced-features/output-file-tracing -COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/standalone ./ -COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static -COPY --from=installer --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public -COPY --from=installer --chown=nextjs:nodejs /app/packages/database/schema.prisma ./packages/database/schema.prisma -COPY --from=installer --chown=nextjs:nodejs /app/packages/database/migrations ./packages/database/migrations +# Leverage output traces to reduce image size +COPY --from=installer --chown=nextjs:nextjs /app/apps/web/.next/standalone ./ +COPY --from=installer --chown=nextjs:nextjs /app/apps/web/.next/static ./apps/web/.next/static +COPY --from=installer --chown=nextjs:nextjs /app/apps/web/public ./apps/web/public +COPY --from=installer --chown=nextjs:nextjs /app/packages/database/schema.prisma ./packages/database/schema.prisma +COPY --from=installer --chown=nextjs:nextjs /app/packages/database/migrations ./packages/database/migrations +COPY --from=installer /app/docker/cronjobs /app/docker/cronjobs EXPOSE 3000 - ENV HOSTNAME "0.0.0.0" +USER nextjs -CMD if [ "$NEXTAUTH_SECRET" != "RANDOM_STRING" ]; then \ - pnpm dlx prisma migrate deploy && node apps/web/server.js; \ +CMD supercronic -quiet /app/docker/cronjobs & \ + if [ "$NEXTAUTH_SECRET" != "RANDOM_STRING" ]; then \ + pnpm dlx prisma migrate deploy && \ + exec node apps/web/server.js; \ else \ - echo "ERROR: Please set a value for NEXTAUTH_SECRET in your docker compose variables!"; \ + echo "ERROR: Please set a value for NEXTAUTH_SECRET in your docker compose variables!" >&2; \ exit 1; \ - fi + fi \ No newline at end of file diff --git a/apps/web/app/api/cron/ping/route.ts b/apps/web/app/api/cron/ping/route.ts new file mode 100644 index 0000000000..a37bec61db --- /dev/null +++ b/apps/web/app/api/cron/ping/route.ts @@ -0,0 +1,17 @@ +import { responses } from "@/app/lib/api/response"; +import { CRON_SECRET } from "@formbricks/lib/constants"; +import { captureTelemetry } from "@formbricks/lib/telemetry"; +import { headers } from "next/headers"; + +export async function POST() { + const headersList = headers(); + const apiKey = headersList.get("x-api-key"); + + if (!apiKey || apiKey !== CRON_SECRET) { + return responses.notAuthenticatedResponse(); + } + + captureTelemetry("ping"); + + return responses.successResponse({}, true); +} diff --git a/docker-compose.yml b/docker-compose.yml index f03676968c..065ea4e119 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -70,11 +70,11 @@ x-sentry-ignore-api-resolution-error: &sentry_ignore_api_resolution_error # Enab x-next-public-sentry-dsn: &next_public_sentry_dsn # Cron Secret +# Set this to a random string to secure your cron endpoints +x-cron-secret: &cron_secret YOUR_CRON_SECRET -x-cron-secret: - &cron_secret # Configure ASSET_PREFIX_URL when you want to ship JS & CSS files from a complete URL instead of the current domain - - + +# Configure ASSET_PREFIX_URL when you want to ship JS & CSS files from a complete URL instead of the current domain x-asset-prefix-url: &asset_prefix_url services: @@ -94,6 +94,7 @@ services: args: DATABASE_URL: *database_url NEXTAUTH_SECRET: *nextauth_secret + ENCRYPTION_KEY: *encryption_key depends_on: - postgres diff --git a/docker/cronjobs b/docker/cronjobs new file mode 100644 index 0000000000..c9af6e941f --- /dev/null +++ b/docker/cronjobs @@ -0,0 +1,3 @@ +0 0 * * * $WEBAPP_URL/api/cron/close_surveys -X POST -H 'content-type: application/json' -H 'x-api-key: '"$CRON_SECRET"'' +0 8 * * 1 curl $WEBAPP_URL/api/cron/weekly_summary -X POST -H 'content-type: application/json' -H 'x-api-key: '"$CRON_SECRET"'' +0 9 * * * curl $WEBAPP_URL/api/cron/ping -X POST -H 'content-type: application/json' -H 'x-api-key: '"$CRON_SECRET"'' diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index d20e79efb3..4c42eb8fd4 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,72 +1,72 @@ version: "3.3" x-environment: &environment - environment: - # The url of your Formbricks instance used in the admin panel - WEBAPP_URL: +environment: + # The url of your Formbricks instance used in the admin panel + WEBAPP_URL: - # PostgreSQL DB for Formbricks to connect to - DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/formbricks?schema=public" + # PostgreSQL DB for Formbricks to connect to + DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/formbricks?schema=public" - # NextJS Auth - # @see: https://next-auth.js.org/configuration/options#nextauth_secret - # You can use: `openssl rand -hex 32` to generate one - NEXTAUTH_SECRET: + # NextJS Auth + # @see: https://next-auth.js.org/configuration/options#nextauth_secret + # You can use: `openssl rand -hex 32` to generate one + NEXTAUTH_SECRET: - # Set this to your public-facing URL, e.g., https://example.com - # You do not need the NEXTAUTH_URL environment variable in Vercel. - NEXTAUTH_URL: http://localhost:3000 + # Set this to your public-facing URL, e.g., https://example.com + # You do not need the NEXTAUTH_URL environment variable in Vercel. + NEXTAUTH_URL: http://localhost:3000 - # Encryption Key is used for 2FA & Single use URLs for Link Surveys - # You can use: $(openssl rand -hex 32) to generate one - ENCRYPTION_KEY: + # Encryption Key is used for 2FA & Single use URLs for Link Surveys + # You can use: $(openssl rand -hex 32) to generate one + ENCRYPTION_KEY: - # PostgreSQL password - POSTGRES_PASSWORD: postgres + # PostgreSQL password + POSTGRES_PASSWORD: postgres - # Email Configuration - # MAIL_FROM: - # SMTP_HOST: - # SMTP_PORT: - # SMTP_SECURE_ENABLED: - # SMTP_USER: - # SMTP_PASSWORD: + # Email Configuration + # MAIL_FROM: + # SMTP_HOST: + # SMTP_PORT: + # SMTP_SECURE_ENABLED: + # SMTP_USER: + # SMTP_PASSWORD: - # Set the below value if you have and want to use a custom URL for the links created by the Link Shortener - # SHORT_URL_BASE: + # Set the below value if you have and want to use a custom URL for the links created by the Link Shortener + # SHORT_URL_BASE: - # Set the below to 0 to enable Email Verification for new signups (will required Email Configuration) - EMAIL_VERIFICATION_DISABLED: 1 + # Set the below to 0 to enable Email Verification for new signups (will required Email Configuration) + EMAIL_VERIFICATION_DISABLED: 1 - # Set the below to 0 to enable Password Reset (will required Email Configuration) - PASSWORD_RESET_DISABLED: 1 + # Set the below to 0 to enable Password Reset (will required Email Configuration) + PASSWORD_RESET_DISABLED: 1 - # Uncomment the below and set it to 1 to disable Signups - # SIGNUP_DISABLED: + # Uncomment the below and set it to 1 to disable Signups + # SIGNUP_DISABLED: - # Uncomment the below and set it to 1 to disable Invites - # INVITE_DISABLED: + # Uncomment the below and set it to 1 to disable Invites + # INVITE_DISABLED: - # Uncomment the below and set a value to have your own Privacy Page URL on the signup & login page - # PRIVACY_URL: + # Uncomment the below and set a value to have your own Privacy Page URL on the signup & login page + # PRIVACY_URL: - # Uncomment the below and set a value to have your own Terms Page URL on the auth and the surveys page - # TERMS_URL: + # Uncomment the below and set a value to have your own Terms Page URL on the auth and the surveys page + # TERMS_URL: - # Uncomment the below and set a value to have your own Imprint Page URL on the auth and the surveys page - # IMPRINT_URL: + # Uncomment the below and set a value to have your own Imprint Page URL on the auth and the surveys page + # IMPRINT_URL: - # Uncomment the below and set to 1 if you want to enable GitHub OAuth - # GITHUB_AUTH_ENABLED: - # GITHUB_ID: - # GITHUB_SECRET: + # Uncomment the below and set to 1 if you want to enable GitHub OAuth + # GITHUB_AUTH_ENABLED: + # GITHUB_ID: + # GITHUB_SECRET: - # Uncomment the below and set to 1 if you want to enable Google OAuth - # GOOGLE_AUTH_ENABLED: - # GOOGLE_CLIENT_ID: - # GOOGLE_CLIENT_SECRET: + # Uncomment the below and set to 1 if you want to enable Google OAuth + # GOOGLE_AUTH_ENABLED: + # GOOGLE_CLIENT_ID: + # GOOGLE_CLIENT_SECRET: - # Configure ASSET_PREFIX_URL when you want to ship JS & CSS files from a complete URL instead of the current domain - # ASSET_PREFIX_URL: *asset_prefix_url + # Configure ASSET_PREFIX_URL when you want to ship JS & CSS files from a complete URL instead of the current domain + # ASSET_PREFIX_URL: *asset_prefix_url services: postgres: