3 Commits
Author SHA1 Message Date
maro 7edeafe7c3 Improve email branding and SMTP envelope
CI / test (push) Canceled after 0s
Release / promote-and-deploy (push) Canceled after 0s
CI / build-image (push) Canceled after 0s
2026-09-18 12:51:40 +02:00
maro ad353190f3 Fix SMTP proxy certificates and speed up CI
CI / test (push) Canceled after 0s
Release / promote-and-deploy (push) Canceled after 0s
CI / build-image (push) Canceled after 0s
2026-09-18 11:59:33 +02:00
maro 82a89cd3d9 Fix media permissions inside app container
Release / promote-and-deploy (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / build-image (push) Canceled after 0s
2026-09-18 02:22:15 +02:00
13 changed files with 94 additions and 45 deletions
+5 -1
View File
@@ -57,7 +57,11 @@ SMTP_HOST=
SMTP_PORT=587 SMTP_PORT=587
SMTP_USER= SMTP_USER=
SMTP_PASSWORD= SMTP_PASSWORD=
SMTP_FROM="ANOUMA <support@anouma.org>" # Mailbox address used by the SMTP relay. The app displays ANOUMA as the name.
SMTP_FROM=support@anouma.org
# Keep true for publicly trusted SMTP certificates. Set false only when the
# configured SMTP proxy intentionally uses a private/self-signed certificate.
SMTP_TLS_REJECT_UNAUTHORIZED=true
# Address used as Reply-To for CMS replies. Configure this mailbox with an # Address used as Reply-To for CMS replies. Configure this mailbox with an
# inbound provider/webhook that POSTs replies to /api/email/inbound. # inbound provider/webhook that POSTs replies to /api/email/inbound.
+2 -5
View File
@@ -35,16 +35,13 @@ jobs:
run: npx next typegen && npx tsc --noEmit run: npx next typegen && npx tsc --noEmit
- name: Test - name: Test
run: npm test run: npm test
- name: Build
env:
NODE_ENV: production
run: npm run build
build-image: build-image:
needs: test needs: test
if: github.ref == 'refs/heads/main' && github.event_name == 'push' if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
actions: write
contents: read contents: read
packages: write packages: write
env: env:
@@ -59,6 +56,6 @@ jobs:
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Build image - name: Build image
run: docker build -t "$IMAGE:$GITHUB_SHA" . run: docker build --tag "$IMAGE:$GITHUB_SHA" .
- name: Push image - name: Push image
run: docker push "$IMAGE:$GITHUB_SHA" run: docker push "$IMAGE:$GITHUB_SHA"
+1 -1
View File
@@ -193,7 +193,7 @@ See `.env.example` for the full, commented list. `setup.sh` writes the real file
| `TURNSTILE_SITE_KEY` | Public Cloudflare Turnstile site key | | `TURNSTILE_SITE_KEY` | Public Cloudflare Turnstile site key |
| `TURNSTILE_SECRET_KEY` | Server-only Cloudflare Turnstile secret; keep it in `/opt/anouma/.env` and never in GitHub Actions or the repository | | `TURNSTILE_SECRET_KEY` | Server-only Cloudflare Turnstile secret; keep it in `/opt/anouma/.env` and never in GitHub Actions or the repository |
| `TURNSTILE_HOSTNAMES` | Hostnames accepted by Siteverify; production is `anouma.org` only (local development may use `localhost,127.0.0.1`) | | `TURNSTILE_HOSTNAMES` | Hostnames accepted by Siteverify; production is `anouma.org` only (local development may use `localhost,127.0.0.1`) |
| `SMTP_*` | The existing ANOUMA mail system | | `SMTP_*` | The existing ANOUMA mail system. `SMTP_TLS_REJECT_UNAUTHORIZED` stays `true` unless the configured SMTP proxy intentionally uses a private/self-signed certificate; only then set it to `false`. |
| `INBOUND_REPLY_TO`, `INBOUND_EMAIL_SECRET` | Mailbox and shared secret for the inbound email webhook (`/api/email/inbound`) | | `INBOUND_REPLY_TO`, `INBOUND_EMAIL_SECRET` | Mailbox and shared secret for the inbound email webhook (`/api/email/inbound`) |
| `LIVEKIT_URL`, `LIVEKIT_API_KEY`, `LIVEKIT_API_SECRET` | eigener LiveKit-SFU und serverseitige Token-Erzeugung | | `LIVEKIT_URL`, `LIVEKIT_API_KEY`, `LIVEKIT_API_SECRET` | eigener LiveKit-SFU und serverseitige Token-Erzeugung |
| `DOCKER_NETWORK`, `NPM_NETWORK` | Docker network names | | `DOCKER_NETWORK`, `NPM_NETWORK` | Docker network names |
+6 -2
View File
@@ -5,10 +5,10 @@
# .env.example) - this image builds without a database connection. # .env.example) - this image builds without a database connection.
FROM node:22-alpine AS base FROM node:22-alpine AS base
RUN apk add --no-cache libc6-compat su-exec
# ---- deps: install once, cached as its own layer ------------------------- # ---- deps: install once, cached as its own layer -------------------------
FROM base AS deps FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app WORKDIR /app
COPY package.json package-lock.json ./ COPY package.json package-lock.json ./
RUN npm ci RUN npm ci
@@ -41,7 +41,11 @@ COPY --from=builder --chown=nextjs:nodejs /app /app
# Docker volumes mounted over these paths (see docker-compose.yml). # Docker volumes mounted over these paths (see docker-compose.yml).
RUN mkdir -p media backups && chown -R nextjs:nodejs media backups RUN mkdir -p media backups && chown -R nextjs:nodejs media backups
USER nextjs COPY --from=builder --chown=root:root /app/docker/anouma-entrypoint.sh /usr/local/bin/anouma-entrypoint.sh
RUN chmod 755 /usr/local/bin/anouma-entrypoint.sh
USER root
ENTRYPOINT ["/usr/local/bin/anouma-entrypoint.sh"]
EXPOSE 3000 EXPOSE 3000
ENV PORT=3000 ENV PORT=3000
+12
View File
@@ -0,0 +1,12 @@
#!/bin/sh
set -eu
# /app/media is a persistent host bind mount in production. Its host owner
# can differ from the unprivileged application user inside the image, so fix
# the mount ownership before dropping privileges for the actual app process.
if [ -d /app/media ]; then
chown -R nextjs:nodejs /app/media
chmod 775 /app/media
fi
exec su-exec nextjs "$@"
+1 -1
View File
@@ -70,7 +70,7 @@ export function bookingConfirmedEmail(
extras.push(renderButton("Meeting betreten", args.online.joinUrl)); extras.push(renderButton("Meeting betreten", args.online.joinUrl));
extras.push( extras.push(
`<p style="margin:0 0 4px;color:#66505f;font-size:14px;"><strong>Meeting-Passwort:</strong></p>` + `<p style="margin:0 0 4px;color:#66505f;font-size:14px;"><strong>Meeting-Passwort:</strong></p>` +
`<p style="margin:0 0 20px;color:#66505f;font-size:20px;letter-spacing:0.08em;font-family:'Courier New',monospace;">${escapeHtml(args.online.password)}</p>`, `<p style="margin:0 0 20px;color:#66505f;font-size:20px;letter-spacing:0.08em;font-family:'Antropos',Georgia,'Times New Roman',serif;">${escapeHtml(args.online.password)}</p>`,
); );
} }
if (args.onsite) { if (args.onsite) {
+18 -11
View File
@@ -5,6 +5,8 @@ const colors = {
creamLight: "#fbf6f1", creamLight: "#fbf6f1",
}; };
const emailFont = "'Antropos', Georgia, 'Times New Roman', serif";
export function escapeHtml(value: string): string { export function escapeHtml(value: string): string {
return value return value
.replace(/&/g, "&amp;") .replace(/&/g, "&amp;")
@@ -17,7 +19,7 @@ export function renderButton(label: string, href: string): string {
return `<table role="presentation" cellpadding="0" cellspacing="0" style="margin:24px auto;"> return `<table role="presentation" cellpadding="0" cellspacing="0" style="margin:24px auto;">
<tr> <tr>
<td style="border-radius:999px;background:${colors.mauveDark};"> <td style="border-radius:999px;background:${colors.mauveDark};">
<a href="${href}" style="display:inline-block;padding:14px 32px;color:#ffffff;text-decoration:none;font-size:14px;font-weight:600;letter-spacing:0.02em;"> <a href="${href}" style="display:inline-block;padding:14px 32px;color:#ffffff;text-decoration:none;font-family:${emailFont};font-size:14px;font-weight:600;letter-spacing:0.02em;">
${escapeHtml(label)} ${escapeHtml(label)}
</a> </a>
</td> </td>
@@ -28,7 +30,7 @@ export function renderButton(label: string, href: string): string {
export function renderFactBox(rows: { label: string; value: string }[]): string { export function renderFactBox(rows: { label: string; value: string }[]): string {
const cells = rows const cells = rows
.map( .map(
(row, i) => `<p style="margin:0 ${i === rows.length - 1 ? "0" : "0 12px"} 0;color:${colors.plum};font-size:14px;"> (row, i) => `<p style="margin:0 ${i === rows.length - 1 ? "0" : "0 12px"} 0;color:${colors.plum};font-family:${emailFont};font-size:14px;">
<strong>${escapeHtml(row.label)}:</strong><br />${escapeHtml(row.value)} <strong>${escapeHtml(row.label)}:</strong><br />${escapeHtml(row.value)}
</p>`, </p>`,
) )
@@ -45,24 +47,29 @@ export function renderEmailShell(args: { preheader?: string; bodyHtml: string; f
return `<!doctype html> return `<!doctype html>
<html lang="de"> <html lang="de">
<body style="margin:0;padding:0;background:${colors.creamLight};font-family:Georgia,'Times New Roman',serif;"> <head>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:${colors.creamLight};padding:32px 16px;"> <style>
@font-face { font-family: 'Antropos'; src: url('https://anouma.org/fonts/baar-antropos.ttf') format('truetype'); font-style: normal; font-weight: 100 900; }
</style>
</head>
<body style="margin:0;padding:0;background:${colors.creamLight};font-family:${emailFont};">
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:${colors.creamLight};padding:32px 16px;font-family:${emailFont};">
<tr> <tr>
<td align="center"> <td align="center">
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="max-width:480px;background:#ffffff;border-radius:24px;overflow:hidden;"> <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="max-width:480px;background:#ffffff;border-radius:24px;overflow:hidden;font-family:${emailFont};">
<tr> <tr>
<td style="background:${colors.mauveDark};padding:28px 32px;text-align:center;"> <td style="background:${colors.mauveDark};padding:28px 32px;text-align:center;font-family:${emailFont};">
<span style="color:#ffffff;font-size:22px;letter-spacing:0.12em;font-weight:600;">ANOUMA</span> <span style="color:#ffffff;font-family:${emailFont};font-size:22px;letter-spacing:0.12em;font-weight:600;">ANOUMA</span>
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="padding:32px;"> <td style="padding:32px;font-family:${emailFont};">
${bodyHtml} ${bodyHtml}
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="padding:20px 32px;background:${colors.cream};text-align:center;"> <td style="padding:20px 32px;background:${colors.cream};text-align:center;font-family:${emailFont};">
<span style="color:${colors.plum};font-size:12px;">${escapeHtml(footerText)}</span> <span style="color:${colors.plum};font-family:${emailFont};font-size:12px;">${escapeHtml(footerText)}</span>
</td> </td>
</tr> </tr>
</table> </table>
@@ -76,5 +83,5 @@ export function renderEmailShell(args: { preheader?: string; bodyHtml: string; f
export function paragraph(text: string, opts: { italic?: boolean; small?: boolean } = {}): string { export function paragraph(text: string, opts: { italic?: boolean; small?: boolean } = {}): string {
const size = opts.small ? "13px" : "16px"; const size = opts.small ? "13px" : "16px";
const style = opts.italic ? "font-style:italic;" : ""; const style = opts.italic ? "font-style:italic;" : "";
return `<p style="margin:0 0 20px;color:${colors.plum};font-size:${size};line-height:1.6;${style}">${escapeHtml(text)}</p>`; return `<p style="margin:0 0 20px;color:${colors.plum};font-family:${emailFont};font-size:${size};line-height:1.6;${style}">${escapeHtml(text)}</p>`;
} }
+3 -2
View File
@@ -1,5 +1,5 @@
import type { PayloadEmailAdapter } from "payload"; import type { PayloadEmailAdapter } from "payload";
import { getMailFrom, getMailTransport } from "./transport"; import { getMailEnvelopeFrom, getMailFrom, getMailTransport } from "./transport";
/** Connects Payload's built-in auth emails to the same SMTP transport as ANOUMA's own emails. */ /** Connects Payload's built-in auth emails to the same SMTP transport as ANOUMA's own emails. */
export const payloadEmailAdapter: PayloadEmailAdapter = () => ({ export const payloadEmailAdapter: PayloadEmailAdapter = () => ({
@@ -9,7 +9,8 @@ export const payloadEmailAdapter: PayloadEmailAdapter = () => ({
sendEmail: async (message) => { sendEmail: async (message) => {
return getMailTransport().sendMail({ return getMailTransport().sendMail({
...message, ...message,
from: message.from || getMailFrom(), from: getMailFrom(),
envelope: { from: getMailEnvelopeFrom() },
}); });
}, },
}); });
+23 -18
View File
@@ -21,34 +21,39 @@ export function renderReminderEmailHtml(data: ReminderEmailData): string {
return `<!doctype html> return `<!doctype html>
<html lang="de"> <html lang="de">
<body style="margin:0;padding:0;background:${colors.creamLight};font-family:Georgia,'Times New Roman',serif;"> <head>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:${colors.creamLight};padding:32px 16px;"> <style>
@font-face { font-family: 'Antropos'; src: url('https://anouma.org/fonts/baar-antropos.ttf') format('truetype'); font-style: normal; font-weight: 100 900; }
</style>
</head>
<body style="margin:0;padding:0;background:${colors.creamLight};font-family:'Antropos',Georgia,'Times New Roman',serif;">
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:${colors.creamLight};padding:32px 16px;font-family:'Antropos',Georgia,'Times New Roman',serif;">
<tr> <tr>
<td align="center"> <td align="center">
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="max-width:480px;background:#ffffff;border-radius:24px;overflow:hidden;"> <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="max-width:480px;background:#ffffff;border-radius:24px;overflow:hidden;font-family:'Antropos',Georgia,'Times New Roman',serif;">
<tr> <tr>
<td style="background:${colors.mauveDark};padding:28px 32px;text-align:center;"> <td style="background:${colors.mauveDark};padding:28px 32px;text-align:center;font-family:'Antropos',Georgia,'Times New Roman',serif;">
<span style="color:#ffffff;font-size:22px;letter-spacing:0.12em;font-weight:600;">ANOUMA</span> <span style="color:#ffffff;font-family:'Antropos',Georgia,'Times New Roman',serif;font-size:22px;letter-spacing:0.12em;font-weight:600;">ANOUMA</span>
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="padding:32px;"> <td style="padding:32px;font-family:'Antropos',Georgia,'Times New Roman',serif;">
<p style="margin:0 0 20px;color:${colors.plum};font-size:16px;line-height:1.6;"> <p style="margin:0 0 20px;color:${colors.plum};font-family:'Antropos',Georgia,'Times New Roman',serif;font-size:16px;line-height:1.6;">
Hallo ${escapeHtml(recipientName)}, Hallo ${escapeHtml(recipientName)},
</p> </p>
<p style="margin:0 0 24px;color:${colors.plum};font-size:18px;line-height:1.6;font-style:italic;"> <p style="margin:0 0 24px;color:${colors.plum};font-family:'Antropos',Georgia,'Times New Roman',serif;font-size:18px;line-height:1.6;font-style:italic;">
Dein Online-Termin bei ANOUMA beginnt in ${minutesBefore} Minuten. Dein Online-Termin bei ANOUMA beginnt in ${minutesBefore} Minuten.
</p> </p>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:${colors.cream};border-radius:16px;"> <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:${colors.cream};border-radius:16px;">
<tr> <tr>
<td style="padding:20px 24px;"> <td style="padding:20px 24px;font-family:'Antropos',Georgia,'Times New Roman',serif;">
<p style="margin:0 0 12px;color:${colors.plum};font-size:14px;"> <p style="margin:0 0 12px;color:${colors.plum};font-family:'Antropos',Georgia,'Times New Roman',serif;font-size:14px;">
<strong>Termin:</strong><br />${escapeHtml(eventTitle)} <strong>Termin:</strong><br />${escapeHtml(eventTitle)}
</p> </p>
<p style="margin:0 0 12px;color:${colors.plum};font-size:14px;"> <p style="margin:0 0 12px;color:${colors.plum};font-family:'Antropos',Georgia,'Times New Roman',serif;font-size:14px;">
<strong>Datum:</strong><br />${escapeHtml(dateLabel)} <strong>Datum:</strong><br />${escapeHtml(dateLabel)}
</p> </p>
<p style="margin:0;color:${colors.plum};font-size:14px;"> <p style="margin:0;color:${colors.plum};font-family:'Antropos',Georgia,'Times New Roman',serif;font-size:14px;">
<strong>Uhrzeit:</strong><br />${escapeHtml(timeLabel)} Uhr <strong>Uhrzeit:</strong><br />${escapeHtml(timeLabel)} Uhr
</p> </p>
</td> </td>
@@ -57,26 +62,26 @@ export function renderReminderEmailHtml(data: ReminderEmailData): string {
<table role="presentation" cellpadding="0" cellspacing="0" style="margin:28px auto;"> <table role="presentation" cellpadding="0" cellspacing="0" style="margin:28px auto;">
<tr> <tr>
<td style="border-radius:999px;background:${colors.mauveDark};"> <td style="border-radius:999px;background:${colors.mauveDark};">
<a href="${joinUrl}" style="display:inline-block;padding:14px 32px;color:#ffffff;text-decoration:none;font-size:14px;font-weight:600;letter-spacing:0.02em;"> <a href="${joinUrl}" style="display:inline-block;padding:14px 32px;color:#ffffff;text-decoration:none;font-family:'Antropos',Georgia,'Times New Roman',serif;font-size:14px;font-weight:600;letter-spacing:0.02em;">
Meeting betreten Meeting betreten
</a> </a>
</td> </td>
</tr> </tr>
</table> </table>
<p style="margin:0 0 4px;color:${colors.plum};font-size:14px;"> <p style="margin:0 0 4px;color:${colors.plum};font-family:'Antropos',Georgia,'Times New Roman',serif;font-size:14px;">
<strong>Meeting-Passwort:</strong> <strong>Meeting-Passwort:</strong>
</p> </p>
<p style="margin:0 0 20px;color:${colors.plum};font-size:20px;letter-spacing:0.08em;font-family:'Courier New',monospace;"> <p style="margin:0 0 20px;color:${colors.plum};font-family:'Antropos',Georgia,'Times New Roman',serif;font-size:20px;letter-spacing:0.08em;">
${escapeHtml(meetingPassword)} ${escapeHtml(meetingPassword)}
</p> </p>
<p style="margin:0;color:${colors.plum};font-size:13px;line-height:1.6;"> <p style="margin:0;color:${colors.plum};font-family:'Antropos',Georgia,'Times New Roman',serif;font-size:13px;line-height:1.6;">
Bitte halte dein Passwort bereit. Beim Betreten des Meetings wirst du außerdem nach deinem Namen gefragt. Bitte halte dein Passwort bereit. Beim Betreten des Meetings wirst du außerdem nach deinem Namen gefragt.
</p> </p>
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="padding:20px 32px;background:${colors.cream};text-align:center;"> <td style="padding:20px 32px;background:${colors.cream};text-align:center;font-family:'Antropos',Georgia,'Times New Roman',serif;">
<span style="color:${colors.plum};font-size:12px;">Räume für Verbindung - mit dir selbst, miteinander und mit der Natur.</span> <span style="color:${colors.plum};font-family:'Antropos',Georgia,'Times New Roman',serif;font-size:12px;">Räume für Verbindung - mit dir selbst, miteinander und mit der Natur.</span>
</td> </td>
</tr> </tr>
</table> </table>
+2 -1
View File
@@ -1,4 +1,4 @@
import { getMailFrom, getMailTransport } from "./transport"; import { getMailEnvelopeFrom, getMailFrom, getMailTransport } from "./transport";
export async function sendEmail( export async function sendEmail(
to: string, to: string,
@@ -8,6 +8,7 @@ export async function sendEmail(
const transport = getMailTransport(); const transport = getMailTransport();
await transport.sendMail({ await transport.sendMail({
from: getMailFrom(), from: getMailFrom(),
envelope: { from: getMailEnvelopeFrom() },
to, to,
subject: template.subject, subject: template.subject,
html: template.html, html: template.html,
+2 -1
View File
@@ -1,10 +1,11 @@
import { getMailFrom, getMailTransport } from "./transport"; import { getMailEnvelopeFrom, getMailFrom, getMailTransport } from "./transport";
import { renderReminderEmailHtml, renderReminderEmailText, type ReminderEmailData } from "./reminderTemplate"; import { renderReminderEmailHtml, renderReminderEmailText, type ReminderEmailData } from "./reminderTemplate";
export async function sendReminderEmail(to: string, data: ReminderEmailData) { export async function sendReminderEmail(to: string, data: ReminderEmailData) {
const transport = getMailTransport(); const transport = getMailTransport();
await transport.sendMail({ await transport.sendMail({
from: getMailFrom(), from: getMailFrom(),
envelope: { from: getMailEnvelopeFrom() },
to, to,
subject: `Dein Online-Termin bei ANOUMA beginnt in ${data.minutesBefore} Minuten`, subject: `Dein Online-Termin bei ANOUMA beginnt in ${data.minutesBefore} Minuten`,
html: renderReminderEmailHtml(data), html: renderReminderEmailHtml(data),
+17 -1
View File
@@ -15,6 +15,12 @@ export function getMailTransport(): nodemailer.Transporter {
host: SMTP_HOST, host: SMTP_HOST,
port: Number(SMTP_PORT), port: Number(SMTP_PORT),
secure: Number(SMTP_PORT) === 465, secure: Number(SMTP_PORT) === 465,
tls: {
// Keep certificate verification enabled by default. A private SMTP
// proxy may expose a self-signed certificate; that deployment can
// explicitly opt out without weakening unrelated HTTPS requests.
rejectUnauthorized: process.env.SMTP_TLS_REJECT_UNAUTHORIZED !== "false",
},
connectionTimeout: 10000, connectionTimeout: 10000,
greetingTimeout: 10000, greetingTimeout: 10000,
socketTimeout: 20000, socketTimeout: 20000,
@@ -24,5 +30,15 @@ export function getMailTransport(): nodemailer.Transporter {
} }
export function getMailFrom(): string { export function getMailFrom(): string {
return process.env.SMTP_FROM || "ANOUMA <support@anouma.org>"; const configured = (process.env.SMTP_FROM || process.env.SMTP_USER || "support@anouma.org").trim();
const unquoted = configured.replace(/^("|')(.*)\1$/, "$2").trim();
// Keep the address in the message header, but show only ANOUMA in mail clients.
if (unquoted.includes("<") && unquoted.includes(">")) return unquoted;
return `ANOUMA <${unquoted}>`;
}
/** Envelope sender used by strict SMTP relays for sender ownership checks. */
export function getMailEnvelopeFrom(): string {
return process.env.SMTP_USER || "support@anouma.org";
} }
+2 -1
View File
@@ -313,6 +313,7 @@ SMTP_PORT=587
SMTP_USER= SMTP_USER=
SMTP_PASSWORD= SMTP_PASSWORD=
SMTP_FROM= SMTP_FROM=
SMTP_TLS_REJECT_UNAUTHORIZED=true
INBOUND_REPLY_TO=reply-to@anouma.org INBOUND_REPLY_TO=reply-to@anouma.org
INBOUND_EMAIL_SECRET= INBOUND_EMAIL_SECRET=
LIVEKIT_URL= LIVEKIT_URL=
@@ -352,7 +353,7 @@ if [ -z "$(env_get NEXT_PUBLIC_SERVER_URL)" ]; then
DOMAIN="$(ask "Domain" "anouma.org" "DOMAIN")" DOMAIN="$(ask "Domain" "anouma.org" "DOMAIN")"
APP_URL="$(ask "Application URL" "https://$DOMAIN" "APP_URL")" APP_URL="$(ask "Application URL" "https://$DOMAIN" "APP_URL")"
env_set NEXT_PUBLIC_SERVER_URL "$APP_URL" env_set NEXT_PUBLIC_SERVER_URL "$APP_URL"
[ -z "$(env_get SMTP_FROM)" ] && env_set SMTP_FROM "ANOUMA <no-reply@${DOMAIN}>" [ -z "$(env_get SMTP_FROM)" ] && env_set SMTP_FROM "no-reply@${DOMAIN}"
else else
log_info "NEXT_PUBLIC_SERVER_URL bereits vorhanden - unverändert." log_info "NEXT_PUBLIC_SERVER_URL bereits vorhanden - unverändert."
fi fi