chore: prepare first release 1.0.1
This commit is contained in:
73
README.md
73
README.md
@@ -1,77 +1,82 @@
|
||||
# Mintel Monorepo
|
||||
|
||||
This monorepo manages multiple client websites using a shared technology stack: Next.js, TypeScript, and React.
|
||||
This monorepo is the central "factory" for all Mintel client websites. It provides a standardized, versioned core of configurations, utilities, and infrastructure templates to ensure consistency, security, and rapid deployment across the entire portfolio.
|
||||
|
||||
## Project Structure
|
||||
## 🏗 Project Structure
|
||||
|
||||
- `apps/`: Client websites (e.g., `sample-website`).
|
||||
- `packages/`: Shared packages under the `@mintel` namespace.
|
||||
- `@mintel/tsconfig`: Shared TypeScript configurations.
|
||||
- `@mintel/eslint-config`: Shared ESLint configurations.
|
||||
- `@mintel/next-config`: Shared Next.js configuration wrapper.
|
||||
- `@mintel/next-utils`: Reusable logic (i18n, rate limiting, etc.).
|
||||
- `@mintel/infra`: Infrastructure templates (Docker, Gitea Actions).
|
||||
- `@mintel/cli`: CLI tool for project setup and migration.
|
||||
- **`apps/`**: Client website implementations (e.g., `sample-website`). These are consumers of the shared packages.
|
||||
- **`packages/`**: Shared, versioned npm packages under the `@mintel` namespace.
|
||||
- [`@mintel/tsconfig`](packages/tsconfig/README.md): Centralized TypeScript configurations.
|
||||
- [`@mintel/eslint-config`](packages/eslint-config/README.md): Shared linting rules and best practices.
|
||||
- [`@mintel/next-config`](packages/next-config/README.md): A powerful Next.js configuration wrapper with built-in i18n and Sentry support.
|
||||
- [`@mintel/next-utils`](packages/next-utils/README.md): Reusable logic for i18n, environment validation, and rate limiting.
|
||||
- [`@mintel/infra`](packages/infra/README.md): Production-ready Docker and Gitea Actions templates.
|
||||
- [`@mintel/cli`](packages/cli/README.md): Automation tool for scaffolding new projects.
|
||||
|
||||
## Getting Started
|
||||
## 🚀 Getting Started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- [pnpm](https://pnpm.io/) (v10+)
|
||||
- Node.js (v20+)
|
||||
- Access to the private registry: `https://npm.infra.mintel.me`
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
```
|
||||
|
||||
### Development
|
||||
|
||||
To run development mode for all shared packages:
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
### Building
|
||||
|
||||
To build all shared packages:
|
||||
```bash
|
||||
pnpm build
|
||||
```
|
||||
|
||||
## Creating a New Project
|
||||
## 🛠 Creating a New Client Project
|
||||
|
||||
Use the Mintel CLI to set up a new project or migrate an existing one:
|
||||
Never copy-paste files manually. Use the Mintel CLI to scaffold a new project with all best practices pre-configured:
|
||||
|
||||
```bash
|
||||
pnpm --filter @mintel/cli start init apps/my-new-website
|
||||
pnpm --filter @mintel/cli start init apps/client-name.com
|
||||
```
|
||||
|
||||
## Versioning and Releasing
|
||||
This command automatically:
|
||||
1. Sets up the directory structure.
|
||||
2. Links all `@mintel` shared packages.
|
||||
3. Configures `next-intl` (i18n) and Sentry.
|
||||
4. Injects Docker and Gitea Actions deployment workflows.
|
||||
|
||||
We use [Changesets](https://github.com/changesets/changesets) for version management.
|
||||
## 🔄 Release Cycle (Monorepo)
|
||||
|
||||
### 1. Add a changeset
|
||||
When you make a change that requires a version bump, run:
|
||||
We use [Changesets](https://github.com/changesets/changesets) to manage the versioning and publishing of shared packages.
|
||||
|
||||
### 1. Documenting Changes
|
||||
When you modify a package in `packages/*`, create a changeset:
|
||||
```bash
|
||||
pnpm changeset
|
||||
```
|
||||
Follow the prompts to select the package and the version bump type (patch, minor, major).
|
||||
|
||||
### 2. Version packages
|
||||
To bump versions based on accumulated changesets:
|
||||
### 2. Versioning
|
||||
When ready to release, bump the versions:
|
||||
```bash
|
||||
pnpm version-packages
|
||||
```
|
||||
This updates `package.json` files and generates `CHANGELOG.md` entries.
|
||||
|
||||
### 3. Publish to registry
|
||||
To build and publish all changed packages to the private registry:
|
||||
```bash
|
||||
pnpm release
|
||||
```
|
||||
### 3. Publishing
|
||||
The release to the private registry is automated via Gitea Actions. On every push to `main`, the `release.yml` workflow runs `pnpm release`. If new versions are detected, they are published to `https://npm.infra.mintel.me`.
|
||||
|
||||
## Deployment
|
||||
## 🌐 Infrastructure & Deployment
|
||||
|
||||
Projects are hosted on Hetzner with Docker and Traefik, deployed via Gitea Actions. See `@mintel/infra` for templates.
|
||||
Client websites scaffolded via the CLI use a **tag-based deployment** strategy:
|
||||
- **Push to `main`**: Deploys to the `testing` environment.
|
||||
- **Git Tag `v*.*.*-rc.*`**: Deploys to the `staging` environment.
|
||||
- **Git Tag `v*.*.*`**: Deploys to the `production` environment.
|
||||
|
||||
## Registry
|
||||
|
||||
Private npm registry: [https://npm.infra.mintel.me](https://npm.infra.mintel.me)
|
||||
See the [`@mintel/infra`](packages/infra/README.md) package for detailed template documentation.
|
||||
|
||||
8
apps/sample-website/CHANGELOG.md
Normal file
8
apps/sample-website/CHANGELOG.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# sample-website
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @mintel/next-utils@1.0.1
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sample-website",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
7
packages/cli/CHANGELOG.md
Normal file
7
packages/cli/CHANGELOG.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# @mintel/cli
|
||||
|
||||
## 1.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Initial release of the Mintel factory packages.
|
||||
@@ -1,26 +1,33 @@
|
||||
# @mintel/cli
|
||||
|
||||
CLI tool for managing the Mintel monorepo and scaffolding new client websites.
|
||||
The Mintel CLI is the primary automation tool for managing the monorepo and ensuring all client websites follow the same high-quality standards and infrastructure patterns.
|
||||
|
||||
## Installation
|
||||
## 🚀 Installation
|
||||
|
||||
The CLI is intended to be used within the monorepo:
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
```
|
||||
|
||||
## Commands
|
||||
## 🛠 Commands
|
||||
|
||||
### `init <path>`
|
||||
|
||||
Initializes a new website project in the specified path (relative to the monorepo root).
|
||||
Scaffolds a new, production-ready client website in the specified path.
|
||||
|
||||
```bash
|
||||
pnpm --filter @mintel/cli start init apps/my-new-website
|
||||
pnpm --filter @mintel/cli start init apps/my-new-website.com
|
||||
```
|
||||
|
||||
This command will:
|
||||
1. Create the project directory.
|
||||
2. Generate `package.json`, `tsconfig.json`, and `eslint.config.mjs` extending `@mintel` defaults.
|
||||
3. Set up a localized Next.js structure (`src/app/[locale]`).
|
||||
4. Configure `next-intl` middleware and request config.
|
||||
5. Inject production-ready `Dockerfile`, `docker-compose.yml`, and Gitea Actions deployment workflows.
|
||||
#### What it does:
|
||||
1. **Project Structure**: Creates a modern Next.js directory layout.
|
||||
2. **Shared Configs**: Generates `package.json`, `tsconfig.json`, and `eslint.config.mjs` that extend the `@mintel` shared packages.
|
||||
3. **Localization**: Sets up a localized routing structure (`src/app/[locale]`) with `next-intl` pre-configured.
|
||||
4. **Error Tracking**: Injects Sentry/GlitchTip instrumentation.
|
||||
5. **Environment Safety**: Adds a validation script (`scripts/validate-env.ts`) to catch missing secrets at build time.
|
||||
6. **Infrastructure**: Injects the universal `Dockerfile`, `docker-compose.yml`, and the tag-based Gitea Actions deployment workflow.
|
||||
|
||||
## 🛠 Development
|
||||
|
||||
To add new features to the scaffold (e.g., new shared files or config templates), modify `packages/cli/src/index.ts`.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/cli",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
|
||||
@@ -99,6 +99,21 @@ export default nextConfig;
|
||||
eslintConfig
|
||||
);
|
||||
|
||||
// Create env validation script
|
||||
await fs.ensureDir(path.join(fullPath, "scripts"));
|
||||
await fs.writeFile(
|
||||
path.join(fullPath, "scripts/validate-env.ts"),
|
||||
`import { validateMintelEnv } from "@mintel/next-utils";
|
||||
|
||||
try {
|
||||
validateMintelEnv();
|
||||
console.log("✅ Environment variables validated");
|
||||
} catch (error) {
|
||||
process.exit(1);
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
// Create basic src structure
|
||||
await fs.ensureDir(path.join(fullPath, "src/app/[locale]"));
|
||||
await fs.writeFile(
|
||||
@@ -186,10 +201,14 @@ export default function RootLayout({
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(fullPath, "src/app/[locale]/page.tsx"),
|
||||
`export default function Home() {
|
||||
`import { useTranslations } from 'next-intl';
|
||||
|
||||
export default function Home() {
|
||||
const t = useTranslations('Index');
|
||||
|
||||
return (
|
||||
<main>
|
||||
<h1>Welcome to ${projectName}</h1>
|
||||
<h1>{t('title')} to ${projectName}</h1>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
7
packages/eslint-config/CHANGELOG.md
Normal file
7
packages/eslint-config/CHANGELOG.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# @mintel/eslint-config
|
||||
|
||||
## 1.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Initial release of the Mintel factory packages.
|
||||
@@ -1,13 +1,28 @@
|
||||
# @mintel/eslint-config
|
||||
|
||||
Shared ESLint configurations for Mintel projects.
|
||||
Shared ESLint configurations for Mintel projects, enforcing code quality and consistent style across Next.js and TypeScript codebases.
|
||||
|
||||
## Usage
|
||||
## 📦 Configurations
|
||||
|
||||
### `next`
|
||||
A comprehensive configuration for Next.js projects.
|
||||
- **Extends**: `next/core-web-vitals` and `next/typescript`.
|
||||
- **Custom Rules**:
|
||||
- `_` prefix for unused variables is allowed.
|
||||
- `any` type is permitted (for rapid migration/prototyping).
|
||||
- React unescaped entities check is disabled.
|
||||
- Image element warnings are enabled (prefer `next/image`).
|
||||
|
||||
## 🚀 Usage
|
||||
|
||||
### In a Next.js App
|
||||
Create an `eslint.config.mjs` in your project root:
|
||||
|
||||
### Next.js
|
||||
In your `eslint.config.mjs`:
|
||||
```javascript
|
||||
import { nextConfig } from "@mintel/eslint-config/next";
|
||||
|
||||
export default nextConfig;
|
||||
```
|
||||
|
||||
## 🛠 Development
|
||||
To add new rules, modify `packages/eslint-config/next.js`. Remember to create a changeset if you make breaking changes.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/eslint-config",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
|
||||
7
packages/infra/CHANGELOG.md
Normal file
7
packages/infra/CHANGELOG.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# @mintel/infra
|
||||
|
||||
## 1.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Initial release of the Mintel factory packages.
|
||||
@@ -1,12 +1,20 @@
|
||||
# @mintel/infra
|
||||
|
||||
Infrastructure templates for Mintel projects.
|
||||
Production-ready infrastructure templates for Mintel client websites, optimized for Hetzner, Traefik, and Gitea Actions.
|
||||
|
||||
## Contents
|
||||
## 📦 Contents
|
||||
|
||||
- `docker/`: Universal `Dockerfile.nextjs` and `docker-compose.template.yml`.
|
||||
- `gitea/`: Production-ready `deploy-action.yml` for Gitea Actions.
|
||||
### `docker/`
|
||||
- **`Dockerfile.nextjs`**: A multi-stage build optimized for Next.js standalone output. Supports build-time ARGs for Umami and Base URL.
|
||||
- **`docker-compose.template.yml`**: A Traefik-ready compose file with automatic HTTPS redirection, security headers, and network isolation.
|
||||
|
||||
## Usage
|
||||
### `gitea/`
|
||||
- **`deploy-action.yml`**: A high-transparency deployment workflow.
|
||||
- **Testing**: Automatic deploy on push to `main`.
|
||||
- **Staging**: Deploy via `v*-rc.*` tags.
|
||||
- **Production**: Deploy via `v*.*.*` tags.
|
||||
- **Features**: Gotify notifications, health checks, and automatic `.env` generation.
|
||||
|
||||
These files are automatically injected into new projects by the `@mintel/cli`.
|
||||
## 🚀 Usage
|
||||
|
||||
These templates are automatically injected into new projects by the [`@mintel/cli`](../cli/README.md). If you need to update an existing project, you can manually copy them from this package.
|
||||
|
||||
@@ -2,101 +2,162 @@ name: Build & Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, staging]
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: docker
|
||||
|
||||
steps:
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# LOGGING: Workflow Start - Full Transparency
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
- name: 📋 Log Workflow Start
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# Workflow Start & Basic Info
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
- name: 📢 Workflow Start
|
||||
run: |
|
||||
echo "🚀 Starting deployment for ${{ github.repository }} (${{ github.ref }})"
|
||||
echo " • Branch: ${{ github.ref_name }}"
|
||||
echo " • Commit: ${{ github.sha }}"
|
||||
echo " • Timestamp: $(date -u +'%Y-%m-%d %H:%M:%S UTC')"
|
||||
echo "┌──────────────────────────────────────────────────────────────┐"
|
||||
echo "│ 🚀 Deployment Workflow gestartet │"
|
||||
echo "├──────────────────────────────────────────────────────────────┤"
|
||||
echo "│ Repository: ${{ github.repository }} │"
|
||||
echo "│ Ref: ${{ github.ref }} │"
|
||||
echo "│ Ref-Name: ${{ github.ref_name }} │"
|
||||
echo "│ Commit: ${{ github.sha }} │"
|
||||
echo "│ Actor: ${{ github.actor }} │"
|
||||
echo "│ Datum: $(date -u +'%Y-%m-%d %H:%M:%S UTC') │"
|
||||
echo "└──────────────────────────────────────────────────────────────┘"
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# LOGGING: Registry Login Phase
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
- name: 🔐 Login to private registry
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# Environment bestimmen + Commit-Message holen
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
- name: 🔍 Environment & Version ermitteln
|
||||
id: determine
|
||||
run: |
|
||||
echo "🔐 Authenticating with registry.infra.mintel.me..."
|
||||
TAG="${{ github.ref_name }}"
|
||||
SHORT_SHA="${{ github.sha }}"
|
||||
SHORT_SHA="${SHORT_SHA:0:9}"
|
||||
|
||||
# Get base domain from secret or env if possible, otherwise placeholder
|
||||
# In a real project, you'd likely have a primary domain secret
|
||||
DOMAIN_BASE=$(echo "${{ secrets.NEXT_PUBLIC_BASE_URL }}" | sed -E 's|https?://||' | sed -E 's|/.*||')
|
||||
|
||||
# Commit-Message holen (erste Zeile)
|
||||
COMMIT_MSG=$(git log -1 --pretty=%s || echo "No commit message available")
|
||||
|
||||
if [[ "${{ github.ref_type }}" == "branch" && "$TAG" == "main" ]]; then
|
||||
TARGET="testing"
|
||||
IMAGE_TAG="main-${SHORT_SHA}"
|
||||
ENV_FILE=".env.testing"
|
||||
TRAEFIK_HOST="\`testing.${DOMAIN_BASE}\`"
|
||||
IS_PROD="false"
|
||||
GOTIFY_TITLE="🧪 Testing-Deploy"
|
||||
GOTIFY_PRIORITY=4
|
||||
elif [[ "${{ github.ref_type }}" == "tag" ]]; then
|
||||
if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
TARGET="production"
|
||||
IMAGE_TAG="$TAG"
|
||||
ENV_FILE=".env.prod"
|
||||
TRAEFIK_HOST="\`${DOMAIN_BASE}\`, \`www.${DOMAIN_BASE}\`"
|
||||
IS_PROD="true"
|
||||
GOTIFY_TITLE="🚀 Production-Release"
|
||||
GOTIFY_PRIORITY=6
|
||||
elif [[ "$TAG" =~ -rc\. || "$TAG" =~ -beta\. || "$TAG" =~ -alpha\. ]]; then
|
||||
TARGET="staging"
|
||||
IMAGE_TAG="$TAG"
|
||||
ENV_FILE=".env.staging"
|
||||
TRAEFIK_HOST="\`staging.${DOMAIN_BASE}\`"
|
||||
IS_PROD="false"
|
||||
GOTIFY_TITLE="🧪 Staging-Deploy (Pre-Release)"
|
||||
GOTIFY_PRIORITY=5
|
||||
else
|
||||
TARGET="skip"
|
||||
GOTIFY_TITLE="❓ Unbekannter Tag"
|
||||
GOTIFY_PRIORITY=3
|
||||
fi
|
||||
else
|
||||
TARGET="skip"
|
||||
fi
|
||||
|
||||
echo "target=$TARGET" >> $GITHUB_OUTPUT
|
||||
echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
|
||||
echo "env_file=$ENV_FILE" >> $GITHUB_OUTPUT
|
||||
echo "traefik_host=$TRAEFIK_HOST" >> $GITHUB_OUTPUT
|
||||
echo "is_prod=$IS_PROD" >> $GITHUB_OUTPUT
|
||||
echo "gotify_title=$GOTIFY_TITLE" >> $GITHUB_OUTPUT
|
||||
echo "gotify_priority=$GOTIFY_PRIORITY" >> $GITHUB_OUTPUT
|
||||
echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT
|
||||
echo "commit_msg=$COMMIT_MSG" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: ⏭️ Skip Deployment
|
||||
if: steps.determine.outputs.target == 'skip'
|
||||
run: |
|
||||
echo "Deployment übersprungen – kein passender Trigger (main oder v*-Tag)"
|
||||
exit 0
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# Registry Login
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
- name: 🔐 Registry Login
|
||||
run: |
|
||||
echo "🔐 Login zu registry.infra.mintel.me ..."
|
||||
echo "${{ secrets.REGISTRY_PASS }}" | docker login registry.infra.mintel.me -u "${{ secrets.REGISTRY_USER }}" --password-stdin
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# LOGGING: Build Phase
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
- name: 🏗️ Build Docker image
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# Build & Push
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
- name: 🏗️ Docker Image bauen & pushen
|
||||
env:
|
||||
NEXT_PUBLIC_BASE_URL: ${{ github.ref_name == 'main' && secrets.NEXT_PUBLIC_BASE_URL || (secrets.STAGING_NEXT_PUBLIC_BASE_URL || secrets.NEXT_PUBLIC_BASE_URL) }}
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${{ github.ref_name == 'main' && secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID || (secrets.STAGING_NEXT_PUBLIC_UMAMI_WEBSITE_ID || secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID) }}
|
||||
NEXT_PUBLIC_UMAMI_SCRIPT_URL: ${{ github.ref_name == 'main' && secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL || (secrets.STAGING_NEXT_PUBLIC_UMAMI_SCRIPT_URL || secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL) }}
|
||||
IMAGE_TAG: ${{ steps.determine.outputs.image_tag }}
|
||||
NEXT_PUBLIC_BASE_URL: ${{ steps.determine.outputs.target == 'production' && secrets.NEXT_PUBLIC_BASE_URL || (steps.determine.outputs.target == 'staging' && secrets.STAGING_NEXT_PUBLIC_BASE_URL || secrets.TESTING_NEXT_PUBLIC_BASE_URL || secrets.NEXT_PUBLIC_BASE_URL) }}
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${{ steps.determine.outputs.target == 'production' && secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID || (steps.determine.outputs.target == 'staging' && secrets.STAGING_NEXT_PUBLIC_UMAMI_WEBSITE_ID || secrets.TESTING_NEXT_PUBLIC_UMAMI_WEBSITE_ID || secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID) }}
|
||||
NEXT_PUBLIC_UMAMI_SCRIPT_URL: ${{ steps.determine.outputs.target == 'production' && secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL || (steps.determine.outputs.target == 'staging' && secrets.STAGING_NEXT_PUBLIC_UMAMI_SCRIPT_URL || secrets.TESTING_NEXT_PUBLIC_UMAMI_SCRIPT_URL || secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL) }}
|
||||
run: |
|
||||
echo "🏗️ Building Docker image (linux/arm64) for branch ${{ github.ref_name }}..."
|
||||
echo "🏗️ Building → ${{ steps.determine.outputs.target }} / $IMAGE_TAG"
|
||||
docker buildx build \
|
||||
--pull \
|
||||
--platform linux/arm64 \
|
||||
--build-arg NEXT_PUBLIC_BASE_URL="$NEXT_PUBLIC_BASE_URL" \
|
||||
--build-arg NEXT_PUBLIC_UMAMI_WEBSITE_ID="$NEXT_PUBLIC_UMAMI_WEBSITE_ID" \
|
||||
--build-arg NEXT_PUBLIC_UMAMI_SCRIPT_URL="$NEXT_PUBLIC_UMAMI_SCRIPT_URL" \
|
||||
-t registry.infra.mintel.me/${{ github.repository }}:${{ github.sha }} \
|
||||
-t registry.infra.mintel.me/${{ github.repository }}:latest \
|
||||
-t registry.infra.mintel.me/${{ github.repository }}:$IMAGE_TAG \
|
||||
--push .
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# LOGGING: Deployment Phase
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
- name: 🚀 Deploy to server
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# Deploy via SSH
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
- name: 🚀 Deploy to ${{ steps.determine.outputs.target }}
|
||||
env:
|
||||
NEXT_PUBLIC_BASE_URL: ${{ github.ref_name == 'main' && secrets.NEXT_PUBLIC_BASE_URL || (secrets.STAGING_NEXT_PUBLIC_BASE_URL || secrets.NEXT_PUBLIC_BASE_URL) }}
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${{ github.ref_name == 'main' && secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID || (secrets.STAGING_NEXT_PUBLIC_UMAMI_WEBSITE_ID || secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID) }}
|
||||
NEXT_PUBLIC_UMAMI_SCRIPT_URL: ${{ github.ref_name == 'main' && secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL || (secrets.STAGING_NEXT_PUBLIC_UMAMI_SCRIPT_URL || secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL) }}
|
||||
SENTRY_DSN: ${{ github.ref_name == 'main' && secrets.SENTRY_DSN || (secrets.STAGING_SENTRY_DSN || secrets.SENTRY_DSN) }}
|
||||
MAIL_HOST: ${{ github.ref_name == 'main' && secrets.MAIL_HOST || (secrets.STAGING_MAIL_HOST || secrets.MAIL_HOST) }}
|
||||
MAIL_PORT: ${{ github.ref_name == 'main' && secrets.MAIL_PORT || (secrets.STAGING_MAIL_PORT || secrets.MAIL_PORT) }}
|
||||
MAIL_USERNAME: ${{ github.ref_name == 'main' && secrets.MAIL_USERNAME || (secrets.STAGING_MAIL_USERNAME || secrets.MAIL_USERNAME) }}
|
||||
MAIL_PASSWORD: ${{ github.ref_name == 'main' && secrets.MAIL_PASSWORD || (secrets.STAGING_MAIL_PASSWORD || secrets.MAIL_PASSWORD) }}
|
||||
MAIL_FROM: ${{ github.ref_name == 'main' && secrets.MAIL_FROM || (secrets.STAGING_MAIL_FROM || secrets.MAIL_FROM) }}
|
||||
MAIL_RECIPIENTS: ${{ github.ref_name == 'main' && secrets.MAIL_RECIPIENTS || (secrets.STAGING_MAIL_RECIPIENTS || secrets.MAIL_RECIPIENTS) }}
|
||||
IMAGE_TAG: ${{ steps.determine.outputs.image_tag }}
|
||||
ENV_FILE: ${{ steps.determine.outputs.env_file }}
|
||||
TRAEFIK_HOST: ${{ steps.determine.outputs.traefik_host }}
|
||||
NEXT_PUBLIC_BASE_URL: ${{ steps.determine.outputs.target == 'production' && secrets.NEXT_PUBLIC_BASE_URL || (steps.determine.outputs.target == 'staging' && secrets.STAGING_NEXT_PUBLIC_BASE_URL || secrets.TESTING_NEXT_PUBLIC_BASE_URL || secrets.NEXT_PUBLIC_BASE_URL) }}
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${{ steps.determine.outputs.target == 'production' && secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID || (steps.determine.outputs.target == 'staging' && secrets.STAGING_NEXT_PUBLIC_UMAMI_WEBSITE_ID || secrets.TESTING_NEXT_PUBLIC_UMAMI_WEBSITE_ID || secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID) }}
|
||||
NEXT_PUBLIC_UMAMI_SCRIPT_URL: ${{ steps.determine.outputs.target == 'production' && secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL || (steps.determine.outputs.target == 'staging' && secrets.STAGING_NEXT_PUBLIC_UMAMI_SCRIPT_URL || secrets.TESTING_NEXT_PUBLIC_UMAMI_SCRIPT_URL || secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL) }}
|
||||
SENTRY_DSN: ${{ steps.determine.outputs.target == 'production' && secrets.SENTRY_DSN || (steps.determine.outputs.target == 'staging' && secrets.STAGING_SENTRY_DSN || secrets.TESTING_SENTRY_DSN || secrets.SENTRY_DSN) }}
|
||||
MAIL_HOST: ${{ steps.determine.outputs.target == 'production' && secrets.MAIL_HOST || (steps.determine.outputs.target == 'staging' && secrets.STAGING_MAIL_HOST || secrets.TESTING_MAIL_HOST || secrets.MAIL_HOST) }}
|
||||
MAIL_PORT: ${{ steps.determine.outputs.target == 'production' && secrets.MAIL_PORT || (steps.determine.outputs.target == 'staging' && secrets.STAGING_MAIL_PORT || secrets.TESTING_MAIL_PORT || secrets.MAIL_PORT) }}
|
||||
MAIL_USERNAME: ${{ steps.determine.outputs.target == 'production' && secrets.MAIL_USERNAME || (steps.determine.outputs.target == 'staging' && secrets.STAGING_MAIL_USERNAME || secrets.TESTING_MAIL_USERNAME || secrets.MAIL_USERNAME) }}
|
||||
MAIL_PASSWORD: ${{ steps.determine.outputs.target == 'production' && secrets.MAIL_PASSWORD || (steps.determine.outputs.target == 'staging' && secrets.STAGING_MAIL_PASSWORD || secrets.TESTING_MAIL_PASSWORD || secrets.MAIL_PASSWORD) }}
|
||||
MAIL_FROM: ${{ steps.determine.outputs.target == 'production' && secrets.MAIL_FROM || (steps.determine.outputs.target == 'staging' && secrets.STAGING_MAIL_FROM || secrets.TESTING_MAIL_FROM || secrets.MAIL_FROM) }}
|
||||
MAIL_RECIPIENTS: ${{ steps.determine.outputs.target == 'production' && secrets.MAIL_RECIPIENTS || (steps.determine.outputs.target == 'staging' && secrets.STAGING_MAIL_RECIPIENTS || secrets.TESTING_MAIL_RECIPIENTS || secrets.MAIL_RECIPIENTS) }}
|
||||
run: |
|
||||
BRANCH=${{ github.ref_name }}
|
||||
|
||||
# Derive domain from NEXT_PUBLIC_BASE_URL (strip https:// and trailing slash)
|
||||
DOMAIN=$(echo "$NEXT_PUBLIC_BASE_URL" | sed -E 's|https?://||' | sed -E 's|/.*||')
|
||||
|
||||
if [ "$BRANCH" = "main" ]; then
|
||||
ENV_FILE=.env.prod
|
||||
# For production, we want both root and www
|
||||
TRAEFIK_HOST="\`$DOMAIN\`, \`www.$DOMAIN\`"
|
||||
else
|
||||
ENV_FILE=.env.staging
|
||||
TRAEFIK_HOST="\`$DOMAIN\`"
|
||||
fi
|
||||
|
||||
echo "🚀 Deploying branch $BRANCH to $ENV_FILE..."
|
||||
echo "🌐 Domain: $DOMAIN"
|
||||
|
||||
# Setup SSH
|
||||
echo "Deploying ${{ steps.determine.outputs.target }} → $IMAGE_TAG"
|
||||
|
||||
# SSH vorbereiten
|
||||
mkdir -p ~/.ssh
|
||||
echo "${{ secrets.SSH_KEY }}" > ~/.ssh/id_ed25519
|
||||
chmod 600 ~/.ssh/id_ed25519
|
||||
ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
|
||||
|
||||
# Create .env file content
|
||||
|
||||
# .env-Datei erstellen
|
||||
cat > /tmp/app.env << EOF
|
||||
# ============================================================================
|
||||
# Environment Configuration ($BRANCH)
|
||||
# ============================================================================
|
||||
# Auto-generated by CI/CD workflow
|
||||
# ============================================================================
|
||||
|
||||
# Generated by CI - ${{ steps.determine.outputs.target }} - $(date -u)
|
||||
NODE_ENV=production
|
||||
NEXT_PUBLIC_BASE_URL=$NEXT_PUBLIC_BASE_URL
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID=$NEXT_PUBLIC_UMAMI_WEBSITE_ID
|
||||
@@ -108,90 +169,80 @@ jobs:
|
||||
MAIL_PASSWORD=$MAIL_PASSWORD
|
||||
MAIL_FROM=$MAIL_FROM
|
||||
MAIL_RECIPIENTS=$MAIL_RECIPIENTS
|
||||
|
||||
# Deployment variables for docker-compose
|
||||
IMAGE_TAG=${{ github.sha }}
|
||||
|
||||
IMAGE_TAG=$IMAGE_TAG
|
||||
TRAEFIK_HOST=$TRAEFIK_HOST
|
||||
ENV_FILE=$ENV_FILE
|
||||
EOF
|
||||
|
||||
|
||||
APP_DIR="/home/deploy/sites/${{ github.event.repository.name }}"
|
||||
ssh -o StrictHostKeyChecking=accept-new root@${{ secrets.SSH_HOST }} "mkdir -p $APP_DIR"
|
||||
scp -o StrictHostKeyChecking=accept-new /tmp/app.env root@${{ secrets.SSH_HOST }}:$APP_DIR/$ENV_FILE
|
||||
scp -o StrictHostKeyChecking=accept-new docker-compose.yml root@${{ secrets.SSH_HOST }}:$APP_DIR/docker-compose.yml
|
||||
|
||||
ssh -o StrictHostKeyChecking=accept-new root@${{ secrets.SSH_HOST }} bash << EOF
|
||||
set -e
|
||||
cd $APP_DIR
|
||||
|
||||
chmod 600 $ENV_FILE
|
||||
chown deploy:deploy $ENV_FILE
|
||||
|
||||
echo "${{ secrets.REGISTRY_PASS }}" | docker login registry.infra.mintel.me -u "${{ secrets.REGISTRY_USER }}" --password-stdin
|
||||
|
||||
echo "📥 Pulling images..."
|
||||
IMAGE_TAG=${{ github.sha }} ENV_FILE=$ENV_FILE TRAEFIK_HOST="$TRAEFIK_HOST" docker compose --env-file $ENV_FILE pull
|
||||
|
||||
echo "🚀 Starting containers..."
|
||||
IMAGE_TAG=${{ github.sha }} ENV_FILE=$ENV_FILE TRAEFIK_HOST="$TRAEFIK_HOST" docker compose --env-file $ENV_FILE up -d
|
||||
|
||||
echo "🧹 Cleaning up old images..."
|
||||
docker system prune -f
|
||||
|
||||
echo "⏳ Giving the app a few seconds to warm up..."
|
||||
sleep 10
|
||||
|
||||
echo "🔍 Checking container status..."
|
||||
docker compose --env-file $ENV_FILE ps
|
||||
|
||||
if ! docker compose --env-file $ENV_FILE ps | grep -q "Up"; then
|
||||
echo "❌ Container failed to start"
|
||||
docker compose --env-file $ENV_FILE logs --tail=100
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Deployment complete!"
|
||||
|
||||
ssh -o StrictHostKeyChecking=accept-new root@${{ secrets.SSH_HOST }} bash << 'EOF'
|
||||
set -e
|
||||
APP_DIR="/home/deploy/sites/${{ github.event.repository.name }}"
|
||||
cd $APP_DIR
|
||||
|
||||
chmod 600 $ENV_FILE
|
||||
chown deploy:deploy $ENV_FILE
|
||||
|
||||
echo "${{ secrets.REGISTRY_PASS }}" | docker login registry.infra.mintel.me -u "${{ secrets.REGISTRY_USER }}" --password-stdin
|
||||
|
||||
echo "→ Pulling image: $IMAGE_TAG"
|
||||
IMAGE_TAG=$IMAGE_TAG ENV_FILE=$ENV_FILE TRAEFIK_HOST="$TRAEFIK_HOST" docker compose --env-file $ENV_FILE pull
|
||||
|
||||
echo "→ Starting containers..."
|
||||
IMAGE_TAG=$IMAGE_TAG ENV_FILE=$ENV_FILE TRAEFIK_HOST="$TRAEFIK_HOST" docker compose --env-file $ENV_FILE up -d
|
||||
|
||||
docker system prune -f --filter "until=168h"
|
||||
|
||||
echo "→ Waiting 15s for warmup..."
|
||||
sleep 15
|
||||
|
||||
echo "→ Container status:"
|
||||
docker compose --env-file $ENV_FILE ps
|
||||
|
||||
if ! docker compose --env-file $ENV_FILE ps | grep -q "Up"; then
|
||||
echo "❌ Fehler: Container nicht Up!"
|
||||
docker compose --env-file $ENV_FILE logs --tail=150
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Deployment erfolgreich auf ${{ steps.determine.outputs.target }}!"
|
||||
EOF
|
||||
|
||||
|
||||
rm -f /tmp/app.env
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# LOGGING: Workflow Summary
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
- name: 📊 Workflow Summary
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# Summary & Gotify
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
- name: 📊 Deployment Summary
|
||||
if: always()
|
||||
run: |
|
||||
echo "📊 Status: ${{ job.status }}"
|
||||
echo "🎯 Target: ${{ secrets.SSH_HOST }}"
|
||||
echo "🌿 Branch: ${{ github.ref_name }}"
|
||||
echo "┌──────────────────────────────┐"
|
||||
echo "│ Deployment Summary │"
|
||||
echo "├──────────────────────────────┤"
|
||||
echo "│ Status: ${{ job.status }} │"
|
||||
echo "│ Umgebung: ${{ steps.determine.outputs.target || 'skipped' }} │"
|
||||
echo "│ Version: ${{ steps.determine.outputs.image_tag }} │"
|
||||
echo "│ Commit: ${{ steps.determine.outputs.short_sha }} │"
|
||||
echo "│ Message: ${{ steps.determine.outputs.commit_msg }} │"
|
||||
echo "└──────────────────────────────┘"
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# NOTIFICATION: Gotify
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
- name: 🔔 Gotify Notification (Success)
|
||||
- name: 🔔 Gotify - Success
|
||||
if: success()
|
||||
run: |
|
||||
echo "Sending success notification to Gotify..."
|
||||
curl -k -s -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \
|
||||
-F "title=✅ Deployment Success: ${{ github.repository }}" \
|
||||
-F "message=The deployment of ${{ github.repository }} (branch: ${{ github.ref_name }}) was successful.
|
||||
|
||||
Commit: ${{ github.sha }}
|
||||
Actor: ${{ github.actor }}
|
||||
Run ID: ${{ github.run_id }}" \
|
||||
-F "priority=5"
|
||||
curl -s -k -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \
|
||||
-F "title=${{ steps.determine.outputs.gotify_title }}" \
|
||||
-F "message=Erfolgreich deployt auf **${{ steps.determine.outputs.target }}**\n\nVersion: **${{ steps.determine.outputs.image_tag }}**\nCommit: ${{ steps.determine.outputs.short_sha }} (${{ steps.determine.outputs.commit_msg }})\nVon: ${{ github.actor }}\nRun: ${{ github.run_id }}" \
|
||||
-F "priority=${{ steps.determine.outputs.gotify_priority }}" || true
|
||||
|
||||
- name: 🔔 Gotify Notification (Failure)
|
||||
- name: 🔔 Gotify - Failure
|
||||
if: failure()
|
||||
run: |
|
||||
echo "Sending failure notification to Gotify..."
|
||||
curl -k -s -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \
|
||||
-F "title=❌ Deployment Failed: ${{ github.repository }}" \
|
||||
-F "message=The deployment of ${{ github.repository }} (branch: ${{ github.ref_name }}) failed!
|
||||
|
||||
Commit: ${{ github.sha }}
|
||||
Actor: ${{ github.actor }}
|
||||
Run ID: ${{ github.run_id }}
|
||||
|
||||
Please check the logs for details." \
|
||||
-F "priority=8"
|
||||
curl -s -k -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \
|
||||
-F "title=❌ Deployment FEHLGESCHLAGEN – ${{ steps.determine.outputs.target || 'unknown' }}" \
|
||||
-F "message=**Fehler beim Deploy auf ${{ steps.determine.outputs.target }}**\n\nVersion: ${{ steps.determine.outputs.image_tag || '?' }}\nCommit: ${{ steps.determine.outputs.short_sha || '?' }}\nVon: ${{ github.actor }}\nRun: ${{ github.run_id }}\n\nBitte Logs prüfen!" \
|
||||
-F "priority=8" || true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/infra",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
|
||||
7
packages/next-config/CHANGELOG.md
Normal file
7
packages/next-config/CHANGELOG.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# @mintel/next-config
|
||||
|
||||
## 1.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Initial release of the Mintel factory packages.
|
||||
@@ -1,16 +1,31 @@
|
||||
# @mintel/next-config
|
||||
|
||||
Shared Next.js configuration wrapper for Mintel projects. Integrates `next-intl` and Sentry by default.
|
||||
A powerful Next.js configuration wrapper that standardizes internationalization, error tracking, and security across all Mintel client websites.
|
||||
|
||||
## Usage
|
||||
## ✨ Features
|
||||
|
||||
- **`next-intl` Integration**: Automatically wraps your config with the internationalization plugin.
|
||||
- **Sentry/GlitchTip**: Pre-configured error tracking with treeshaking and silent CI builds.
|
||||
- **Standalone Output**: Optimized for Docker deployments by default.
|
||||
- **Security Headers**: Strict Content Security Policy (CSP) and SVG safety.
|
||||
- **Analytics Proxy**: Built-in rewrites for Umami analytics (`/stats/*`) and GlitchTip (`/errors/*`).
|
||||
|
||||
## 🚀 Usage
|
||||
|
||||
In your `next.config.ts`:
|
||||
|
||||
```typescript
|
||||
import mintelNextConfig from "@mintel/next-config";
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
// Your project specific config
|
||||
// Your project specific config (redirects, etc.)
|
||||
};
|
||||
|
||||
export default mintelNextConfig(nextConfig);
|
||||
```
|
||||
|
||||
## 🌐 Environment Variables
|
||||
The following variables are used by this config:
|
||||
- `NEXT_PUBLIC_UMAMI_SCRIPT_URL`: URL to your Umami instance.
|
||||
- `SENTRY_DSN`: Your GlitchTip/Sentry DSN.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/next-config",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
|
||||
7
packages/next-utils/CHANGELOG.md
Normal file
7
packages/next-utils/CHANGELOG.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# @mintel/next-utils
|
||||
|
||||
## 1.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Initial release of the Mintel factory packages.
|
||||
@@ -1,28 +1,47 @@
|
||||
# @mintel/next-utils
|
||||
|
||||
Reusable utilities for Mintel Next.js projects.
|
||||
A collection of reusable utilities and helpers for Mintel Next.js projects, focusing on internationalization, environment safety, and security.
|
||||
|
||||
## Features
|
||||
## ✨ Features
|
||||
|
||||
- **i18n**: Standardized middleware and request configuration for `next-intl`.
|
||||
- **Env Validation**: Zod-based environment variable validation.
|
||||
- **Rate Limiting**: Simple in-memory rate limiting for server actions.
|
||||
### 🌍 Internationalization (i18n)
|
||||
Standardized helpers for `next-intl`:
|
||||
- `createMintelMiddleware`: A logging-enabled middleware wrapper.
|
||||
- `createMintelI18nRequestConfig`: Centralized request configuration for server-side translations.
|
||||
|
||||
## Usage
|
||||
### 🔐 Environment Validation
|
||||
Zod-based validation to ensure your app never boots with missing secrets:
|
||||
- `validateMintelEnv`: Validates standard Mintel variables (Mail, Sentry, Umami).
|
||||
|
||||
### i18n Middleware
|
||||
### 🛡 Rate Limiting
|
||||
- `rateLimit`: A simple in-memory rate limiter for protecting server actions and form submissions.
|
||||
|
||||
## 🚀 Usage
|
||||
|
||||
### i18n Middleware (`src/middleware.ts`)
|
||||
```typescript
|
||||
import { createMintelMiddleware } from "@mintel/next-utils";
|
||||
|
||||
export default createMintelMiddleware({
|
||||
locales: ["en", "de"],
|
||||
defaultLocale: "en",
|
||||
logRequests: true,
|
||||
});
|
||||
```
|
||||
|
||||
### Env Validation
|
||||
### Env Validation (`scripts/validate-env.ts`)
|
||||
```typescript
|
||||
import { validateMintelEnv } from "@mintel/next-utils";
|
||||
|
||||
const env = validateMintelEnv();
|
||||
validateMintelEnv();
|
||||
```
|
||||
|
||||
### Rate Limiting
|
||||
```typescript
|
||||
import { rateLimit } from "@mintel/next-utils";
|
||||
|
||||
export async function myAction(data: any) {
|
||||
await rateLimit(data.email);
|
||||
// ... logic
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/next-utils",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
|
||||
7
packages/tsconfig/CHANGELOG.md
Normal file
7
packages/tsconfig/CHANGELOG.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# @mintel/tsconfig
|
||||
|
||||
## 1.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Initial release of the Mintel factory packages.
|
||||
@@ -1,21 +1,47 @@
|
||||
# @mintel/tsconfig
|
||||
|
||||
Shared TypeScript configurations for Mintel projects.
|
||||
Centralized TypeScript configurations for all Mintel projects, ensuring consistent compiler settings and modern target environments.
|
||||
|
||||
## Usage
|
||||
## 📦 Configurations
|
||||
|
||||
### Base Configuration
|
||||
In your `tsconfig.json`:
|
||||
### `base.json`
|
||||
The foundation for all TypeScript projects in the monorepo.
|
||||
- **Target**: `ES2020`
|
||||
- **Module Resolution**: `bundler`
|
||||
- **Strictness**: `strict: false` (aligned with `klz-2026` standards)
|
||||
- **Features**: Enables `esModuleInterop`, `resolveJsonModule`, and `isolatedModules`.
|
||||
|
||||
### `nextjs.json`
|
||||
Extends `base.json` with specific settings for Next.js applications.
|
||||
- **Plugins**: Includes the `next` TypeScript plugin for enhanced IDE support.
|
||||
- **JSX**: Set to `preserve`.
|
||||
|
||||
## 🚀 Usage
|
||||
|
||||
### In a Next.js App
|
||||
Create a `tsconfig.json` in your project root:
|
||||
```json
|
||||
{
|
||||
"extends": "@mintel/tsconfig/base.json"
|
||||
"extends": "@mintel/tsconfig/nextjs.json",
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
```
|
||||
|
||||
### Next.js Configuration
|
||||
In your `tsconfig.json`:
|
||||
### In a Library Package
|
||||
Create a `tsconfig.json` in your package root:
|
||||
```json
|
||||
{
|
||||
"extends": "@mintel/tsconfig/nextjs.json"
|
||||
"extends": "@mintel/tsconfig/base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/tsconfig",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
|
||||
Reference in New Issue
Block a user