env
All checks were successful
Build & Deploy KLZ Cables / build-and-deploy (push) Successful in 3m46s

This commit is contained in:
2026-01-28 00:34:40 +01:00
parent dab4f3f5b5
commit 8242687b07
8 changed files with 69 additions and 50 deletions

16
.env
View File

@@ -1,11 +1,10 @@
# Application # Application
NODE_ENV=production NODE_ENV=production
NEXT_PUBLIC_BASE_URL=https://klz-cables.com NEXT_PUBLIC_BASE_URL=https://klz-cables.com
UMAMI_SCRIPT_URL=https://analytics.infra.mintel.me/script.js
NEXT_PUBLIC_UMAMI_WEBSITE_ID=59a7db94-0100-4c7e-98ef-99f45b17f9c3
SENTRY_DSN=https://c10957d0182245b1a2a806ac2d34a197@errors.infra.mintel.me/1
LOG_LEVEL=info LOG_LEVEL=info
PDF_DEBUG_EXCEL=0
PDF_LOCALE=
PDF_MATCH=
PDF_LIMIT=0
# WooCommerce & WordPress # WooCommerce & WordPress
WOOCOMMERCE_URL=https://klz-cables.com WOOCOMMERCE_URL=https://klz-cables.com
@@ -13,15 +12,8 @@ WOOCOMMERCE_CONSUMER_KEY=ck_38d97df86880e8fefbd54ab5cdf47a9c5a9e5b39
WOOCOMMERCE_CONSUMER_SECRET=cs_d675ee2ac2ec7c22de84ae5451c07e42b1717759 WOOCOMMERCE_CONSUMER_SECRET=cs_d675ee2ac2ec7c22de84ae5451c07e42b1717759
WORDPRESS_APP_PASSWORD="DlJH 49dp fC3a Itc3 Sl7Z Wz0k" WORDPRESS_APP_PASSWORD="DlJH 49dp fC3a Itc3 Sl7Z Wz0k"
# Umami Analytics
NEXT_PUBLIC_UMAMI_WEBSITE_ID=59a7db94-0100-4c7e-98ef-99f45b17f9c3
UMAMI_SCRIPT_URL=https://analytics.infra.mintel.me/script.js
# GlitchTip (Sentry protocol)
SENTRY_DSN=https://c10957d0182245b1a2a806ac2d34a197@errors.infra.mintel.me/1
# Redis Cache # Redis Cache
REDIS_URL= REDIS_URL=redis://redis:6379/2
REDIS_KEY_PREFIX=klz: REDIS_KEY_PREFIX=klz:
# SMTP Configuration # SMTP Configuration

View File

@@ -74,8 +74,8 @@ jobs:
echo "" echo ""
echo "📦 Build Arguments:" echo "📦 Build Arguments:"
echo " • NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${{ secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID != '' && '***' || 'NOT SET' }}" echo " • NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${{ secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID != '' && '***' || 'NOT SET' }}"
echo " • NEXT_PUBLIC_UMAMI_SCRIPT_URL: ${{ secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL != '' && '***' || 'NOT SET' }}"
echo " • SENTRY_DSN: ${{ secrets.SENTRY_DSN != '' && '***' || 'NOT SET' }}" echo " • SENTRY_DSN: ${{ secrets.SENTRY_DSN != '' && '***' || 'NOT SET' }}"
echo " • NEXT_PUBLIC_SENTRY_DSN: ${{ secrets.NEXT_PUBLIC_SENTRY_DSN != '' && '***' || 'NOT SET' }}"
echo " • NEXT_PUBLIC_BASE_URL: ${{ secrets.NEXT_PUBLIC_BASE_URL != '' && '***' || 'NOT SET' }}" echo " • NEXT_PUBLIC_BASE_URL: ${{ secrets.NEXT_PUBLIC_BASE_URL != '' && '***' || 'NOT SET' }}"
echo "" echo ""
echo "⏱️ Build started at: $(date -u +'%Y-%m-%d %H:%M:%S UTC')" echo "⏱️ Build started at: $(date -u +'%Y-%m-%d %H:%M:%S UTC')"
@@ -87,8 +87,8 @@ jobs:
--pull \ --pull \
--platform linux/arm64 \ --platform linux/arm64 \
--build-arg NEXT_PUBLIC_UMAMI_WEBSITE_ID="${{ secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID }}" \ --build-arg NEXT_PUBLIC_UMAMI_WEBSITE_ID="${{ secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID }}" \
--build-arg NEXT_PUBLIC_UMAMI_SCRIPT_URL="${{ secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL }}" \
--build-arg SENTRY_DSN="${{ secrets.SENTRY_DSN }}" \ --build-arg SENTRY_DSN="${{ secrets.SENTRY_DSN }}" \
--build-arg NEXT_PUBLIC_SENTRY_DSN="${{ secrets.NEXT_PUBLIC_SENTRY_DSN }}" \
--build-arg NEXT_PUBLIC_BASE_URL="${{ secrets.NEXT_PUBLIC_BASE_URL }}" \ --build-arg NEXT_PUBLIC_BASE_URL="${{ secrets.NEXT_PUBLIC_BASE_URL }}" \
-t registry.infra.mintel.me/mintel/klz-cables.com:latest \ -t registry.infra.mintel.me/mintel/klz-cables.com:latest \
--push . --push .
@@ -160,11 +160,13 @@ jobs:
MAIL_RECIPIENTS='${{ secrets.MAIL_RECIPIENTS }}' \ MAIL_RECIPIENTS='${{ secrets.MAIL_RECIPIENTS }}' \
MAIL_USERNAME='${{ secrets.MAIL_USERNAME }}' \ MAIL_USERNAME='${{ secrets.MAIL_USERNAME }}' \
NEXT_PUBLIC_BASE_URL='${{ secrets.NEXT_PUBLIC_BASE_URL }}' \ NEXT_PUBLIC_BASE_URL='${{ secrets.NEXT_PUBLIC_BASE_URL }}' \
NEXT_PUBLIC_SENTRY_DSN='${{ secrets.NEXT_PUBLIC_SENTRY_DSN }}' \
NEXT_PUBLIC_UMAMI_WEBSITE_ID='${{ secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID }}' \ NEXT_PUBLIC_UMAMI_WEBSITE_ID='${{ secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID }}' \
NEXT_PUBLIC_UMAMI_SCRIPT_URL='${{ secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL }}' \
NODE_ENV='${{ secrets.NODE_ENV }}' \ NODE_ENV='${{ secrets.NODE_ENV }}' \
SENTRY_DSN='${{ secrets.SENTRY_DSN }}' \ SENTRY_DSN='${{ secrets.SENTRY_DSN }}' \
sudo -u deploy -H -E /home/deploy/deploy.sh" REDIS_URL='${{ secrets.REDIS_URL }}' \
REDIS_KEY_PREFIX='${{ secrets.REDIS_KEY_PREFIX }}' \
/home/deploy/deploy.sh"
DEPLOY_EXIT_CODE=$? DEPLOY_EXIT_CODE=$?
echo "" echo ""

View File

@@ -25,12 +25,11 @@ ENV NEXT_TELEMETRY_DISABLED=1
ARG NEXT_PUBLIC_UMAMI_WEBSITE_ID ARG NEXT_PUBLIC_UMAMI_WEBSITE_ID
ARG NEXT_PUBLIC_UMAMI_SCRIPT_URL ARG NEXT_PUBLIC_UMAMI_SCRIPT_URL
ARG SENTRY_DSN ARG SENTRY_DSN
ARG NEXT_PUBLIC_SENTRY_DSN
ARG NEXT_PUBLIC_BASE_URL ARG NEXT_PUBLIC_BASE_URL
ENV NEXT_PUBLIC_UMAMI_WEBSITE_ID=$NEXT_PUBLIC_UMAMI_WEBSITE_ID ENV NEXT_PUBLIC_UMAMI_WEBSITE_ID=$NEXT_PUBLIC_UMAMI_WEBSITE_ID
ENV NEXT_PUBLIC_UMAMI_SCRIPT_URL=$NEXT_PUBLIC_UMAMI_SCRIPT_URL ENV NEXT_PUBLIC_UMAMI_SCRIPT_URL=$NEXT_PUBLIC_UMAMI_SCRIPT_URL
ENV SENTRY_DSN=$SENTRY_DSN ENV SENTRY_DSN=$SENTRY_DSN
ENV NEXT_PUBLIC_SENTRY_DSN=$NEXT_PUBLIC_SENTRY_DSN
ENV NEXT_PUBLIC_BASE_URL=$NEXT_PUBLIC_BASE_URL ENV NEXT_PUBLIC_BASE_URL=$NEXT_PUBLIC_BASE_URL
RUN npm run build RUN npm run build

View File

@@ -0,0 +1,4 @@
services:
app:
env_file:
- .env

View File

@@ -57,24 +57,19 @@ services:
retries: 5 retries: 5
start_period: 30s start_period: 30s
environment: environment:
# Umami - NODE_ENV=${NODE_ENV:-production}
- NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL:?NEXT_PUBLIC_BASE_URL is required}
- NEXT_PUBLIC_UMAMI_WEBSITE_ID=${NEXT_PUBLIC_UMAMI_WEBSITE_ID} - NEXT_PUBLIC_UMAMI_WEBSITE_ID=${NEXT_PUBLIC_UMAMI_WEBSITE_ID}
- NEXT_PUBLIC_UMAMI_SCRIPT_URL=${NEXT_PUBLIC_UMAMI_SCRIPT_URL:-https://analytics.infra.mintel.me/script.js} - NEXT_PUBLIC_UMAMI_SCRIPT_URL=${NEXT_PUBLIC_UMAMI_SCRIPT_URL}
# GlitchTip (Sentry protocol)
- SENTRY_DSN=${SENTRY_DSN} - SENTRY_DSN=${SENTRY_DSN}
- NEXT_PUBLIC_SENTRY_DSN=${NEXT_PUBLIC_SENTRY_DSN}
# Redis (app-spezifischer DB-Index)
- REDIS_URL=${REDIS_URL:-redis://redis:6379/2}
- REDIS_KEY_PREFIX=${REDIS_KEY_PREFIX:-klz:}
# Mail
- MAIL_HOST=${MAIL_HOST} - MAIL_HOST=${MAIL_HOST}
- MAIL_PORT=${MAIL_PORT} - MAIL_PORT=${MAIL_PORT:-587}
- MAIL_USERNAME=${MAIL_USERNAME} - MAIL_USERNAME=${MAIL_USERNAME}
- MAIL_PASSWORD=${MAIL_PASSWORD} - MAIL_PASSWORD=${MAIL_PASSWORD}
- MAIL_FROM=${MAIL_FROM} - MAIL_FROM=${MAIL_FROM}
- MAIL_RECIPIENTS=${MAIL_RECIPIENTS} - MAIL_RECIPIENTS=${MAIL_RECIPIENTS}
# App - REDIS_URL=${REDIS_URL}
- NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL} - REDIS_KEY_PREFIX=${REDIS_KEY_PREFIX}
networks: networks:
infra: infra:

View File

@@ -111,16 +111,33 @@ Next.js App (port 3000)
### Build-time (in Dockerfile/Workflow) ### Build-time (in Dockerfile/Workflow)
These variables are baked into the Docker image during the build process.
- `NEXT_PUBLIC_BASE_URL` (Required)
- `NEXT_PUBLIC_UMAMI_WEBSITE_ID` - `NEXT_PUBLIC_UMAMI_WEBSITE_ID`
- `NEXT_PUBLIC_UMAMI_SCRIPT_URL` - `NEXT_PUBLIC_UMAMI_SCRIPT_URL`
- `NEXT_PUBLIC_SENTRY_DSN` - `SENTRY_DSN`
### Runtime (in docker-compose.yml) ### Runtime (in docker-compose.yml)
These variables are passed to the container at runtime.
- `NODE_ENV` (Defaults to `production`)
- `NEXT_PUBLIC_BASE_URL` (Must be passed again at runtime for server-side use)
- `SENTRY_DSN` - `SENTRY_DSN`
- `MAIL_HOST`
- `MAIL_PORT`
- `MAIL_USERNAME`
- `MAIL_PASSWORD`
- `MAIL_FROM`
- `MAIL_RECIPIENTS`
- `REDIS_URL` - `REDIS_URL`
- `REDIS_KEY_PREFIX` - `REDIS_KEY_PREFIX`
### Local Development
For local development, use a `.env` file. The `docker-compose.override.yml` file automatically loads this file for the `app` service.
## Monitoring ## Monitoring
### Health Checks ### Health Checks

View File

@@ -12,20 +12,40 @@ const getEnv = (key: string, defaultValue?: string): string | undefined => {
return (process.env as any)[key] || defaultValue; return (process.env as any)[key] || defaultValue;
} }
if (typeof process === 'undefined') return defaultValue;
if (typeof process === 'undefined') return defaultValue; if (typeof process === 'undefined') return defaultValue;
// In Docker/Production, variables are in process.env // In Docker/Production, variables are in process.env
// In local development, they might be in .env // In local development, they might be in .env
const value = process.env[key]; const value = process.env[key];
if (value !== undefined && value !== '') { // Check for quoted values (common when passed via SSH/Docker)
return value; if (typeof value === 'string') {
const trimmed = value.trim();
if ((trimmed.startsWith("'") && trimmed.endsWith("'")) ||
(trimmed.startsWith('"') && trimmed.endsWith('"'))) {
return trimmed.slice(1, -1);
}
if (trimmed !== '') return trimmed;
} }
return defaultValue; return defaultValue;
}; };
const isProduction = getEnv('NODE_ENV') === 'production';
// Required variables in production
if (isProduction && typeof window === 'undefined') {
const required = [
'NEXT_PUBLIC_BASE_URL',
];
for (const key of required) {
if (!getEnv(key)) {
throw new Error(`Missing required environment variable: ${key}`);
}
}
}
export const config = { export const config = {
env: getEnv('NODE_ENV', 'development'), env: getEnv('NODE_ENV', 'development'),
isProduction: getEnv('NODE_ENV') === 'production', isProduction: getEnv('NODE_ENV') === 'production',
@@ -37,7 +57,7 @@ export const config = {
analytics: { analytics: {
umami: { umami: {
websiteId: getEnv('NEXT_PUBLIC_UMAMI_WEBSITE_ID'), websiteId: getEnv('NEXT_PUBLIC_UMAMI_WEBSITE_ID'),
scriptUrl: getEnv('UMAMI_SCRIPT_URL', 'https://analytics.infra.mintel.me/script.js'), scriptUrl: getEnv('NEXT_PUBLIC_UMAMI_SCRIPT_URL', 'https://analytics.infra.mintel.me/script.js'),
// The proxied path used in the frontend // The proxied path used in the frontend
proxyPath: '/stats/script.js', proxyPath: '/stats/script.js',
enabled: Boolean(getEnv('NEXT_PUBLIC_UMAMI_WEBSITE_ID')), enabled: Boolean(getEnv('NEXT_PUBLIC_UMAMI_WEBSITE_ID')),
@@ -74,16 +94,6 @@ export const config = {
from: getEnv('MAIL_FROM'), from: getEnv('MAIL_FROM'),
recipients: getEnv('MAIL_RECIPIENTS', '')?.split(',').filter(Boolean) || [], recipients: getEnv('MAIL_RECIPIENTS', '')?.split(',').filter(Boolean) || [],
}, },
woocommerce: {
url: getEnv('WOOCOMMERCE_URL'),
consumerKey: getEnv('WOOCOMMERCE_CONSUMER_KEY'),
consumerSecret: getEnv('WOOCOMMERCE_CONSUMER_SECRET'),
},
wordpress: {
appPassword: getEnv('WORDPRESS_APP_PASSWORD'),
},
} as const; } as const;
/** /**

View File

@@ -322,7 +322,7 @@ const nextConfig = {
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;", contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
}, },
async rewrites() { async rewrites() {
const umamiUrl = (process.env.UMAMI_SCRIPT_URL || 'https://analytics.infra.mintel.me').replace('/script.js', ''); const umamiUrl = (process.env.NEXT_PUBLIC_UMAMI_SCRIPT_URL || 'https://analytics.infra.mintel.me').replace('/script.js', '');
const glitchtipUrl = process.env.SENTRY_DSN const glitchtipUrl = process.env.SENTRY_DSN
? new URL(process.env.SENTRY_DSN).origin ? new URL(process.env.SENTRY_DSN).origin
: 'https://errors.infra.mintel.me'; : 'https://errors.infra.mintel.me';