Compare commits

...

7 Commits

Author SHA1 Message Date
6b7236ba97 fix: add refined .gitignore and exclude Directus volume data
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Successful in 2m33s
Monorepo Pipeline / 🚀 Release (push) Successful in 2m31s
Monorepo Pipeline / 🐳 Build & Push Images (push) Failing after 9m31s
2026-02-03 12:05:01 +01:00
40a95b5353 fix: implement Lean Docker strategy with mintel/runtime and remove explicit container_name fields
Some checks failed
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
Monorepo Pipeline / 🐳 Build & Push Images (push) Has been cancelled
Monorepo Pipeline / 🧪 Quality Assurance (push) Has been cancelled
2026-02-03 11:59:44 +01:00
7329e00125 fix: remove image tags from application compose files to avoid registry ambiguity
Some checks failed
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
Monorepo Pipeline / 🐳 Build & Push Images (push) Has been cancelled
Monorepo Pipeline / 🧪 Quality Assurance (push) Has been cancelled
2026-02-03 11:53:01 +01:00
94be60ba4e fix: correct Docker registry strategy, add custom Directus Dockerfile, and revert app tagging 2026-02-03 11:51:45 +01:00
a8bc039c02 feat: implement centralized Docker base-image strategy and automate registry pushes
All checks were successful
Monorepo Pipeline / 🧪 Quality Assurance (push) Successful in 2m33s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build & Push Images (push) Has been skipped
2026-02-03 11:50:17 +01:00
653deb7995 feat: adapt klz-2026 high-performance Docker setup and environment handling
All checks were successful
Monorepo Pipeline / 🧪 Quality Assurance (push) Successful in 2m33s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
2026-02-03 11:44:16 +01:00
61f65107f2 feat: standardize NPM scripts across monorepo via @mintel/cli enhancement
Some checks failed
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
Monorepo Pipeline / 🧪 Quality Assurance (push) Has been cancelled
2026-02-03 11:31:50 +01:00
16 changed files with 486 additions and 121 deletions

View File

@@ -80,3 +80,63 @@ jobs:
echo "🏷️ Tag detected [${{ github.ref_name }}], performing sync release..."
pnpm sync-versions
pnpm release:tag
build-images:
name: 🐳 Build & Push Images
needs: qa
if: startsWith(github.ref, 'refs/tags/v')
runs-on: docker
steps:
- name: Checkout
uses: actions/checkout@v4
- name: 🐳 Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: 🔐 Registry Login
run: |
echo "${{ secrets.REGISTRY_PASS }}" | docker login registry.infra.mintel.me -u "${{ secrets.REGISTRY_USER }}" --password-stdin
- name: 🏗️ Build & Push Nextjs Build-Base
env:
TAG: ${{ github.ref_name }}
run: |
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t registry.infra.mintel.me/mintel/nextjs:$TAG \
-t registry.infra.mintel.me/mintel/nextjs:latest \
-f packages/infra/docker/Dockerfile.nextjs \
--push .
- name: 🏗️ Build & Push Production Runtime
env:
TAG: ${{ github.ref_name }}
run: |
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t registry.infra.mintel.me/mintel/runtime:$TAG \
-t registry.infra.mintel.me/mintel/runtime:latest \
-f packages/infra/docker/Dockerfile.runtime \
--push .
- name: 🏗️ Build & Push Gatekeeper (Product)
env:
TAG: ${{ github.ref_name }}
run: |
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t registry.infra.mintel.me/mintel/gatekeeper:$TAG \
-t registry.infra.mintel.me/mintel/gatekeeper:latest \
-f packages/infra/docker/Dockerfile.gatekeeper \
--push .
- name: 🏗️ Build & Push Directus (Base)
env:
TAG: ${{ github.ref_name }}
run: |
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t registry.infra.mintel.me/mintel/directus:$TAG \
-t registry.infra.mintel.me/mintel/directus:latest \
-f packages/infra/docker/Dockerfile.directus \
--push .

View File

@@ -0,0 +1,31 @@
# Project
PROJECT_NAME=sample-website
PROJECT_COLOR=#82ed20
# Authentication
GATEKEEPER_PASSWORD=mintel
AUTH_COOKIE_NAME=mintel_gatekeeper_session
# Host Config (Local)
TRAEFIK_HOST=sample-website.localhost
DIRECTUS_HOST=cms.sample-website.localhost
# Next.js
NEXT_PUBLIC_BASE_URL=http://sample-website.localhost
# Directus
DIRECTUS_URL=http://localhost:8055
DIRECTUS_KEY=sample-key-123
DIRECTUS_SECRET=sample-secret-123
DIRECTUS_ADMIN_EMAIL=admin@mintel.me
DIRECTUS_ADMIN_PASSWORD=mintel-admin-pass
DIRECTUS_DB_NAME=directus
DIRECTUS_DB_USER=directus
DIRECTUS_DB_PASSWORD=mintel-db-pass
# Sentry / Glitchtip
SENTRY_DSN=
# Analytics (Umami)
NEXT_PUBLIC_UMAMI_WEBSITE_ID=
NEXT_PUBLIC_UMAMI_SCRIPT_URL=https://analytics.infra.mintel.me/script.js

34
apps/sample-website/.gitignore vendored Normal file
View File

@@ -0,0 +1,34 @@
# dependencies
/node_modules
/.pnpm-debug.log*
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
# directus
/directus/uploads
/directus/extensions
/.env

View File

@@ -0,0 +1,38 @@
# Start from the pre-built Nextjs Base image
FROM registry.infra.mintel.me/mintel/nextjs:latest AS builder
WORKDIR /app
# Build-time environment variables for Next.js
ARG NEXT_PUBLIC_BASE_URL
ARG NEXT_PUBLIC_UMAMI_WEBSITE_ID
ARG NEXT_PUBLIC_UMAMI_SCRIPT_URL
ARG NEXT_PUBLIC_TARGET
ARG DIRECTUS_URL
ENV NEXT_PUBLIC_BASE_URL=$NEXT_PUBLIC_BASE_URL
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_TARGET=$NEXT_PUBLIC_TARGET
ENV DIRECTUS_URL=$DIRECTUS_URL
# Build the specific application
RUN pnpm --filter sample-website build
# Production runner image
FROM registry.infra.mintel.me/mintel/runtime:latest AS runner
WORKDIR /app
COPY --from=builder /app/apps/sample-website/public ./apps/sample-website/public
# Set the correct permission for prerender cache
RUN mkdir -p apps/sample-website/.next && chown nextjs:nodejs apps/sample-website/.next
# Copy standalone output and static files from the monorepo path
COPY --from=builder --chown=nextjs:nodejs /app/apps/sample-website/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/apps/sample-website/.next/static ./apps/sample-website/.next/static
USER nextjs
# server.js in monorepo standalone is created for each app
CMD ["node", "apps/sample-website/server.js"]

View File

@@ -0,0 +1,71 @@
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
NEXT_PUBLIC_BASE_URL: ${NEXT_PUBLIC_BASE_URL:-http://localhost:3000}
NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${NEXT_PUBLIC_UMAMI_WEBSITE_ID}
NEXT_PUBLIC_UMAMI_SCRIPT_URL: ${NEXT_PUBLIC_UMAMI_SCRIPT_URL}
NEXT_PUBLIC_TARGET: ${TARGET:-development}
DIRECTUS_URL: ${DIRECTUS_URL:-http://directus:8055}
restart: always
networks:
- infra
env_file:
- .env
ports:
- "3000:3000"
labels:
- "traefik.enable=true"
- "traefik.http.routers.sample-website.rule=Host(`${TRAEFIK_HOST:-sample-website.localhost}`)"
- "traefik.http.services.sample-website.loadbalancer.server.port=3000"
directus:
image: registry.infra.mintel.me/mintel/directus:latest
restart: always
networks:
- infra
env_file:
- .env
environment:
KEY: ${DIRECTUS_KEY:-mintel-key}
SECRET: ${DIRECTUS_SECRET:-mintel-secret}
ADMIN_EMAIL: ${DIRECTUS_ADMIN_EMAIL:-admin@mintel.me}
ADMIN_PASSWORD: ${DIRECTUS_ADMIN_PASSWORD:-mintel-admin}
DB_CLIENT: 'pg'
DB_HOST: 'directus-db'
DB_PORT: '5432'
DB_DATABASE: ${DIRECTUS_DB_NAME:-directus}
DB_USER: ${DIRECTUS_DB_USER:-directus}
DB_PASSWORD: ${DIRECTUS_DB_PASSWORD:-mintel-db-pass}
WEBSOCKETS_ENABLED: 'true'
PUBLIC_URL: ${DIRECTUS_URL:-http://localhost:8055}
ports:
- "8055:8055"
volumes:
- ./directus/uploads:/directus/uploads
- ./directus/extensions:/directus/extensions
labels:
- "traefik.enable=true"
- "traefik.http.routers.sample-website-directus.rule=Host(`${DIRECTUS_HOST:-cms.sample-website.localhost}`)"
- "traefik.http.services.sample-website-directus.loadbalancer.server.port=8055"
directus-db:
image: postgres:15-alpine
restart: always
networks:
- infra
environment:
POSTGRES_DB: ${DIRECTUS_DB_NAME:-directus}
POSTGRES_USER: ${DIRECTUS_DB_USER:-directus}
POSTGRES_PASSWORD: ${DIRECTUS_DB_PASSWORD:-mintel-db-pass}
volumes:
- directus-db-data:/var/lib/postgresql/data
networks:
infra:
external: true
volumes:
directus-db-data:

View File

@@ -4,11 +4,21 @@
"private": true,
"type": "module",
"scripts": {
"dev": "next dev",
"dev": "mintel dev",
"dev:local": "mintel dev --local",
"build": "next build",
"start": "next start",
"lint": "next lint",
"test": "vitest run"
"typecheck": "tsc --noEmit",
"test": "vitest run --passWithNoTests",
"cms:bootstrap": "mintel directus bootstrap",
"cms:push:testing": "mintel directus sync push testing",
"cms:pull:testing": "mintel directus sync pull testing",
"cms:push:staging": "mintel directus sync push staging",
"cms:pull:staging": "mintel directus sync pull staging",
"cms:push:prod": "mintel directus sync push production",
"cms:pull:prod": "mintel directus sync pull production",
"pagespeed:test": "mintel pagespeed"
},
"dependencies": {
"@mintel/next-utils": "workspace:*",

View File

@@ -10,7 +10,76 @@ const program = new Command();
program
.name("mintel")
.description("CLI for Mintel monorepo management")
.version("1.0.0");
.version("1.0.1");
program
.command("dev")
.description("Start the development environment (Docker stack)")
.option("-l, --local", "Run Next.js locally instead of in Docker")
.action(async (options) => {
const { execSync } = await import("child_process");
console.log(chalk.blue("🚀 Starting Development Environment..."));
if (options.local) {
console.log(chalk.cyan("Running Next.js locally..."));
execSync("next dev", { stdio: "inherit" });
} else {
console.log(chalk.cyan("Starting Docker stack (App, Directus, DB)..."));
// Ensure network exists
try {
execSync("docker network create infra", { stdio: "ignore" });
} catch (e) {}
console.log(
chalk.yellow(`
📱 App: http://localhost:3000
🗄️ CMS: http://localhost:8055/admin
🚦 Traefik: http://localhost:8080
`),
);
execSync(
"docker-compose down --remove-orphans && docker-compose up app directus directus-db",
{ stdio: "inherit" },
);
}
});
const directus = program
.command("directus")
.description("Directus management commands");
directus
.command("bootstrap")
.description("Setup Directus branding and settings")
.action(async () => {
const { execSync } = await import("child_process");
console.log(chalk.blue("🎨 Bootstrapping Directus..."));
execSync("npx tsx --env-file=.env scripts/setup-directus.ts", {
stdio: "inherit",
});
});
directus
.command("sync <action> <env>")
.description("Sync Directus data (push/pull) for a specific environment")
.action(async (action, env) => {
const { execSync } = await import("child_process");
console.log(
chalk.blue(`📥 Executing Directus sync: ${action} -> ${env}...`),
);
execSync(`./scripts/sync-directus.sh ${action} ${env}`, {
stdio: "inherit",
});
});
program
.command("pagespeed")
.description("Run PageSpeed (Lighthouse) tests")
.action(async () => {
const { execSync } = await import("child_process");
console.log(chalk.blue("⚡ Running PageSpeed tests..."));
execSync("npx tsx ./scripts/pagespeed-sitemap.ts", { stdio: "inherit" });
});
program
.command("init <path>")
@@ -34,10 +103,21 @@ program
private: true,
type: "module",
scripts: {
dev: "next dev",
dev: "mintel dev",
"dev:local": "mintel dev --local",
build: "next build",
start: "next start",
lint: "next lint",
typecheck: "tsc --noEmit",
test: "vitest run --passWithNoTests",
"directus:bootstrap": "mintel directus bootstrap",
"directus:push:testing": "mintel directus sync push testing",
"directus:pull:testing": "mintel directus sync pull testing",
"directus:push:staging": "mintel directus sync push staging",
"directus:pull:staging": "mintel directus sync pull staging",
"directus:push:prod": "mintel directus sync push production",
"directus:pull:prod": "mintel directus sync pull production",
"pagespeed:test": "mintel pagespeed",
},
dependencies: {
next: "15.1.6",
@@ -238,34 +318,56 @@ export default function Home() {
// Copy infra templates
const infraPath = path.resolve(__dirname, "../../infra");
if (await fs.pathExists(infraPath)) {
await fs.copy(
path.join(infraPath, "docker/Dockerfile.nextjs"),
path.join(fullPath, "Dockerfile"),
// Setup Dockerfile from template
const templatePath = path.join(
infraPath,
"docker/Dockerfile.app-template",
);
await fs.copy(
path.join(infraPath, "docker/docker-compose.template.yml"),
path.join(fullPath, "docker-compose.yml"),
if (await fs.pathExists(templatePath)) {
let dockerfile = await fs.readFile(templatePath, "utf8");
dockerfile = dockerfile.replace(/\$\{APP_NAME:-app\}/g, projectName);
await fs.writeFile(path.join(fullPath, "Dockerfile"), dockerfile);
}
// Setup docker-compose from template
const composeTemplatePath = path.join(
infraPath,
"docker/docker-compose.template.yml",
);
if (await fs.pathExists(composeTemplatePath)) {
let compose = await fs.readFile(composeTemplatePath, "utf8");
compose = compose.replace(/\$\{APP_NAME:-app\}/g, projectName);
compose = compose.replace(/\$\{PROJECT_NAME:-app\}/g, projectName);
await fs.writeFile(
path.join(fullPath, "docker-compose.yml"),
compose,
);
}
await fs.ensureDir(path.join(fullPath, ".gitea/workflows"));
await fs.copy(
path.join(infraPath, "gitea/deploy-action.yml"),
path.join(fullPath, ".gitea/workflows/deploy.yml"),
const deployActionPath = path.join(
infraPath,
"gitea/deploy-action.yml",
);
if (await fs.pathExists(deployActionPath)) {
await fs.copy(
deployActionPath,
path.join(fullPath, ".gitea/workflows/deploy.yml"),
);
}
}
// Create Directus structure
await fs.ensureDir(path.join(fullPath, "directus/uploads"));
await fs.ensureDir(path.join(fullPath, "directus/extensions"));
await fs.writeFile(
path.join(fullPath, "directus/uploads/.gitkeep"),
"",
);
await fs.writeFile(
path.join(fullPath, "directus/extensions/.gitkeep"),
"",
);
// Create Directus structure
await fs.ensureDir(path.join(fullPath, "directus/uploads"));
await fs.ensureDir(path.join(fullPath, "directus/extensions"));
await fs.writeFile(path.join(fullPath, "directus/uploads/.gitkeep"), "");
await fs.writeFile(
path.join(fullPath, "directus/extensions/.gitkeep"),
"",
);
// Create .env.example
const envExample = `# Project
// Create .env.example
const envExample = `# Project
PROJECT_NAME=${projectName}
PROJECT_COLOR=#82ed20
@@ -297,14 +399,13 @@ SENTRY_DSN=
NEXT_PUBLIC_UMAMI_WEBSITE_ID=
NEXT_PUBLIC_UMAMI_SCRIPT_URL=https://analytics.infra.mintel.me/script.js
`;
await fs.writeFile(path.join(fullPath, ".env.example"), envExample);
await fs.writeFile(path.join(fullPath, ".env.example"), envExample);
// Copy premium templates (globals.css, lib/directus.ts, scripts/setup-directus.ts)
const templatePath = path.join(infraPath, "templates/website");
if (await fs.pathExists(templatePath)) {
console.log(chalk.blue("Applying premium templates..."));
await fs.copy(templatePath, fullPath, { overwrite: true });
}
// Copy premium templates (globals.css, lib/directus.ts, scripts/setup-directus.ts)
const templatePath = path.join(infraPath, "templates/website");
if (await fs.pathExists(templatePath)) {
console.log(chalk.blue("Applying premium templates..."));
await fs.copy(templatePath, fullPath, { overwrite: true });
}
console.log(

View File

@@ -0,0 +1,33 @@
# Start from the pre-built Nextjs Base image
FROM registry.infra.mintel.me/mintel/nextjs:latest AS builder
WORKDIR /app
# Build-time environment variables for Next.js
ARG NEXT_PUBLIC_BASE_URL
ARG NEXT_PUBLIC_UMAMI_WEBSITE_ID
ARG NEXT_PUBLIC_UMAMI_SCRIPT_URL
ARG NEXT_PUBLIC_TARGET
ARG DIRECTUS_URL
ENV NEXT_PUBLIC_BASE_URL=$NEXT_PUBLIC_BASE_URL
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_TARGET=$NEXT_PUBLIC_TARGET
ENV DIRECTUS_URL=$DIRECTUS_URL
# Build the specific application
RUN pnpm --filter ${APP_NAME:-app} build
# Production runner image
FROM registry.infra.mintel.me/mintel/runtime:latest AS runner
WORKDIR /app
# Copy standalone output and static files
COPY --from=builder --chown=nextjs:nodejs /app/apps/${APP_NAME:-app}/public ./apps/${APP_NAME:-app}/public
COPY --from=builder --chown=nextjs:nodejs /app/apps/${APP_NAME:-app}/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/apps/${APP_NAME:-app}/.next/static ./apps/${APP_NAME:-app}/.next/static
USER nextjs
CMD ["node", "apps/${APP_NAME:-app}/server.js"]

View File

@@ -0,0 +1,12 @@
FROM directus/directus:11
# Add any custom extensions or configurations here if needed
# COPY ./extensions /directus/extensions
# Default environment for optimized production use
ENV LOGGER_LEVEL="info"
ENV WEBSOCKETS_ENABLED="true"
# Health check
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8055/health || exit 1

View File

@@ -1,47 +1,42 @@
FROM node:20-alpine AS base
# Install dependencies only when needed
FROM base AS deps
RUN apk add --no-cache libc6-compat
RUN apk add --no-cache libc6-compat curl
WORKDIR /app
# Install dependencies
COPY package.json pnpm-lock.yaml* ./
RUN corepack enable pnpm && pnpm i --frozen-lockfile
# Enable pnpm
RUN corepack enable pnpm
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
# Install dependencies (using monorepo root context)
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json .npmrc* ./
COPY packages/gatekeeper/package.json ./packages/gatekeeper/
COPY packages/next-utils/package.json ./packages/next-utils/
COPY packages/tsconfig/package.json ./packages/tsconfig/
COPY packages/eslint-config/package.json ./packages/eslint-config/
COPY packages/next-config/package.json ./packages/next-config/
RUN --mount=type=cache,target=/root/.local/share/pnpm/store/v3 \
pnpm i --frozen-lockfile
# Copy source
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
# Build Gatekeeper
RUN pnpm --filter @mintel/gatekeeper build
# Build the application
RUN corepack enable pnpm && pnpm run build
# Production image, copy all the files and run next
# Runner
FROM base AS runner
WORKDIR /app
RUN apk add --no-cache curl
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder /app/packages/gatekeeper/public ./packages/gatekeeper/public
COPY --from=builder /app/packages/gatekeeper/.next/standalone ./
COPY --from=builder /app/packages/gatekeeper/.next/static ./packages/gatekeeper/.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]
CMD ["node", "packages/gatekeeper/server.js"]

View File

@@ -1,66 +1,24 @@
FROM node:20-alpine AS base
# Install dependencies only when needed
FROM base AS deps
RUN apk add --no-cache libc6-compat curl
WORKDIR /app
# Install dependencies based on the preferred package manager
COPY package.json package-lock.json* pnpm-lock.yaml* ./
RUN if [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
else npm i; fi
# Enable pnpm
RUN corepack enable pnpm
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
# Copy root configurations
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json .npmrc* ./
# Copy all package.json files to allow pnpm install to be cached
COPY packages/*/package.json ./packages/
COPY apps/*/package.json ./apps/
# Install dependencies for the entire monorepo
RUN --mount=type=cache,target=/root/.local/share/pnpm/store/v3 \
pnpm i --frozen-lockfile
# Copy the rest of the source code
COPY . .
# Next.js collects completely anonymous telemetry data about general usage.
ENV NEXT_TELEMETRY_DISABLED=1
# Build-time environment variables for Next.js
ARG NEXT_PUBLIC_BASE_URL
ARG NEXT_PUBLIC_UMAMI_WEBSITE_ID
ARG NEXT_PUBLIC_UMAMI_SCRIPT_URL
ENV NEXT_PUBLIC_BASE_URL=$NEXT_PUBLIC_BASE_URL
ENV NEXT_PUBLIC_UMAMI_WEBSITE_ID=$NEXT_PUBLIC_UMAMI_WEBSITE_ID
ENV NEXT_PUBLIC_UMAMI_SCRIPT_URL=$NEXT_PUBLIC_UMAMI_SCRIPT_URL
# Build the application
RUN if [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
else npm run build; fi
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
# Install curl for health checks
RUN apk add --no-cache curl
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next
# Automatically leverage output traces to reduce image size
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]
# Post-install/Build shared packages if needed
RUN pnpm -r build --filter="./packages/*"

View File

@@ -0,0 +1,19 @@
FROM node:20-alpine
# Install essential production utilities
RUN apk add --no-cache curl libc6-compat
# Set standard production environment
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
WORKDIR /app
# Create non-root user for security
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
# Expose the default Next.js port
EXPOSE 3000

View File

@@ -1,6 +1,5 @@
services:
app:
image: registry.infra.mintel.me/mintel/${APP_NAME:-app}:${IMAGE_TAG:-latest}
restart: always
networks:
- infra
@@ -40,7 +39,6 @@ services:
gatekeeper:
image: registry.infra.mintel.me/mintel/gatekeeper:${IMAGE_TAG:-latest}
container_name: ${PROJECT_NAME}-gatekeeper
restart: always
networks:
- infra
@@ -55,7 +53,7 @@ services:
- "traefik.http.services.${PROJECT_NAME}-gatekeeper.loadbalancer.server.port=3000"
directus:
image: directus/directus:11
image: registry.infra.mintel.me/mintel/directus:latest
restart: always
networks:
- infra

View File

@@ -27,3 +27,8 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
# directus
/directus/uploads
/directus/extensions
/.env