Compare commits

...

14 Commits

Author SHA1 Message Date
fcdfdb4588 chore: release next-config v1.6.1
All checks were successful
Monorepo Pipeline / 🧪 Quality Assurance (push) Successful in 2m12s
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Successful in 18s
Monorepo Pipeline / 🚀 Release (push) Successful in 2m17s
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Successful in 2m13s
Monorepo Pipeline / 🐳 Build Production Runtime (push) Successful in 17s
Monorepo Pipeline / 🐳 Build Build-Base (push) Successful in 5m27s
2026-02-10 00:27:59 +01:00
6bbaa8d105 chore: cms sync 2026-02-10 00:26:13 +01:00
eccc084441 chore: cms sync commands 2026-02-10 00:13:42 +01:00
da6b8aba64 fix: cms sync
All checks were successful
Monorepo Pipeline / 🧪 Quality Assurance (push) Successful in 2m43s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-10 00:03:27 +01:00
290097b4e6 chore: fix linter
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Has been cancelled
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been cancelled
2026-02-10 00:02:26 +01:00
45894cce34 chore: fix linter
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Failing after 57s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-09 23:59:22 +01:00
7195906da0 chore: fix linter
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Failing after 42s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-09 23:54:58 +01:00
dcb466f53b chore: fix husky
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Failing after 1m3s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-09 23:44:34 +01:00
14089766ea feat: infra cms
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Failing after 1m5s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-09 23:33:45 +01:00
6ecabe4a04 chore: sync lock file
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Failing after 9s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-09 23:26:38 +01:00
b205220bde fix: cli compatibility with nextjs 16
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Failing after 14s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-09 23:15:50 +01:00
3d5a802c6e chore: release packages
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Failing after 1m42s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-09 23:05:15 +01:00
b5d1272f85 fix: customer manager
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Failing after 2m15s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-09 23:04:21 +01:00
e152fb8171 fix: sync versions 2026-02-09 22:50:28 +01:00
42 changed files with 2518 additions and 2724 deletions

View File

@@ -1,5 +0,0 @@
---
"@mintel/mail": minor
---
Initial release of the branded email system package.

View File

@@ -36,6 +36,10 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: 🏷️ Sync Versions (if Tagged)
if: startsWith(github.ref, 'refs/tags/v')
run: pnpm sync-versions
- name: Lint
run: pnpm lint
@@ -77,7 +81,6 @@ jobs:
- name: 🏷️ Release Packages (Tag-Driven)
run: |
echo "🏷️ Tag detected [${{ github.ref_name }}], performing sync release..."
pnpm sync-versions
pnpm release:tag
build-images:

13
.husky/pre-push Executable file
View File

@@ -0,0 +1,13 @@
# Check if we are pushing a tag
if echo "$*" | grep -q "refs/tags/v"; then
echo "🏷️ Tag detected in push, syncing versions..."
pnpm sync-versions
# Stage the changed package.json files
git add "package.json" "packages/*/package.json" "apps/*/package.json"
# Amend the tag if it's on the current commit, but this is complex in pre-push.
# Better: Just warn the user that they might need to update the tag if package.json changed.
echo "⚠️ package.json files updated to match tag. Please ensure these changes are part of your tag/commit."
fi

View File

@@ -1,3 +0,0 @@
import { nextConfig } from "@mintel/eslint-config/next";
export default nextConfig;

View File

@@ -1,6 +1,6 @@
{
"name": "sample-website",
"version": "0.1.1",
"version": "1.6.0",
"private": true,
"type": "module",
"scripts": {
@@ -8,15 +8,9 @@
"dev:local": "mintel dev --local",
"build": "next build",
"start": "next start",
"lint": "next lint",
"lint": "eslint src/",
"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"
},
@@ -24,8 +18,8 @@
"@mintel/next-utils": "workspace:*",
"@mintel/observability": "workspace:*",
"@mintel/next-observability": "workspace:*",
"@sentry/nextjs": "^8.55.0",
"next": "15.1.6",
"@sentry/nextjs": "10.38.0",
"next": "16.1.6",
"next-intl": "^4.8.2",
"react": "^19.0.0",
"react-dom": "^19.0.0"

View File

@@ -0,0 +1,19 @@
version: 1
directus: 11.15.1
vendor: postgres
collections: []
fields: []
systemFields:
- collection: directus_activity
field: timestamp
schema:
is_indexed: true
- collection: directus_revisions
field: activity
schema:
is_indexed: true
- collection: directus_revisions
field: parent
schema:
is_indexed: true
relations: []

View File

View File

@@ -1,17 +1,18 @@
services:
app:
build:
context: .
context: ./apps/sample-website
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
environment:
- DIRECTUS_URL=${DIRECTUS_URL:-http://directus:8055}
env_file:
- .env
ports:
@@ -46,6 +47,7 @@ services:
volumes:
- ./directus/uploads:/directus/uploads
- ./directus/extensions:/directus/extensions
- ./directus/schema:/directus/schema
labels:
- "traefik.enable=true"
- "traefik.http.routers.sample-website-directus.rule=Host(`${DIRECTUS_HOST:-cms.sample-website.localhost}`)"

View File

@@ -8,6 +8,10 @@ export default [
"packages/customer-manager/index.js",
"**/*.db",
"**/build/**",
"**/data/**",
"**/reference/**",
"**/dist/**",
"**/.next/**",
],
},
...baseConfig,

View File

@@ -5,11 +5,17 @@
"scripts": {
"build": "pnpm -r build",
"dev": "pnpm -r dev",
"lint": "pnpm -r lint",
"lint": "pnpm -r --filter='./packages/**' --filter='./apps/**' lint",
"test": "pnpm -r test",
"changeset": "changeset",
"version-packages": "changeset version",
"sync-versions": "tsx scripts/sync-versions.ts",
"cms:push:infra": "./scripts/sync-directus.sh push infra",
"cms:pull:infra": "./scripts/sync-directus.sh pull infra",
"cms:schema:snapshot": "./scripts/cms-snapshot.sh",
"cms:schema:apply": "./scripts/cms-apply.sh local",
"cms:schema:apply:infra": "./scripts/cms-apply.sh infra",
"dev:infra": "docker-compose up -d directus directus-db",
"release": "pnpm build && changeset publish",
"release:tag": "pnpm build && pnpm -r publish --no-git-checks --access public",
"prepare": "husky"
@@ -27,7 +33,7 @@
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.2",
"eslint": "^9.39.2",
"eslint-plugin-next": "^0.0.0",
"@next/eslint-plugin-next": "16.1.6",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1",
"happy-dom": "^20.4.0",
@@ -45,5 +51,12 @@
"pino": "^10.3.1",
"pino-pretty": "^13.1.3",
"require-in-the-middle": "^8.0.1"
},
"version": "1.6.0",
"pnpm": {
"overrides": {
"next": "16.1.6",
"@sentry/nextjs": "10.38.0"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@mintel/cli",
"version": "1.0.1",
"version": "1.6.0",
"publishConfig": {
"access": "public",
"registry": "https://npm.infra.mintel.me"
@@ -28,4 +28,4 @@
"@types/prompts": "^2.4.4",
"@mintel/tsconfig": "workspace:*"
}
}
}

View File

@@ -231,7 +231,7 @@ program
"pagespeed:test": "mintel pagespeed",
},
dependencies: {
next: "15.1.6",
next: "16.1.6",
react: "^19.0.0",
"react-dom": "^19.0.0",
"@mintel/next-utils": "workspace:*",

View File

Binary file not shown.

View File

@@ -1,11 +1,11 @@
{
"name": "@mintel/cms-infra",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"up": "docker compose up -d",
"down": "docker compose down",
"logs": "docker compose logs -f"
}
}
"name": "@mintel/cms-infra",
"version": "1.6.0",
"private": true,
"type": "module",
"scripts": {
"up": "docker compose up -d",
"down": "docker compose down",
"logs": "docker compose logs -f"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +1,29 @@
{
"name": "customer-manager",
"description": "Custom High-Fidelity Customer & Company Management for Directus",
"icon": "supervisor_account",
"version": "1.0.0",
"keywords": [
"directus",
"directus-extension",
"directus-extension-module"
],
"files": [
"dist"
],
"directus:extension": {
"type": "module",
"path": "index.js",
"source": "src/index.ts",
"host": "*",
"name": "Customer Manager"
},
"scripts": {
"build": "directus-extension build",
"dev": "directus-extension build -w"
},
"devDependencies": {
"@directus/extensions-sdk": "11.0.2",
"vue": "^3.4.0"
}
}
"name": "customer-manager",
"description": "Custom High-Fidelity Customer & Company Management for Directus",
"icon": "supervisor_account",
"version": "1.6.0",
"keywords": [
"directus",
"directus-extension",
"directus-extension-module"
],
"files": [
"dist"
],
"directus:extension": {
"type": "module",
"path": "index.js",
"source": "src/index.ts",
"host": "*",
"name": "Customer Manager"
},
"scripts": {
"build": "directus-extension build",
"dev": "directus-extension build -w"
},
"devDependencies": {
"@directus/extensions-sdk": "11.0.2",
"vue": "^3.4.0"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@mintel/eslint-config",
"version": "1.0.1",
"version": "1.6.0",
"publishConfig": {
"access": "public",
"registry": "https://npm.infra.mintel.me"
@@ -20,8 +20,8 @@
"dependencies": {
"@eslint/eslintrc": "^3.0.0",
"@eslint/js": "^9.39.2",
"@next/eslint-plugin-next": "15.1.6",
"eslint-config-next": "15.1.6",
"@next/eslint-plugin-next": "16.1.6",
"eslint-config-next": "16.1.6",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1",
"typescript-eslint": "^8.54.0"

View File

@@ -1,29 +1,29 @@
{
"name": "@mintel/extension-feedback-commander",
"description": "Custom High-Fidelity Feedback Management Extension for Directus",
"icon": "view_kanban",
"version": "1.0.0",
"keywords": [
"directus",
"directus-extension",
"directus-extension-module"
],
"files": [
"dist"
],
"directus:extension": {
"type": "module",
"path": "dist/index.js",
"source": "src/index.ts",
"host": "*",
"name": "Feedback Commander"
},
"scripts": {
"build": "directus-extension build",
"dev": "directus-extension build -w"
},
"devDependencies": {
"@directus/extensions-sdk": "11.0.2",
"vue": "^3.4.0"
}
}
"name": "@mintel/extension-feedback-commander",
"description": "Custom High-Fidelity Feedback Management Extension for Directus",
"icon": "view_kanban",
"version": "1.6.0",
"keywords": [
"directus",
"directus-extension",
"directus-extension-module"
],
"files": [
"dist"
],
"directus:extension": {
"type": "module",
"path": "dist/index.js",
"source": "src/index.ts",
"host": "*",
"name": "Feedback Commander"
},
"scripts": {
"build": "directus-extension build",
"dev": "directus-extension build -w"
},
"devDependencies": {
"@directus/extensions-sdk": "11.0.2",
"vue": "^3.4.0"
}
}

View File

@@ -1,20 +1,20 @@
{
"name": "@mintel/gatekeeper",
"version": "1.0.0",
"version": "1.6.0",
"private": true,
"type": "module",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"lint": "eslint src/",
"test": "vitest run"
},
"dependencies": {
"@mintel/next-utils": "workspace:*",
"clsx": "^2.1.1",
"lucide-react": "^0.474.0",
"next": "15.1.7",
"next": "16.1.6",
"next-intl": "^4.8.2",
"react": "^19.0.0",
"react-dom": "^19.0.0",

View File

@@ -44,7 +44,7 @@ export async function GET(req: NextRequest) {
return response;
}
} catch (e) {
} catch (_e) {
// URL parsing failed, proceed with normal logic
}
@@ -61,7 +61,7 @@ export async function GET(req: NextRequest) {
isAuthenticated = true;
identity = payload.identity;
}
} catch (e) {
} catch (_e) {
// Fallback or old format
}
}

View File

@@ -1,7 +1,7 @@
import { NextRequest, NextResponse } from "next/server";
import { cookies } from "next/headers";
export async function GET(req: NextRequest) {
export async function GET(_req: NextRequest) {
const cookieStore = await cookies();
const authCookieName =
process.env.AUTH_COOKIE_NAME || "mintel_gatekeeper_session";
@@ -17,7 +17,7 @@ export async function GET(req: NextRequest) {
const payload = JSON.parse(session.value);
identity = payload.identity || "Guest";
company = payload.company || null;
} catch (e) {
} catch (_e) {
// Old format probably just the password
}

View File

@@ -1,6 +1,6 @@
{
"name": "@mintel/husky-config",
"version": "1.0.0",
"version": "1.6.0",
"publishConfig": {
"access": "public",
"registry": "https://npm.infra.mintel.me"

View File

@@ -1,6 +1,6 @@
{
"name": "@mintel/infra",
"version": "1.0.1",
"version": "1.6.0",
"publishConfig": {
"access": "public",
"registry": "https://npm.infra.mintel.me"

View File

@@ -0,0 +1,7 @@
# @mintel/mail
## 1.7.0
### Minor Changes
- 96ec2c7: Initial release of the branded email system package.

View File

@@ -1,6 +1,6 @@
{
"name": "@mintel/mail",
"version": "1.2.0",
"version": "1.7.0",
"private": false,
"publishConfig": {
"access": "public",

View File

@@ -1,5 +1,17 @@
# @mintel/next-config
## 1.6.1
### Patch Changes
- Add `turbopack: {}` to support Next.js 16 default Turbopack behavior when a webpack config is present.
## 1.6.1
### Patch Changes
- Add `turbopack: {}` to support Next.js 16 default Turbopack behavior when a webpack config is present.
## 1.0.1
### Patch Changes

View File

@@ -7,6 +7,7 @@ import path from "node:path";
/** @type {import('next').NextConfig} */
export const baseNextConfig = {
output: "standalone",
turbopack: {},
images: {
dangerouslyAllowSVG: true,
contentDispositionType: "attachment",

View File

@@ -1,6 +1,6 @@
{
"name": "@mintel/next-config",
"version": "1.0.1",
"version": "1.6.1",
"publishConfig": {
"access": "public",
"registry": "https://npm.infra.mintel.me"
@@ -16,6 +16,7 @@
},
"dependencies": {
"next-intl": "^4.8.2",
"@sentry/nextjs": "^8.0.0"
"@sentry/nextjs": "^10.38.0",
"next": "16.1.6"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@mintel/next-feedback",
"version": "1.0.0",
"version": "1.6.0",
"publishConfig": {
"access": "public",
"registry": "https://npm.infra.mintel.me"
@@ -31,7 +31,7 @@
"framer-motion": "^11.5.4",
"html2canvas": "^1.4.1",
"lucide-react": "^0.441.0",
"next": "15.1.7",
"next": "16.1.6",
"tailwind-merge": "^2.5.2"
},
"devDependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@mintel/next-observability",
"version": "1.0.0",
"version": "1.6.0",
"publishConfig": {
"access": "public",
"registry": "https://npm.infra.mintel.me"
@@ -28,8 +28,8 @@
},
"dependencies": {
"@mintel/observability": "workspace:*",
"@sentry/nextjs": "^8.55.0",
"next": "15.1.7"
"@sentry/nextjs": "^10.38.0",
"next": "16.1.6"
},
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@mintel/next-utils",
"version": "1.0.1",
"version": "1.6.0",
"publishConfig": {
"access": "public",
"registry": "https://npm.infra.mintel.me"
@@ -16,7 +16,7 @@
},
"dependencies": {
"@directus/sdk": "^21.0.0",
"next": "15.1.7",
"next": "16.1.6",
"next-intl": "^4.8.2",
"zod": "^3.0.0"
},

View File

@@ -1,6 +1,6 @@
{
"name": "@mintel/observability",
"version": "1.0.0",
"version": "1.6.0",
"publishConfig": {
"access": "public",
"registry": "https://npm.infra.mintel.me"

View File

@@ -1,6 +1,6 @@
{
"name": "@mintel/tsconfig",
"version": "1.0.1",
"version": "1.6.0",
"publishConfig": {
"access": "public",
"registry": "https://npm.infra.mintel.me"

3391
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

71
scripts/cms-apply.sh Executable file
View File

@@ -0,0 +1,71 @@
#!/bin/bash
# Configuration
PROJECT="infra-cms"
LOCAL_SCHEMA_PATH="./packages/cms-infra/schema/snapshot.yaml"
REMOTE_HOST="root@infra.mintel.me"
REMOTE_DIR="/opt/infra/directus"
ENV=$1
if [ -z "$ENV" ]; then
echo "Usage: ./scripts/cms-apply.sh [local|infra]"
exit 1
fi
case $ENV in
local)
PROJECT="infra-cms"
CMD_PREFIX="docker-compose -f packages/cms-infra/docker-compose.yml"
LOCAL_CONTAINER=$($CMD_PREFIX ps -q $PROJECT)
if [ -z "$LOCAL_CONTAINER" ]; then
echo "❌ Local $PROJECT container not found. Is it running?"
exit 1
fi
echo "🚀 Applying schema to LOCAL $PROJECT..."
docker exec "$LOCAL_CONTAINER" npx directus schema apply -y /directus/schema/snapshot.yaml
;;
infra)
# 'infra' is the remote production server for at-mintel
PROJECT="directus" # Remote project name
echo "🔍 Detecting remote container..."
REMOTE_CONTAINER=$(ssh "$REMOTE_HOST" "docker ps --filter label=com.docker.compose.project=$PROJECT --filter label=com.docker.compose.service=directus -q")
if [ -z "$REMOTE_CONTAINER" ]; then
# Fallback to older name if labels fail
REMOTE_CONTAINER=$(ssh "$REMOTE_HOST" "docker ps -f name=directus-directus-1 -q")
fi
if [ -z "$REMOTE_CONTAINER" ]; then
echo "❌ Remote container for $ENV not found."
exit 1
fi
echo "📦 Syncing extensions to REMOTE $ENV..."
# Ensure remote directory exists
ssh "$REMOTE_HOST" "mkdir -p $REMOTE_DIR/extensions"
rsync -avz --delete ./packages/cms-infra/extensions/ "$REMOTE_HOST:$REMOTE_DIR/extensions/"
echo "📤 Injecting snapshot directly into container $REMOTE_CONTAINER..."
# Inject file via stdin to avoid needing a host-side mount or scp path matching
ssh "$REMOTE_HOST" "docker exec -i $REMOTE_CONTAINER sh -c 'cat > /tmp/snapshot.yaml'" < "$LOCAL_SCHEMA_PATH"
echo "🚀 Applying schema to REMOTE $ENV..."
ssh "$REMOTE_HOST" "docker exec $REMOTE_CONTAINER npx directus schema apply -y /tmp/snapshot.yaml"
echo "🔄 Restarting remote Directus to clear cache..."
ssh "$REMOTE_HOST" "cd $REMOTE_DIR && docker compose restart directus"
# Cleanup
ssh "$REMOTE_HOST" "docker exec $REMOTE_CONTAINER rm /tmp/snapshot.yaml"
;;
*)
echo "❌ Invalid environment: $ENV. Supported: local, infra."
exit 1
;;
esac
echo "✨ Schema apply complete!"

23
scripts/cms-snapshot.sh Executable file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
# Configuration
PROJECT="infra-cms"
SCHEMA_PATH="./packages/cms-infra/schema/snapshot.yaml"
CMD_PREFIX="docker-compose -f packages/cms-infra/docker-compose.yml"
# Detect local container
LOCAL_CONTAINER=$($CMD_PREFIX ps -q $PROJECT)
if [ -z "$LOCAL_CONTAINER" ]; then
echo "❌ Local $PROJECT container not found. Is it running?"
exit 1
fi
echo "📸 Creating schema snapshot for local $PROJECT..."
# Note: we save it to the mounted volume path inside the container
docker exec "$LOCAL_CONTAINER" npx directus schema snapshot -y /directus/schema/snapshot.yaml
echo "🛠️ Repairing snapshot for Postgres compatibility..."
python3 ./scripts/fix_snapshot_v3.py
echo "✅ Snapshot saved and repaired at $SCHEMA_PATH"

View File

@@ -0,0 +1,96 @@
import sys
import os
path = '/Users/marcmintel/Projects/at-mintel/packages/cms-infra/schema/snapshot.yaml'
if not os.path.exists(path):
print(f"File not found: {path}")
sys.exit(1)
with open(path, 'r') as f:
lines = f.readlines()
new_lines = []
current_collection = None
current_field = None
in_schema = False
fix_fields = {'id', 'company', 'user_created', 'user_updated', 'screenshot', 'logo', 'feedback_id'}
uuid_fields = {'id', 'company', 'user_created', 'user_updated'}
# For multi-pass logic
snapshot_has_feedback_id = False
for line in lines:
stripped = line.strip()
if stripped.startswith('- collection:'):
current_collection = stripped.split(':')[-1].strip()
in_schema = False
elif stripped.startswith('field:'):
current_field = stripped.split(':')[-1].strip()
if current_collection == 'visual_feedback_comments' and current_field == 'feedback_id':
snapshot_has_feedback_id = True
elif stripped == 'schema:':
in_schema = True
elif stripped == 'meta:' or stripped.startswith('- collection:') or (not line.startswith(' ') and line.strip() and not line.startswith('-')):
in_schema = False
# Top-level field type
if not in_schema and stripped.startswith('type:') and current_field in uuid_fields:
line = line.replace('type: string', 'type: uuid')
# Schema data type
if in_schema and current_field in fix_fields:
if 'data_type: char' in line or 'data_type: varchar' in line:
line = line.replace('data_type: char', 'data_type: uuid').replace('data_type: varchar', 'data_type: uuid')
if 'max_length:' in line:
line = ' max_length: null\n'
new_lines.append(line)
# Handle Missing feedback_id Injection
if not snapshot_has_feedback_id:
# We find systemFields and inject before it
injected = False
final_lines = []
feedback_id_block = """ - collection: visual_feedback_comments
field: feedback_id
type: integer
meta:
collection: visual_feedback_comments
field: feedback_id
interface: select-dropdown-m2o
required: true
sort: 4
width: full
schema:
name: feedback_id
table: visual_feedback_comments
data_type: integer
is_nullable: false
is_indexed: true
foreign_key_table: visual_feedback
foreign_key_column: id
"""
for line in new_lines:
if 'systemFields:' in line and not injected:
final_lines.append(feedback_id_block)
injected = True
final_lines.append(line)
new_lines = final_lines
# Second pass for primary key nullability
final_lines = []
for i in range(len(new_lines)):
line = new_lines[i]
if 'is_primary_key: true' in line:
# Search backwards and forwards
for j in range(max(0, i-10), min(len(new_lines), i+10)):
if 'is_nullable: true' in new_lines[j]:
new_lines[j] = new_lines[j].replace('is_nullable: true', 'is_nullable: false')
final_lines.append(line)
with open(path, 'w') as f:
f.writelines(new_lines)
print("SUCCESS: Full normalization and field injection complete.")

123
scripts/sync-directus.sh Executable file
View File

@@ -0,0 +1,123 @@
#!/bin/bash
# Configuration
REMOTE_HOST="root@infra.mintel.me"
REMOTE_DIR="/opt/infra/directus"
# DB Details (matching docker-compose defaults)
DB_USER="directus"
DB_NAME="directus"
ACTION=$1
ENV=$2
# Help
if [ -z "$ACTION" ] || [ -z "$ENV" ]; then
echo "Usage: ./scripts/sync-directus.sh [push|pull] [infra|testing|staging|production]"
echo ""
echo "Commands:"
echo " push Sync LOCAL data -> REMOTE"
echo " pull Sync REMOTE data -> LOCAL"
echo ""
echo "Environments:"
echo " infra (infra.mintel.me)"
exit 1
fi
# Map Environment
case $ENV in
infra)
PROJECT_NAME="directus"
;;
*)
echo "❌ Invalid environment: $ENV. Only 'infra' is currently configured for monorepo sync."
exit 1
;;
esac
# Detect local containers
echo "🔍 Detecting local database..."
LOCAL_DB_CONTAINER=$(docker compose ps -q directus-db)
if [ -z "$LOCAL_DB_CONTAINER" ]; then
echo "❌ Local directus-db container not found. Is it running? (npm run dev)"
exit 1
fi
if [ "$ACTION" == "push" ]; then
echo "🚀 Pushing Local Data to $ENV..."
# 1. DB Dump
echo "📦 Dumping local database..."
docker exec "$LOCAL_DB_CONTAINER" pg_dump -U "$DB_USER" --clean --if-exists --no-owner --no-privileges "$DB_NAME" > dump.sql
# 2. Upload Dump
echo "📤 Uploading dump to remote server..."
scp dump.sql "$REMOTE_HOST:$REMOTE_DIR/dump.sql"
# 3. Restore on Remote
echo "🔄 Restoring dump on $ENV..."
REMOTE_DB_CONTAINER=$(ssh "$REMOTE_HOST" "cd $REMOTE_DIR && docker compose -p $PROJECT_NAME ps -q directus-postgres")
if [ -z "$REMOTE_DB_CONTAINER" ]; then
echo "❌ Remote $ENV-db container not found!"
exit 1
fi
# Wipe remote DB clean before restore to avoid constraint errors
echo "🧹 Wiping remote database schema..."
ssh "$REMOTE_HOST" "docker exec $REMOTE_DB_CONTAINER psql -U $DB_USER $DB_NAME -c 'DROP SCHEMA public CASCADE; CREATE SCHEMA public;'"
echo "⚡ Restoring database..."
ssh "$REMOTE_HOST" "docker exec -i $REMOTE_DB_CONTAINER psql -U $DB_USER $DB_NAME < $REMOTE_DIR/dump.sql"
# 4. Sync Uploads
echo "📁 Syncing uploads (Local -> $ENV)..."
rsync -avz --progress ./directus/uploads/ "$REMOTE_HOST:$REMOTE_DIR/uploads/"
# Clean up
rm dump.sql
ssh "$REMOTE_HOST" "rm $REMOTE_DIR/dump.sql"
# 5. Restart Directus to trigger migrations and refresh schema cache
echo "🔄 Restarting remote Directus to apply migrations..."
ssh "$REMOTE_HOST" "cd $REMOTE_DIR && docker compose -p $PROJECT_NAME restart directus"
echo "✨ Push to $ENV complete!"
elif [ "$ACTION" == "pull" ]; then
echo "📥 Pulling $ENV Data to Local..."
# 1. DB Dump on Remote
echo "📦 Dumping remote database ($ENV)..."
REMOTE_DB_CONTAINER=$(ssh "$REMOTE_HOST" "cd $REMOTE_DIR && docker compose -p $PROJECT_NAME ps -q directus-postgres")
if [ -z "$REMOTE_DB_CONTAINER" ]; then
echo "❌ Remote $ENV-db container not found!"
exit 1
fi
ssh "$REMOTE_HOST" "docker exec $REMOTE_DB_CONTAINER pg_dump -U $DB_USER --clean --if-exists --no-owner --no-privileges $DB_NAME > $REMOTE_DIR/dump.sql"
# 2. Download Dump
echo "📥 Downloading dump..."
scp "$REMOTE_HOST:$REMOTE_DIR/dump.sql" dump.sql
# Wipe local DB clean before restore to avoid constraint errors
echo "🧹 Wiping local database schema..."
docker exec "$LOCAL_DB_CONTAINER" psql -U "$DB_USER" "$DB_NAME" -c 'DROP SCHEMA public CASCADE; CREATE SCHEMA public;'
echo "⚡ Restoring database locally..."
docker exec -i "$LOCAL_DB_CONTAINER" psql -U "$DB_USER" "$DB_NAME" < dump.sql
# 4. Sync Uploads
echo "📁 Syncing uploads ($ENV -> Local)..."
rsync -avz --progress "$REMOTE_HOST:$REMOTE_DIR/uploads/" ./directus/uploads/
# Clean up
rm dump.sql
ssh "$REMOTE_HOST" "rm $REMOTE_DIR/dump.sql"
echo "✨ Pull to Local complete!"
else
echo "Invalid action: $ACTION. Use push or pull."
exit 1
fi

View File

@@ -1,11 +1,45 @@
import * as fs from "fs";
import * as path from "path";
const tag = process.env.GITHUB_REF_NAME || process.env.TAG;
import { execSync } from "child_process";
if (!tag || !tag.startsWith("v")) {
console.error("❌ No valid tag found (must start with v, e.g., v1.0.0)");
process.exit(1);
/**
* Gets the current version tag from environment or git.
*/
function getVersionTag() {
// 1. Check CI environment variables
if (
process.env.GITHUB_REF_NAME &&
process.env.GITHUB_REF_NAME.startsWith("v")
) {
return process.env.GITHUB_REF_NAME;
}
if (process.env.TAG && process.env.TAG.startsWith("v")) {
return process.env.TAG;
}
// 2. Try to get it from local git
try {
const gitTag = execSync("git describe --tags --abbrev=0", {
encoding: "utf8",
}).trim();
if (gitTag && gitTag.startsWith("v")) {
return gitTag;
}
} catch (e) {
// Fallback or silence
}
return null;
}
const tag = getVersionTag();
if (!tag) {
console.log(
" No version tag found (starting with v). Skipping version sync.",
);
process.exit(0); // Exit gracefully if no tag is present
}
const version = tag.replace(/^v/, "");