From 7182ca6e4c543d026f0c8bc10d952757685bab75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Gerhard=20Lindesva=CC=88rd?= Date: Mon, 17 Feb 2025 21:18:35 +0100 Subject: [PATCH] improve(self-hosting): add resend and send email questions --- .../content/docs/self-hosting/changelog.mdx | 2 + self-hosting/.env.template | 3 +- self-hosting/quiz.ts | 102 +++++++++++++----- 3 files changed, 80 insertions(+), 27 deletions(-) diff --git a/apps/public/content/docs/self-hosting/changelog.mdx b/apps/public/content/docs/self-hosting/changelog.mdx index 6a0308f3..e8b7d074 100644 --- a/apps/public/content/docs/self-hosting/changelog.mdx +++ b/apps/public/content/docs/self-hosting/changelog.mdx @@ -17,6 +17,8 @@ If you upgrading from a previous version, you'll need to edit your `.env` file i - `ALLOW_REGISTRATION` - If set to `false` new users will not be able to register (only the first user can register). - `ALLOW_INVITATION` - If set to `false` new users will not be able to be invited. +- `RESEND_API_KEY` - If set, we'll use Resend to send e-mails. +- `EMAIL_SENDER` - The e-mail address that will be used to send e-mails. ## 0.0.6 diff --git a/self-hosting/.env.template b/self-hosting/.env.template index ab511d0e..9dc34770 100644 --- a/self-hosting/.env.template +++ b/self-hosting/.env.template @@ -13,4 +13,5 @@ DATABASE_URL_DIRECT="$DATABASE_URL_DIRECT" NEXT_PUBLIC_DASHBOARD_URL="$NEXT_PUBLIC_DASHBOARD_URL" NEXT_PUBLIC_API_URL="$NEXT_PUBLIC_API_URL" COOKIE_SECRET="$COOKIE_SECRET" -EMAIL_SENDER=no-repy@openpanel.dev \ No newline at end of file +EMAIL_SENDER="$EMAIL_SENDER" +RESEND_API_KEY="$RESEND_API_KEY" \ No newline at end of file diff --git a/self-hosting/quiz.ts b/self-hosting/quiz.ts index 7a7d4475..a1add077 100644 --- a/self-hosting/quiz.ts +++ b/self-hosting/quiz.ts @@ -5,6 +5,25 @@ import bcrypt from 'bcrypt'; import inquirer from 'inquirer'; import yaml from 'js-yaml'; +let envs = { + CLICKHOUSE_URL: '', + REDIS_URL: '', + DATABASE_URL: '', + DOMAIN_NAME: '', + COOKIE_SECRET: generatePassword(32), + RESEND_API_KEY: '', + EMAIL_SENDER: '', +}; + +type EnvVars = typeof envs; + +const addEnvs = (env: Partial) => { + envs = { + ...envs, + ...env, + }; +}; + function generatePassword(length: number) { const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; @@ -111,13 +130,7 @@ function removeServiceFromDockerCompose(serviceName: string) { fs.writeFileSync(dockerComposePath, newYaml); } -function writeEnvFile(envs: { - CLICKHOUSE_URL: string; - REDIS_URL: string; - DATABASE_URL: string; - DOMAIN_NAME: string; - COOKIE_SECRET: string; -}) { +function writeEnvFile(envs: EnvVars) { const envTemplatePath = path.resolve(__dirname, '.env.template'); const envPath = path.resolve(__dirname, '.env'); const envTemplate = fs.readFileSync(envTemplatePath, 'utf-8'); @@ -132,7 +145,9 @@ function writeEnvFile(envs: { .replace( '$NEXT_PUBLIC_API_URL', `${stripTrailingSlash(envs.DOMAIN_NAME)}/api`, - ); + ) + .replace('$RESEND_API_KEY', envs.RESEND_API_KEY) + .replace('$EMAIL_SENDER', envs.EMAIL_SENDER); fs.writeFileSync( envPath, @@ -175,10 +190,10 @@ async function initiateOnboarding() { // Domain name - const domainNameResponse = await inquirer.prompt([ + const domain = await inquirer.prompt([ { type: 'input', - name: 'domainName', + name: 'DOMAIN_NAME', message: "What's the domain name you want to use?", default: process.env.DEBUG ? 'http://localhost' : undefined, prefix: '🌐', @@ -192,6 +207,8 @@ async function initiateOnboarding() { }, ]); + addEnvs(domain); + // Dependencies const dependenciesResponse = await inquirer.prompt([ @@ -205,7 +222,6 @@ async function initiateOnboarding() { }, ]); - let envs: Record = {}; if (!dependenciesResponse.dependencies.includes('Clickhouse')) { const clickhouseResponse = await inquirer.prompt([ { @@ -217,10 +233,7 @@ async function initiateOnboarding() { }, ]); - envs = { - ...envs, - ...clickhouseResponse, - }; + addEnvs(clickhouseResponse); } if (!dependenciesResponse.dependencies.includes('Redis')) { @@ -232,10 +245,8 @@ async function initiateOnboarding() { default: process.env.DEBUG ? 'redis://op-kv:6379' : undefined, }, ]); - envs = { - ...envs, - ...redisResponse, - }; + + addEnvs(redisResponse); } if (!dependenciesResponse.dependencies.includes('Postgres')) { @@ -250,10 +261,8 @@ async function initiateOnboarding() { : undefined, }, ]); - envs = { - ...envs, - ...dbResponse, - }; + + addEnvs(dbResponse); } // Proxy @@ -293,6 +302,45 @@ async function initiateOnboarding() { }, ]); + const resend = await inquirer.prompt<{ + RESEND_API_KEY: string; + }>([ + { + type: 'input', + name: 'RESEND_API_KEY', + message: 'Enter your Resend API key (optional):', + }, + ]); + + if (resend.RESEND_API_KEY) { + const emailSender = await inquirer.prompt<{ + email: string; + }>([ + { + type: 'input', + name: 'EMAIL_SENDER', + default: `no-reply@${envs.DOMAIN_NAME.replace(/https?:\/\//, '')}`, + message: 'The email which will be used to send out emails:', + validate: (value) => { + if (!value) { + return 'Field is required'; + } + + if (!value.includes('@')) { + return 'Please enter a valid email'; + } + + return true; + }, + }, + ]); + + addEnvs({ + ...resend, + ...emailSender, + }); + } + const basicAuth = await inquirer.prompt<{ password: string; }>([ @@ -324,8 +372,10 @@ async function initiateOnboarding() { DATABASE_URL: envs.DATABASE_URL || 'postgresql://postgres:postgres@op-db:5432/postgres?schema=public', - DOMAIN_NAME: domainNameResponse.domainName, - COOKIE_SECRET: generatePassword(32), + DOMAIN_NAME: envs.DOMAIN_NAME, + COOKIE_SECRET: envs.COOKIE_SECRET, + RESEND_API_KEY: envs.RESEND_API_KEY || '', + EMAIL_SENDER: envs.EMAIL_SENDER || '', }); console.log('Updating docker-compose.yml file...\n'); @@ -350,7 +400,7 @@ async function initiateOnboarding() { if (proxyResponse.proxy === 'Bring my own') { removeServiceFromDockerCompose('op-proxy'); } else { - writeCaddyfile(domainNameResponse.domainName, basicAuth.password); + writeCaddyfile(envs.DOMAIN_NAME, basicAuth.password); } searchAndReplaceDockerCompose([['$OP_WORKER_REPLICAS', cpus.CPUS]]);