32 Commits

Author SHA1 Message Date
29d474a102 fix: traefik issues
All checks were successful
Build & Deploy / 🔍 Prepare Environment (push) Successful in 8s
Build & Deploy / 🧪 QA (push) Successful in 1m13s
Build & Deploy / 🚀 Deploy (push) Successful in 9s
Build & Deploy / 🏗️ Build (push) Successful in 2m5s
Build & Deploy / 🔔 Notifications (push) Successful in 2s
2026-02-06 23:15:22 +01:00
a31202f63b refactor: use explicit Git reference variables for more robust deployment target and image tag determination in Gitea workflow.
All checks were successful
Build & Deploy / 🔍 Prepare Environment (push) Successful in 5s
Build & Deploy / 🧪 QA (push) Successful in 1m33s
Build & Deploy / 🏗️ Build (push) Successful in 2m7s
Build & Deploy / 🚀 Deploy (push) Successful in 9s
Build & Deploy / 🔔 Notifications (push) Successful in 2s
2026-02-06 22:51:40 +01:00
0afd6bbb60 fix: logo position
All checks were successful
Build & Deploy / 🔍 Prepare Environment (push) Successful in 7s
Build & Deploy / 🧪 QA (push) Successful in 1m32s
Build & Deploy / 🏗️ Build (push) Successful in 4m44s
Build & Deploy / 🚀 Deploy (push) Successful in 13s
Build & Deploy / 🔔 Notifications (push) Successful in 2s
2026-02-06 21:42:43 +01:00
2c647f0284 chore: directus sync
All checks were successful
Build & Deploy / 🔍 Prepare Environment (push) Successful in 4s
Build & Deploy / 🧪 QA (push) Successful in 1m57s
Build & Deploy / 🏗️ Build (push) Successful in 2m3s
Build & Deploy / 🚀 Deploy (push) Successful in 33s
Build & Deploy / 🔔 Notifications (push) Successful in 2s
2026-02-06 21:35:00 +01:00
d9ff6d640d feat: Configure Traefik to use the infra network for services, add an internal Directus URL, and enhance Directus and Gatekeeper configurations.
All checks were successful
Build & Deploy / 🔍 Prepare Environment (push) Successful in 9s
Build & Deploy / 🧪 QA (push) Successful in 1m13s
Build & Deploy / 🏗️ Build (push) Successful in 4m51s
Build & Deploy / 🚀 Deploy (push) Successful in 10s
Build & Deploy / 🔔 Notifications (push) Successful in 1s
2026-02-06 19:23:35 +01:00
8ab9ec7d1f chore: bootstrap command 2026-02-06 19:11:19 +01:00
0cc67d54ef refactor: overhaul Directus sync script with schema wiping and restart, update branding, and rename CMS scripts. 2026-02-06 19:09:56 +01:00
cbb95a38cf feat: Introduce COOKIE_DOMAIN and NEXT_PUBLIC_BASE_URL environment variables for gatekeeper service configuration.
All checks were successful
Build & Deploy / 🔍 Prepare Environment (push) Successful in 9s
Build & Deploy / 🧪 QA (push) Successful in 1m17s
Build & Deploy / 🏗️ Build (push) Successful in 5m1s
Build & Deploy / 🚀 Deploy (push) Successful in 10s
Build & Deploy / 🔔 Notifications (push) Successful in 2s
2026-02-06 18:05:04 +01:00
5b163d6d74 feat: Enable dynamic app environment variable configuration via workflow and docker-compose.
All checks were successful
Build & Deploy / 🔍 Prepare Environment (push) Successful in 9s
Build & Deploy / 🧪 QA (push) Successful in 1m27s
Build & Deploy / 🏗️ Build (push) Successful in 2m6s
Build & Deploy / 🚀 Deploy (push) Successful in 23s
Build & Deploy / 🔔 Notifications (push) Successful in 2s
2026-02-06 17:26:28 +01:00
f6e774b5c9 feat: Add GATEKEEPER_PASSWORD environment variable for authentication.
All checks were successful
Build & Deploy / 🔍 Prepare Environment (push) Successful in 4s
Build & Deploy / 🧪 QA (push) Successful in 1m13s
Build & Deploy / 🏗️ Build (push) Successful in 5m2s
Build & Deploy / 🚀 Deploy (push) Successful in 12s
Build & Deploy / 🔔 Notifications (push) Successful in 2s
2026-02-06 17:07:25 +01:00
613c8b1645 fix: Streamline variable interpolation in deploy workflow and Traefik labels by removing unnecessary quoting and default fallbacks.
All checks were successful
Build & Deploy / 🔍 Prepare Environment (push) Successful in 4s
Build & Deploy / 🧪 QA (push) Successful in 1m14s
Build & Deploy / 🏗️ Build (push) Successful in 2m5s
Build & Deploy / 🚀 Deploy (push) Successful in 28s
Build & Deploy / 🔔 Notifications (push) Successful in 1s
2026-02-06 16:32:21 +01:00
9e1aae5d76 feat: Add dedicated subdomain routing for Gatekeeper and update its service alias for forward authentication middleware.
All checks were successful
Build & Deploy / 🔍 Prepare Environment (push) Successful in 5s
Build & Deploy / 🧪 QA (push) Successful in 1m14s
Build & Deploy / 🏗️ Build (push) Successful in 4m34s
Build & Deploy / 🚀 Deploy (push) Successful in 15s
Build & Deploy / 🔔 Notifications (push) Successful in 1s
2026-02-06 16:11:39 +01:00
f1e3ad1357 chore: npm update
All checks were successful
Build & Deploy / 🔍 Prepare Environment (push) Successful in 7s
Build & Deploy / 🧪 QA (push) Successful in 1m12s
Build & Deploy / 🏗️ Build (push) Successful in 4m41s
Build & Deploy / 🚀 Deploy (push) Successful in 15s
Build & Deploy / 🔔 Notifications (push) Successful in 2s
2026-02-06 15:52:49 +01:00
39b044c2c2 chore: Use caret version specifiers for Mintel dependencies.
Some checks failed
Build & Deploy / 🔍 Prepare Environment (push) Successful in 7s
Build & Deploy / 🧪 QA (push) Failing after 10s
Build & Deploy / 🏗️ Build (push) Successful in 5m32s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🔔 Notifications (push) Successful in 2s
2026-02-06 15:28:07 +01:00
c0c73315c8 chore(deploy): switch main branch to testing domain and add staging tag logic (aligned with klz-2026)
All checks were successful
Build & Deploy / 🔍 Prepare Environment (push) Successful in 5s
Build & Deploy / 🧪 QA (push) Successful in 1m29s
Build & Deploy / 🏗️ Build (push) Successful in 4m45s
Build & Deploy / 🚀 Deploy (push) Successful in 20s
Build & Deploy / 🔔 Notifications (push) Successful in 1s
2026-02-05 22:16:27 +01:00
72fbae0666 fix(deploy): remove redundant backticks from Traefik Host labels to fix double-quoting
All checks were successful
Build & Deploy / 🔍 Prepare Environment (push) Successful in 5s
Build & Deploy / 🧪 QA (push) Successful in 1m34s
Build & Deploy / 🏗️ Build (push) Successful in 2m6s
Build & Deploy / 🚀 Deploy (push) Successful in 14s
Build & Deploy / 🔔 Notifications (push) Successful in 2s
2026-02-05 22:10:04 +01:00
3ed32210ad fix(ci): quote .env heredoc and fix docker-compose extension
All checks were successful
Build & Deploy / 🔍 Prepare Environment (push) Successful in 4s
Build & Deploy / 🧪 QA (push) Successful in 1m56s
Build & Deploy / 🏗️ Build (push) Successful in 2m5s
Build & Deploy / 🚀 Deploy (push) Successful in 16s
Build & Deploy / 🔔 Notifications (push) Successful in 1s
2026-02-05 19:23:46 +01:00
f2366b5a38 fix(ci): refactor SSH deployment to manual ssh/scp (aligned with klz-2026)
Some checks failed
Build & Deploy / 🔍 Prepare Environment (push) Successful in 5s
Build & Deploy / 🧪 QA (push) Successful in 1m13s
Build & Deploy / 🏗️ Build (push) Successful in 4m47s
Build & Deploy / 🚀 Deploy (push) Failing after 6s
Build & Deploy / 🔔 Notifications (push) Successful in 2s
2026-02-05 14:19:57 +01:00
dccf6ad2ce fix(ci): pass NEXT_PUBLIC_TARGET to docker build and suppress sentry warnings
Some checks failed
Build & Deploy / 🔍 Prepare Environment (push) Successful in 4s
Build & Deploy / 🧪 QA (push) Successful in 1m12s
Build & Deploy / 🏗️ Build (push) Successful in 4m38s
Build & Deploy / 🚀 Deploy (push) Failing after 3s
Build & Deploy / 🔔 Notifications (push) Successful in 1s
2026-02-05 13:00:21 +01:00
788c9ca7ac fix(ci): restore Next 16 and isolate Docker build from base image workspace
Some checks failed
Build & Deploy / 🔍 Prepare Environment (push) Successful in 7s
Build & Deploy / 🧪 QA (push) Successful in 1m30s
Build & Deploy / 🏗️ Build (push) Failing after 1m58s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🔔 Notifications (push) Successful in 2s
2026-02-05 12:53:18 +01:00
34474de163 chore: delete pnpm-workspace.yaml.
Some checks failed
Build & Deploy / 🔍 Prepare Environment (push) Successful in 5s
Build & Deploy / 🧪 QA (push) Successful in 1m59s
Build & Deploy / 🏗️ Build (push) Failing after 2m42s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🔔 Notifications (push) Successful in 1s
2026-02-05 12:04:31 +01:00
12646e45e4 fix(ci): regenerate lockfile and relax frozen-lockfile for Docker 2026-02-05 12:03:46 +01:00
b25299a3a8 chore: Remove /dev from Next.js routes types import path in next-env.d.ts.
Some checks failed
Build & Deploy / 🔍 Prepare Environment (push) Successful in 8s
Build & Deploy / 🏗️ Build (push) Failing after 53s
Build & Deploy / 🧪 QA (push) Successful in 1m12s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🔔 Notifications (push) Successful in 2s
2026-02-05 11:58:26 +01:00
aa9b280f5c fix(ci): fix docker build by adding pnpm install in Dockerfile
Some checks failed
Build & Deploy / 🔍 Prepare Environment (push) Successful in 8s
Build & Deploy / 🏗️ Build (push) Failing after 39s
Build & Deploy / 🧪 QA (push) Successful in 1m30s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🔔 Notifications (push) Successful in 1s
2026-02-05 11:53:29 +01:00
2ec9a29565 fix(ci): align registry secret names with Mintel standard (USER/PASS)
Some checks failed
Build & Deploy / 🔍 Prepare Environment (push) Successful in 4s
Build & Deploy / 🏗️ Build (push) Failing after 42s
Build & Deploy / 🧪 QA (push) Successful in 1m30s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🔔 Notifications (push) Successful in 2s
2026-02-05 11:50:56 +01:00
20cafce97d fix(ci): fix eslint compatibility, downgrade to v8, and fix lint errors
Some checks failed
Build & Deploy / 🔍 Prepare Environment (push) Successful in 8s
Build & Deploy / 🏗️ Build (push) Failing after 12s
Build & Deploy / 🧪 QA (push) Successful in 1m13s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🔔 Notifications (push) Successful in 2s
2026-02-05 11:15:41 +01:00
31f931f7ce fix(ci): use eslint directly and fix lint step environment
Some checks failed
Build & Deploy / 🔍 Prepare Environment (push) Successful in 4s
Build & Deploy / 🏗️ Build (push) Failing after 9s
Build & Deploy / 🧪 QA (push) Failing after 43s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🔔 Notifications (push) Successful in 2s
2026-02-05 11:10:43 +01:00
e415b5118b fix(ci): use corepack enable for pnpm to avoid hang in runner
Some checks failed
Build & Deploy / 🔍 Prepare Environment (push) Successful in 4s
Build & Deploy / 🏗️ Build (push) Failing after 10s
Build & Deploy / 🧪 QA (push) Failing after 33s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🔔 Notifications (push) Successful in 1s
2026-02-05 11:08:34 +01:00
84aef6b860 fix(ci): use linux/arm64 platform to match infra
Some checks failed
Build & Deploy / 🔍 Prepare Environment (push) Successful in 4s
Build & Deploy / 🧪 QA (push) Failing after 6s
Build & Deploy / 🏗️ Build (push) Failing after 15s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🔔 Notifications (push) Successful in 2s
2026-02-05 10:57:04 +01:00
195932dde4 fix(ci): explicitly use shell: bash for all steps 2026-02-05 10:56:56 +01:00
977773fe94 fix(ci): add debug info and make maintenance optional 2026-02-05 10:56:42 +01:00
a5e2e5a2db fix: Align CI workflow with Mintel standards (add container image & buildx)
All checks were successful
Build & Deploy / 🔍 Prepare Environment (push) Successful in 31s
Build & Deploy / 🧪 QA (push) Has been skipped
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🔔 Notifications (push) Successful in 1s
2026-02-05 10:45:30 +01:00
15 changed files with 661 additions and 406 deletions

3
.eslintrc.json Normal file
View File

@@ -0,0 +1,3 @@
{
"extends": ["next/core-web-vitals", "next/typescript"]
}

View File

@@ -16,6 +16,8 @@ jobs:
prepare: prepare:
name: 🔍 Prepare Environment name: 🔍 Prepare Environment
runs-on: docker runs-on: docker
container:
image: catthehacker/ubuntu:act-latest
outputs: outputs:
target: ${{ steps.determine.outputs.target }} target: ${{ steps.determine.outputs.target }}
image_tag: ${{ steps.determine.outputs.image_tag }} image_tag: ${{ steps.determine.outputs.image_tag }}
@@ -24,8 +26,25 @@ jobs:
next_public_base_url: ${{ steps.determine.outputs.next_public_base_url }} next_public_base_url: ${{ steps.determine.outputs.next_public_base_url }}
directus_url: ${{ steps.determine.outputs.directus_url }} directus_url: ${{ steps.determine.outputs.directus_url }}
directus_host: ${{ steps.determine.outputs.directus_host }} directus_host: ${{ steps.determine.outputs.directus_host }}
gatekeeper_host: ${{ steps.determine.outputs.gatekeeper_host }}
traefik_rule: ${{ steps.determine.outputs.traefik_rule }}
gatekeeper_rule: ${{ steps.determine.outputs.gatekeeper_rule }}
project_name: ${{ steps.determine.outputs.project_name }} project_name: ${{ steps.determine.outputs.project_name }}
steps: steps:
- name: 🔍 Debug Info
shell: bash
run: |
echo "ref_name: ${{ github.ref_name }}"
echo "ref_type: ${{ github.ref_type }}"
echo "tag: ${{ github.ref_name }}"
- name: 🧹 Maintenance (Runner Cleanup)
continue-on-error: true
shell: bash
run: |
docker image prune -f || true
docker builder prune -f --filter "until=24h" || true
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
@@ -33,36 +52,84 @@ jobs:
- name: 🔍 Determine Environment - name: 🔍 Determine Environment
id: determine id: determine
shell: bash
run: | run: |
TAG="${{ github.ref_name }}" REF="${{ github.ref }}"
REF_NAME="${{ github.ref_name }}"
REF_TYPE="${{ github.ref_type }}"
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7) SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
DOMAIN_BASE="mb-grid-solutions.com" DOMAIN_BASE="mb-grid-solutions.com"
PRJ_ID="mb-grid-solutions" PRJ_ID="mb-grid-solutions"
if [[ "${{ github.ref_type }}" == "branch" && "$TAG" == "main" ]]; then echo "Detecting environment for ref: $REF ($REF_NAME, type: $REF_TYPE)"
TARGET="staging"
IMAGE_TAG="staging-${SHORT_SHA}" # Fallback for REF_TYPE if missing
ENV_FILE=".env.staging" if [[ -z "$REF_TYPE" ]]; then
TRAEFIK_HOST="\`staging.${DOMAIN_BASE}\`" if [[ "$REF" == refs/tags/* ]]; then
NEXT_PUBLIC_BASE_URL="https://staging.${DOMAIN_BASE}" REF_TYPE="tag"
DIRECTUS_URL="https://cms.staging.${DOMAIN_BASE}" elif [[ "$REF" == refs/heads/* ]]; then
DIRECTUS_HOST="\`cms.staging.${DOMAIN_BASE}\`" REF_TYPE="branch"
elif [[ "${{ github.ref_type }}" == "tag" ]]; then fi
TARGET="production"
IMAGE_TAG="$TAG"
ENV_FILE=".env.prod"
TRAEFIK_HOST="\`${DOMAIN_BASE}\`, \`www.${DOMAIN_BASE}\`"
NEXT_PUBLIC_BASE_URL="https://${DOMAIN_BASE}"
DIRECTUS_URL="https://cms.${DOMAIN_BASE}"
DIRECTUS_HOST="\`cms.${DOMAIN_BASE}\`"
else
TARGET="skip"
fi fi
if [[ "$REF_TYPE" == "branch" && "$REF_NAME" == "main" ]]; then
TARGET="testing"
IMAGE_TAG="testing-${SHORT_SHA}"
ENV_FILE=".env.testing"
TRAEFIK_HOST="testing.${DOMAIN_BASE}"
GATEKEEPER_HOST="gatekeeper.testing.${DOMAIN_BASE}"
NEXT_PUBLIC_BASE_URL="https://testing.${DOMAIN_BASE}"
DIRECTUS_URL="https://cms.testing.${DOMAIN_BASE}"
DIRECTUS_HOST="cms.testing.${DOMAIN_BASE}"
elif [[ "$REF_TYPE" == "tag" ]]; then
if [[ "$REF_NAME" =~ ^v[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then
TARGET="production"
IMAGE_TAG="$REF_NAME"
ENV_FILE=".env.prod"
TRAEFIK_HOST="${DOMAIN_BASE}" # Primary domain
GATEKEEPER_HOST="gatekeeper.${DOMAIN_BASE}"
NEXT_PUBLIC_BASE_URL="https://${DOMAIN_BASE}"
DIRECTUS_URL="https://cms.${DOMAIN_BASE}"
DIRECTUS_HOST="cms.${DOMAIN_BASE}"
elif [[ "$REF_NAME" =~ -rc || "$REF_NAME" =~ -beta || "$REF_NAME" =~ -alpha ]]; then
TARGET="staging"
IMAGE_TAG="$REF_NAME"
ENV_FILE=".env.staging"
TRAEFIK_HOST="staging.${DOMAIN_BASE}"
GATEKEEPER_HOST="gatekeeper.staging.${DOMAIN_BASE}"
NEXT_PUBLIC_BASE_URL="https://staging.${DOMAIN_BASE}"
DIRECTUS_URL="https://cms.staging.${DOMAIN_BASE}"
DIRECTUS_HOST="cms.staging.${DOMAIN_BASE}"
else
TARGET="skip"
echo "Tag $REF_NAME did not match any environment pattern."
fi
else
TARGET="skip"
echo "Ref type $REF_TYPE is not handled for deployment."
fi
# Determine Rules based on target (if not skipped)
if [[ "$TARGET" != "skip" ]]; then
if [[ "$TARGET" == "production" ]]; then
TRAEFIK_RULE="Host(\`${DOMAIN_BASE}\`) || Host(\`www.${DOMAIN_BASE}\`)"
GATEKEEPER_RULE="(Host(\`${DOMAIN_BASE}\`) || Host(\`www.${DOMAIN_BASE}\`)) && PathPrefix(\`/gatekeeper\`) || Host(\`gatekeeper.${DOMAIN_BASE}\`)"
else
TRAEFIK_RULE="Host(\`${TRAEFIK_HOST}\`)"
GATEKEEPER_RULE="(Host(\`${TRAEFIK_HOST}\`) && PathPrefix(\`/gatekeeper\`)) || Host(\`gatekeeper.${TRAEFIK_HOST}\`)"
fi
fi
echo "Target determined: $TARGET"
echo "Image tag: $IMAGE_TAG"
echo "target=$TARGET" >> "$GITHUB_OUTPUT" echo "target=$TARGET" >> "$GITHUB_OUTPUT"
echo "image_tag=$IMAGE_TAG" >> "$GITHUB_OUTPUT" echo "image_tag=$IMAGE_TAG" >> "$GITHUB_OUTPUT"
echo "env_file=$ENV_FILE" >> "$GITHUB_OUTPUT" echo "env_file=$ENV_FILE" >> "$GITHUB_OUTPUT"
echo "traefik_host=$TRAEFIK_HOST" >> "$GITHUB_OUTPUT" echo "traefik_host=$TRAEFIK_HOST" >> "$GITHUB_OUTPUT"
echo "traefik_rule=$TRAEFIK_RULE" >> "$GITHUB_OUTPUT"
echo "gatekeeper_rule=$GATEKEEPER_RULE" >> "$GITHUB_OUTPUT"
echo "gatekeeper_host=$GATEKEEPER_HOST" >> "$GITHUB_OUTPUT"
echo "next_public_base_url=$NEXT_PUBLIC_BASE_URL" >> "$GITHUB_OUTPUT" echo "next_public_base_url=$NEXT_PUBLIC_BASE_URL" >> "$GITHUB_OUTPUT"
echo "directus_url=$DIRECTUS_URL" >> "$GITHUB_OUTPUT" echo "directus_url=$DIRECTUS_URL" >> "$GITHUB_OUTPUT"
echo "directus_host=$DIRECTUS_HOST" >> "$GITHUB_OUTPUT" echo "directus_host=$DIRECTUS_HOST" >> "$GITHUB_OUTPUT"
@@ -73,18 +140,30 @@ jobs:
needs: prepare needs: prepare
if: needs.prepare.outputs.target != 'skip' if: needs.prepare.outputs.target != 'skip'
runs-on: docker runs-on: docker
container:
image: catthehacker/ubuntu:act-latest
steps: steps:
- uses: actions/checkout@v4 - name: Checkout repository
- uses: pnpm/action-setup@v4 uses: actions/checkout@v4
- uses: actions/setup-node@v4 - name: Setup Node.js
uses: actions/setup-node@v4
with: with:
node-version: 20 node-version: 20
cache: 'pnpm' - name: Install dependencies
- run: pnpm install --frozen-lockfile shell: bash
run: |
corepack enable
pnpm install --frozen-lockfile
env: env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- run: pnpm lint - name: 🧪 Lint
- run: pnpm build shell: bash
run: pnpm lint
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: 🏗️ Build Test
shell: bash
run: pnpm build
env: env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NEXT_PUBLIC_BASE_URL: https://dummy.test NEXT_PUBLIC_BASE_URL: https://dummy.test
@@ -94,70 +173,130 @@ jobs:
needs: prepare needs: prepare
if: needs.prepare.outputs.target != 'skip' if: needs.prepare.outputs.target != 'skip'
runs-on: docker runs-on: docker
container:
image: catthehacker/ubuntu:act-latest
steps: steps:
- uses: actions/checkout@v4 - name: Checkout repository
uses: actions/checkout@v4
- name: 🐳 Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: 🔐 Registry Login - name: 🔐 Registry Login
run: echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login registry.infra.mintel.me -u "${{ secrets.REGISTRY_USERNAME }}" --password-stdin run: echo "${{ secrets.REGISTRY_PASS }}" | docker login registry.infra.mintel.me -u "${{ secrets.REGISTRY_USER }}" --password-stdin
- name: 🏗️ Build and Push - name: 🏗️ Build and Push
shell: bash
run: | run: |
docker build \ docker buildx build \
--pull \
--platform linux/arm64 \
--build-arg NPM_TOKEN=${{ secrets.NPM_TOKEN }} \ --build-arg NPM_TOKEN=${{ secrets.NPM_TOKEN }} \
--build-arg NEXT_PUBLIC_BASE_URL=${{ needs.prepare.outputs.next_public_base_url }} \ --build-arg NEXT_PUBLIC_BASE_URL=${{ needs.prepare.outputs.next_public_base_url }} \
--build-arg NEXT_PUBLIC_TARGET=${{ needs.prepare.outputs.target }} \
--build-arg DIRECTUS_URL=${{ needs.prepare.outputs.directus_url }} \ --build-arg DIRECTUS_URL=${{ needs.prepare.outputs.directus_url }} \
-t registry.infra.mintel.me/mintel/mb-grid-solutions:${{ needs.prepare.outputs.image_tag }} . -t registry.infra.mintel.me/mintel/mb-grid-solutions:${{ needs.prepare.outputs.image_tag }} \
docker push registry.infra.mintel.me/mintel/mb-grid-solutions:${{ needs.prepare.outputs.image_tag }} --push .
deploy: deploy:
name: 🚀 Deploy name: 🚀 Deploy
needs: [prepare, build, qa] needs: [prepare, build, qa]
if: needs.prepare.outputs.target != 'skip' if: needs.prepare.outputs.target != 'skip'
runs-on: docker runs-on: docker
container:
image: catthehacker/ubuntu:act-latest
steps: steps:
- name: 🚀 Deploy via SSH - name: Checkout repository
uses: appleboy/ssh-action@master uses: actions/checkout@v4
with: with:
host: ${{ secrets.SSH_HOST }} fetch-depth: 1
username: root
key: ${{ secrets.SSH_PRIVATE_KEY }} - name: 🚀 Deploy via SSH
script: | shell: bash
run: |
echo "Deploying to alpha.mintel.me"
mkdir -p ~/.ssh
echo "${{ secrets.ALPHA_SSH_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H alpha.mintel.me >> ~/.ssh/known_hosts 2>/dev/null
# Generate Environment File
cat > .env.deploy << 'EOF'
ENV_FILE=${{ needs.prepare.outputs.env_file }}
IMAGE_TAG=${{ needs.prepare.outputs.image_tag }}
TRAEFIK_HOST=${{ needs.prepare.outputs.traefik_host }}
TRAEFIK_RULE=${{ needs.prepare.outputs.traefik_rule }}
GATEKEEPER_RULE=${{ needs.prepare.outputs.gatekeeper_rule }}
GATEKEEPER_HOST=${{ needs.prepare.outputs.gatekeeper_host }}
PROJECT_NAME=${{ needs.prepare.outputs.project_name }}
NEXT_PUBLIC_BASE_URL=${{ needs.prepare.outputs.next_public_base_url }}
# Directus
DIRECTUS_URL=${{ needs.prepare.outputs.directus_url }}
DIRECTUS_HOST=${{ needs.prepare.outputs.directus_host }}
INTERNAL_DIRECTUS_URL=http://directus:8055
DIRECTUS_API_TOKEN=${{ secrets.DIRECTUS_API_TOKEN || vars.DIRECTUS_API_TOKEN }}
DIRECTUS_ADMIN_EMAIL=${{ secrets.DIRECTUS_ADMIN_EMAIL || vars.DIRECTUS_ADMIN_EMAIL || 'admin@mintel.me' }}
DIRECTUS_ADMIN_PASSWORD=${{ secrets.DIRECTUS_ADMIN_PASSWORD || vars.DIRECTUS_ADMIN_PASSWORD }}
DIRECTUS_DB_NAME=${{ secrets.DIRECTUS_DB_NAME || vars.DIRECTUS_DB_NAME || 'directus' }}
DIRECTUS_DB_USER=${{ secrets.DIRECTUS_DB_USER || vars.DIRECTUS_DB_USER || 'directus' }}
DIRECTUS_DB_PASSWORD=${{ secrets.DIRECTUS_DB_PASSWORD || vars.DIRECTUS_DB_PASSWORD }}
DIRECTUS_KEY=${{ secrets.DIRECTUS_KEY || vars.DIRECTUS_KEY }}
DIRECTUS_SECRET=${{ secrets.DIRECTUS_SECRET || vars.DIRECTUS_SECRET }}
# SMTP Config
SMTP_HOST=${{ secrets.SMTP_HOST || vars.SMTP_HOST }}
SMTP_PORT=${{ secrets.SMTP_PORT || vars.SMTP_PORT || '587' }}
SMTP_SECURE=${{ secrets.SMTP_SECURE || vars.SMTP_SECURE || 'false' }}
SMTP_USER=${{ secrets.SMTP_USER || vars.SMTP_USER }}
SMTP_PASS=${{ secrets.SMTP_PASS || vars.SMTP_PASS }}
SMTP_FROM=${{ secrets.SMTP_FROM || vars.SMTP_FROM }}
CONTACT_RECIPIENT=${{ secrets.CONTACT_RECIPIENT || vars.CONTACT_RECIPIENT }}
# Authentication
GATEKEEPER_PASSWORD=${{ secrets.GATEKEEPER_PASSWORD || vars.GATEKEEPER_PASSWORD }}
AUTH_COOKIE_NAME=${{ secrets.AUTH_COOKIE_NAME || vars.AUTH_COOKIE_NAME || 'mintel_gatekeeper_session' }}
COOKIE_DOMAIN=${{ secrets.COOKIE_DOMAIN || vars.COOKIE_DOMAIN || '.mb-grid-solutions.com' }}
# External Services
SENTRY_DSN=${{ secrets.SENTRY_DSN || vars.SENTRY_DSN }}
GOTIFY_URL=${{ secrets.GOTIFY_URL || vars.GOTIFY_URL }}
GOTIFY_TOKEN=${{ secrets.GOTIFY_TOKEN || vars.GOTIFY_TOKEN }}
NEXT_PUBLIC_UMAMI_WEBSITE_ID=${{ secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID || vars.NEXT_PUBLIC_UMAMI_WEBSITE_ID }}
NEXT_PUBLIC_UMAMI_SCRIPT_URL=${{ secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL || vars.NEXT_PUBLIC_UMAMI_SCRIPT_URL }}
# Project
PROJECT_COLOR=${{ secrets.PROJECT_COLOR || vars.PROJECT_COLOR || '#82ed20' }}
EOF
APP_DIR="/home/deploy/sites/mb-grid-solutions.com"
ssh -o StrictHostKeyChecking=accept-new root@alpha.mintel.me "mkdir -p $APP_DIR"
scp -o StrictHostKeyChecking=accept-new .env.deploy root@alpha.mintel.me:$APP_DIR/${{ needs.prepare.outputs.env_file }}
scp -o StrictHostKeyChecking=accept-new docker-compose.yaml root@alpha.mintel.me:$APP_DIR/docker-compose.yaml
ssh -o StrictHostKeyChecking=accept-new root@alpha.mintel.me bash << 'EOF'
set -e
APP_DIR="/home/deploy/sites/mb-grid-solutions.com" APP_DIR="/home/deploy/sites/mb-grid-solutions.com"
mkdir -p $APP_DIR
cd $APP_DIR cd $APP_DIR
# Update Environment echo "${{ secrets.REGISTRY_PASS }}" | docker login registry.infra.mintel.me -u "${{ secrets.REGISTRY_USER }}" --password-stdin
cat > ${{ needs.prepare.outputs.env_file }} << EOF
IMAGE_TAG=${{ needs.prepare.outputs.image_tag }}
TRAEFIK_HOST=${{ needs.prepare.outputs.traefik_host }}
PROJECT_NAME=${{ needs.prepare.outputs.project_name }}
NEXT_PUBLIC_BASE_URL=${{ needs.prepare.outputs.next_public_base_url }}
# Directus
DIRECTUS_URL=${{ needs.prepare.outputs.directus_url }}
DIRECTUS_HOST=${{ needs.prepare.outputs.directus_host }}
DIRECTUS_API_TOKEN=${{ secrets.DIRECTUS_API_TOKEN }}
DIRECTUS_ADMIN_EMAIL=${{ secrets.DIRECTUS_ADMIN_EMAIL || 'admin@mintel.me' }}
DIRECTUS_ADMIN_PASSWORD=${{ secrets.DIRECTUS_ADMIN_PASSWORD }}
DIRECTUS_DB_NAME=${{ secrets.DIRECTUS_DB_NAME || 'directus' }}
DIRECTUS_DB_USER=${{ secrets.DIRECTUS_DB_USER || 'directus' }}
DIRECTUS_DB_PASSWORD=${{ secrets.DIRECTUS_DB_PASSWORD }}
DIRECTUS_KEY=${{ secrets.DIRECTUS_KEY }}
DIRECTUS_SECRET=${{ secrets.DIRECTUS_SECRET }}
EOF
# Sync docker-compose
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login registry.infra.mintel.me -u "${{ secrets.REGISTRY_USERNAME }}" --password-stdin
docker compose -p "${{ needs.prepare.outputs.project_name }}" --env-file ${{ needs.prepare.outputs.env_file }} pull docker compose -p "${{ needs.prepare.outputs.project_name }}" --env-file ${{ needs.prepare.outputs.env_file }} pull
docker compose -p "${{ needs.prepare.outputs.project_name }}" --env-file ${{ needs.prepare.outputs.env_file }} up -d --remove-orphans docker compose -p "${{ needs.prepare.outputs.project_name }}" --env-file ${{ needs.prepare.outputs.env_file }} up -d --remove-orphans
docker system prune -f --filter "until=24h" docker system prune -f --filter "until=24h"
EOF
notifications: notifications:
name: 🔔 Notifications name: 🔔 Notifications
needs: [prepare, deploy] needs: [prepare, deploy]
if: always() if: always()
runs-on: docker runs-on: docker
container:
image: catthehacker/ubuntu:act-latest
steps: steps:
- name: Notify Gotify - name: Notify Gotify
shell: bash
run: | run: |
STATUS="${{ needs.deploy.result }}" STATUS="${{ needs.deploy.result }}"
COLOR="info" COLOR="info"

View File

@@ -3,18 +3,33 @@ FROM registry.infra.mintel.me/mintel/nextjs:latest AS builder
WORKDIR /app WORKDIR /app
# Ensure we are in a clean, standalone environment
RUN rm -rf packages apps pnpm-workspace.yaml 2>/dev/null || true
# Build-time environment variables for Next.js # Build-time environment variables for Next.js
ARG NEXT_PUBLIC_BASE_URL ARG NEXT_PUBLIC_BASE_URL
ARG NEXT_PUBLIC_UMAMI_WEBSITE_ID ARG NEXT_PUBLIC_UMAMI_WEBSITE_ID
ARG NEXT_PUBLIC_UMAMI_SCRIPT_URL ARG NEXT_PUBLIC_UMAMI_SCRIPT_URL
ARG NEXT_PUBLIC_TARGET ARG NEXT_PUBLIC_TARGET
ARG DIRECTUS_URL ARG DIRECTUS_URL
ARG NPM_TOKEN
ENV NEXT_PUBLIC_BASE_URL=$NEXT_PUBLIC_BASE_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_WEBSITE_ID=$NEXT_PUBLIC_UMAMI_WEBSITE_ID
ENV NEXT_PUBLIC_UMAMI_SCRIPT_URL=$NEXT_PUBLIC_UMAMI_SCRIPT_URL ENV NEXT_PUBLIC_UMAMI_SCRIPT_URL=$NEXT_PUBLIC_UMAMI_SCRIPT_URL
ENV NEXT_PUBLIC_TARGET=$NEXT_PUBLIC_TARGET ENV NEXT_PUBLIC_TARGET=$NEXT_PUBLIC_TARGET
ENV DIRECTUS_URL=$DIRECTUS_URL ENV DIRECTUS_URL=$DIRECTUS_URL
ENV NPM_TOKEN=$NPM_TOKEN
ENV SENTRY_SUPPRESS_TURBOPACK_WARNING=1
# Enable corepack
RUN corepack enable
# Copy package files
COPY package.json pnpm-lock.yaml* .npmrc ./
# Install dependencies
RUN pnpm install --no-frozen-lockfile
# Copy local files # Copy local files
COPY . . COPY . .

View File

@@ -143,25 +143,27 @@ export default function About() {
</Reveal> </Reveal>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{t.raw("manifest.items").map((item: any, i: number) => { {t
const Icon = manifestIcons[i]; .raw("manifest.items")
return ( .map((item: { title: string; desc: string }, i: number) => {
<Reveal key={i} delay={i * 0.1}> const Icon = manifestIcons[i];
<div className="bg-white/5 p-10 rounded-3xl border border-white/10 group hover:-translate-y-1 transition-[box-shadow,transform] duration-300 h-full motion-fix relative overflow-hidden"> return (
<div className="absolute top-0 left-0 w-full h-1 bg-accent/0 group-hover:bg-accent/50 transition-all duration-500" /> <Reveal key={i} delay={i * 0.1}>
<div className="text-accent mb-6"> <div className="bg-white/5 p-10 rounded-3xl border border-white/10 group hover:-translate-y-1 transition-[box-shadow,transform] duration-300 h-full motion-fix relative overflow-hidden">
<Icon size={32} /> <div className="absolute top-0 left-0 w-full h-1 bg-accent/0 group-hover:bg-accent/50 transition-all duration-500" />
<div className="text-accent mb-6">
<Icon size={32} />
</div>
<h4 className="text-xl font-bold text-white mb-4">
{i + 1}. {item.title}
</h4>
<p className="text-slate-400 leading-relaxed">
{item.desc}
</p>
</div> </div>
<h4 className="text-xl font-bold text-white mb-4"> </Reveal>
{i + 1}. {item.title} );
</h4> })}
<p className="text-slate-400 leading-relaxed">
{item.desc}
</p>
</div>
</Reveal>
);
})}
</div> </div>
</div> </div>
</section> </section>

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import React, { useRef, useState } from "react"; import React, { useState } from "react";
import { m, LazyMotion, domAnimation } from "framer-motion"; import { m, LazyMotion, domAnimation } from "framer-motion";
import Link from "next/link"; import Link from "next/link";
import { ArrowRight } from "lucide-react"; import { ArrowRight } from "lucide-react";

View File

@@ -2,6 +2,7 @@
import React, { useState } from "react"; import React, { useState } from "react";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link";
import { Mail, MapPin, CheckCircle } from "lucide-react"; import { Mail, MapPin, CheckCircle } from "lucide-react";
import { Button } from "./Button"; import { Button } from "./Button";
import { Counter } from "./Counter"; import { Counter } from "./Counter";
@@ -57,7 +58,7 @@ export default function Contact() {
buttonText: t("form.tryAgain") || "Erneut versuchen", buttonText: t("form.tryAgain") || "Erneut versuchen",
}); });
} }
} catch (error) { } catch {
setStatusModal({ setStatusModal({
isOpen: true, isOpen: true,
type: "error", type: "error",
@@ -279,12 +280,12 @@ export default function Contact() {
<p className="text-xs text-slate-400 text-center"> <p className="text-xs text-slate-400 text-center">
{t.rich("form.privacyNote", { {t.rich("form.privacyNote", {
link: (chunks) => ( link: (chunks) => (
<a <Link
href="/datenschutz" href="/datenschutz"
className="text-accent hover:underline font-semibold" className="text-accent hover:underline font-semibold"
> >
{chunks} {chunks}
</a> </Link>
), ),
})} })}
</p> </p>

View File

@@ -52,10 +52,10 @@ const Layout = ({ children }: { children: React.ReactNode }) => {
className="fixed top-0 left-0 right-0 z-[100]" className="fixed top-0 left-0 right-0 z-[100]"
> >
<header <header
className={`transition-all duration-300 flex items-center py-1 ${ className={`transition-all duration-300 flex items-center ${
isScrolled isScrolled
? "bg-white/90 backdrop-blur-lg border-b border-slate-200 shadow-sm" ? "bg-white/90 backdrop-blur-lg border-b border-slate-200 shadow-sm py-2"
: "bg-gradient-to-b from-white/80 via-white/40 to-transparent" : "bg-gradient-to-b from-white/80 via-white/40 to-transparent py-4"
}`} }`}
> >
<div className="container-custom flex justify-between items-center w-full relative z-10"> <div className="container-custom flex justify-between items-center w-full relative z-10">
@@ -65,7 +65,7 @@ const Layout = ({ children }: { children: React.ReactNode }) => {
aria-label={`${t("nav.home")} - Zur Startseite`} aria-label={`${t("nav.home")} - Zur Startseite`}
> >
<div <div
className={`relative transition-all duration-300 ${isScrolled ? "h-[50px] md:h-[80px] w-[120px] md:w-[200px] mt-0 mb-[-10px]" : "h-[80px] md:h-[140px] w-[180px] md:w-[320px] mt-2 md:mt-4 mb-[-20px] md:mb-[-40px]"}`} className={`relative transition-all duration-300 ${isScrolled ? "h-[50px] md:h-[60px] w-[120px] md:w-[150px]" : "h-[70px] md:h-[100px] w-[160px] md:w-[240px]"}`}
> >
<Image <Image
src="/assets/logo.png" src="/assets/logo.png"

View File

@@ -8,39 +8,49 @@ services:
- ${ENV_FILE:-.env} - ${ENV_FILE:-.env}
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
- "traefik.http.routers.${PROJECT_NAME:-mb-grid-solutions}.rule=Host(`${TRAEFIK_HOST:-mb-grid-solutions.localhost}`)" - "traefik.http.routers.${PROJECT_NAME}.rule=${TRAEFIK_RULE:-Host(`${TRAEFIK_HOST:-mb-grid-solutions.localhost}`)}"
- "traefik.http.routers.${PROJECT_NAME:-mb-grid-solutions}.entrypoints=websecure" - "traefik.http.routers.${PROJECT_NAME}.entrypoints=websecure"
- "traefik.http.routers.${PROJECT_NAME:-mb-grid-solutions}.tls.certresolver=le" - "traefik.http.routers.${PROJECT_NAME}.tls.certresolver=le"
- "traefik.http.routers.${PROJECT_NAME:-mb-grid-solutions}.tls=true" - "traefik.http.routers.${PROJECT_NAME}.tls=true"
- "traefik.http.services.${PROJECT_NAME:-mb-grid-solutions}.loadbalancer.server.port=3000" - "traefik.http.services.${PROJECT_NAME}.loadbalancer.server.port=3000"
- "traefik.http.routers.${PROJECT_NAME:-mb-grid-solutions}.middlewares=${PROJECT_NAME:-mb-grid-solutions}-auth" - "traefik.http.routers.${PROJECT_NAME}.middlewares=${PROJECT_NAME}-auth"
- "traefik.docker.network=infra"
# Gatekeeper Router # Gatekeeper Router (Shared Host + dedicated Subdomain)
- "traefik.http.routers.${PROJECT_NAME:-mb-grid-solutions}-gatekeeper.rule=Host(`${TRAEFIK_HOST:-mb-grid-solutions.localhost}`) && PathPrefix(`/gatekeeper`)" - "traefik.http.routers.${PROJECT_NAME}-gatekeeper.rule=${GATEKEEPER_RULE:-(Host(`${TRAEFIK_HOST:-mb-grid-solutions.localhost}`) && PathPrefix(`/gatekeeper`)) || Host(`gatekeeper.${TRAEFIK_HOST:-mb-grid-solutions.localhost}`)}"
- "traefik.http.routers.${PROJECT_NAME:-mb-grid-solutions}-gatekeeper.entrypoints=websecure" - "traefik.http.routers.${PROJECT_NAME}-gatekeeper.entrypoints=websecure"
- "traefik.http.routers.${PROJECT_NAME:-mb-grid-solutions}-gatekeeper.tls.certresolver=le" - "traefik.http.routers.${PROJECT_NAME}-gatekeeper.tls.certresolver=le"
- "traefik.http.routers.${PROJECT_NAME:-mb-grid-solutions}-gatekeeper.tls=true" - "traefik.http.routers.${PROJECT_NAME}-gatekeeper.tls=true"
- "traefik.http.routers.${PROJECT_NAME:-mb-grid-solutions}-gatekeeper.service=${PROJECT_NAME:-mb-grid-solutions}-gatekeeper" - "traefik.http.routers.${PROJECT_NAME}-gatekeeper.service=${PROJECT_NAME}-gatekeeper"
# Auth Middleware Definition - "traefik.http.middlewares.${PROJECT_NAME}-auth.forwardauth.address=http://${PROJECT_NAME}-gatekeeper:3000/api/verify"
- "traefik.http.middlewares.${PROJECT_NAME:-mb-grid-solutions}-auth.forwardauth.address=http://gatekeeper:3000/api/verify" - "traefik.http.middlewares.${PROJECT_NAME}-auth.forwardauth.trustForwardHeader=true"
- "traefik.http.middlewares.${PROJECT_NAME:-mb-grid-solutions}-auth.forwardauth.trustForwardHeader=true" - "traefik.http.middlewares.${PROJECT_NAME}-auth.forwardauth.authResponseHeaders=X-Auth-User"
- "traefik.http.middlewares.${PROJECT_NAME:-mb-grid-solutions}-auth.forwardauth.authResponseHeaders=X-Auth-User" - "traefik.docker.network=infra"
gatekeeper: gatekeeper:
image: registry.infra.mintel.me/mintel/gatekeeper:latest image: registry.infra.mintel.me/mintel/gatekeeper:latest
container_name: ${PROJECT_NAME:-mb-grid-solutions}-gatekeeper
restart: always restart: always
networks: networks:
- infra infra:
aliases:
- ${PROJECT_NAME:-mb-grid-solutions}-gatekeeper
env_file: env_file:
- ${ENV_FILE:-.env} - ${ENV_FILE:-.env}
environment: environment:
PORT: 3000 PORT: ${PORT:-3000}
PROJECT_NAME: "MB Grid Solutions" PROJECT_NAME: ${PROJECT_NAME:-MB Grid Solutions}
PROJECT_COLOR: "#82ed20" PROJECT_COLOR: ${PROJECT_COLOR:-#82ed20}
COOKIE_DOMAIN: ${COOKIE_DOMAIN:-.mb-grid-solutions.com}
AUTH_COOKIE_NAME: ${AUTH_COOKIE_NAME:-mintel_gatekeeper_session}
GATEKEEPER_PASSWORD: ${GATEKEEPER_PASSWORD:-mintel}
# Dedicated Base URL for Gatekeeper subdomain to prevent redirect loops
NEXT_PUBLIC_BASE_URL: https://${GATEKEEPER_HOST:-gatekeeper.mb-grid-solutions.localhost}
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
- "traefik.http.services.${PROJECT_NAME:-mb-grid-solutions}-gatekeeper.loadbalancer.server.port=3000" - "traefik.http.services.${PROJECT_NAME}-gatekeeper.loadbalancer.server.port=3000"
- "traefik.docker.network=infra"
directus: directus:
image: directus/directus:11 image: directus/directus:11
@@ -63,17 +73,23 @@ services:
DB_DATABASE: ${DIRECTUS_DB_NAME:-directus} DB_DATABASE: ${DIRECTUS_DB_NAME:-directus}
DB_USER: ${DIRECTUS_DB_USER:-directus} DB_USER: ${DIRECTUS_DB_USER:-directus}
DB_PASSWORD: ${DIRECTUS_DB_PASSWORD:-directus} DB_PASSWORD: ${DIRECTUS_DB_PASSWORD:-directus}
# Telemetry & Performance
LOGGER_LEVEL: ${LOG_LEVEL:-info}
SENTRY_DSN: ${SENTRY_DSN}
SENTRY_ENVIRONMENT: ${TARGET:-development}
volumes: volumes:
- ./directus/uploads:/directus/uploads - ./directus/uploads:/directus/uploads
- ./directus/extensions:/directus/extensions - ./directus/extensions:/directus/extensions
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
- "traefik.http.routers.${PROJECT_NAME:-mb-grid-solutions}-directus.rule=Host(`${DIRECTUS_HOST:-cms.mb-grid-solutions.localhost}`)" - "traefik.http.routers.${PROJECT_NAME}-directus.rule=Host(`${DIRECTUS_HOST:-cms.mb-grid-solutions.localhost}`)"
- "traefik.http.routers.${PROJECT_NAME:-mb-grid-solutions}-directus.entrypoints=websecure" - "traefik.http.routers.${PROJECT_NAME}-directus.entrypoints=websecure"
- "traefik.http.routers.${PROJECT_NAME:-mb-grid-solutions}-directus.tls.certresolver=le" - "traefik.http.routers.${PROJECT_NAME}-directus.tls.certresolver=le"
- "traefik.http.routers.${PROJECT_NAME:-mb-grid-solutions}-directus.tls=true" - "traefik.http.routers.${PROJECT_NAME}-directus.tls=true"
- "traefik.http.routers.${PROJECT_NAME:-mb-grid-solutions}-directus.middlewares=${PROJECT_NAME:-mb-grid-solutions}-auth" - "traefik.http.routers.${PROJECT_NAME}-directus.middlewares=${PROJECT_NAME}-forward,compress"
- "traefik.http.services.${PROJECT_NAME:-mb-grid-solutions}-directus.loadbalancer.server.port=8055" - "traefik.http.services.${PROJECT_NAME}-directus.loadbalancer.server.port=8055"
- "traefik.http.middlewares.${PROJECT_NAME}-forward.headers.customrequestheaders.X-Forwarded-Proto=https"
- "traefik.docker.network=infra"
directus-db: directus-db:
image: postgres:15-alpine image: postgres:15-alpine

View File

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

View File

@@ -1,7 +1,7 @@
import type { CacheService } from "./cache-service"; import type { CacheService } from "./cache-service";
export class MemoryCacheService implements CacheService { export class MemoryCacheService implements CacheService {
private cache = new Map<string, { value: any; expiry: number | null }>(); private cache = new Map<string, { value: unknown; expiry: number | null }>();
async get<T>(key: string): Promise<T | null> { async get<T>(key: string): Promise<T | null> {
const item = this.cache.get(key); const item = this.cache.get(key);

View File

@@ -2,21 +2,22 @@
"name": "mb-grid-solutions.com", "name": "mb-grid-solutions.com",
"version": "1.0.0", "version": "1.0.0",
"type": "module", "type": "module",
"packageManager": "pnpm@10.18.3",
"scripts": { "scripts": {
"dev": "docker network create infra 2>/dev/null || true && echo '\\n🚀 Development Environment Starting...\\n\\n📱 App: http://mb-grid-solutions.localhost\\n🗄 CMS: http://cms.mb-grid-solutions.localhost/admin\\n🚦 Traefik: http://localhost:8080\\n\\n(Press Ctrl+C to stop)\\n' && docker compose down --remove-orphans && docker compose up app directus directus-db", "dev": "docker network create infra 2>/dev/null || true && echo '\\n🚀 Development Environment Starting...\\n\\n📱 App: http://mb-grid-solutions.localhost\\n🗄 CMS: http://cms.mb-grid-solutions.localhost/admin\\n🚦 Traefik: http://localhost:8080\\n\\n(Press Ctrl+C to stop)\\n' && docker compose down --remove-orphans && docker compose up app directus directus-db",
"dev:next": "next dev", "dev:next": "next dev",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint", "lint": "eslint app components lib scripts",
"test": "vitest", "test": "vitest",
"prepare": "husky", "prepare": "husky",
"directus:bootstrap": "DIRECTUS_URL=http://localhost:8055 npx tsx --env-file=.env scripts/setup-directus.ts", "cms:bootstrap": "DIRECTUS_URL=http://localhost:8055 npx tsx --env-file=.env scripts/setup-directus.ts",
"directus:push:staging": "./scripts/sync-directus.sh push staging", "cms:push:staging": "./scripts/sync-directus.sh push staging",
"directus:pull:staging": "./scripts/sync-directus.sh pull staging", "cms:pull:staging": "./scripts/sync-directus.sh pull staging",
"directus:push:testing": "./scripts/sync-directus.sh push testing", "cms:push:testing": "./scripts/sync-directus.sh push testing",
"directus:pull:testing": "./scripts/sync-directus.sh pull testing", "cms:pull:testing": "./scripts/sync-directus.sh pull testing",
"directus:push:prod": "./scripts/sync-directus.sh push production", "cms:push:prod": "./scripts/sync-directus.sh push production",
"directus:pull:prod": "./scripts/sync-directus.sh pull production", "cms:pull:prod": "./scripts/sync-directus.sh pull production",
"pagespeed:test": "mintel pagespeed test" "pagespeed:test": "mintel pagespeed test"
}, },
"keywords": [], "keywords": [],
@@ -24,8 +25,8 @@
"license": "ISC", "license": "ISC",
"description": "", "description": "",
"dependencies": { "dependencies": {
"@mintel/next-config": "1.1.13", "@mintel/next-config": "^1.1.13",
"@mintel/next-utils": "1.1.13", "@mintel/next-utils": "^1.1.13",
"@sentry/nextjs": "^10.38.0", "@sentry/nextjs": "^10.38.0",
"framer-motion": "^12.29.2", "framer-motion": "^12.29.2",
"lucide-react": "^0.562.0", "lucide-react": "^0.562.0",
@@ -41,10 +42,10 @@
"@commitlint/cli": "^20.4.0", "@commitlint/cli": "^20.4.0",
"@commitlint/config-conventional": "^20.4.0", "@commitlint/config-conventional": "^20.4.0",
"@directus/sdk": "^21.0.0", "@directus/sdk": "^21.0.0",
"@mintel/cli": "1.1.13", "@mintel/cli": "^1.1.13",
"@mintel/eslint-config": "1.1.13", "@mintel/eslint-config": "^1.1.13",
"@mintel/husky-config": "1.1.13", "@mintel/husky-config": "^1.1.13",
"@mintel/tsconfig": "1.1.13", "@mintel/tsconfig": "^1.1.13",
"@tailwindcss/postcss": "^4.1.18", "@tailwindcss/postcss": "^4.1.18",
"@testing-library/jest-dom": "^6.9.1", "@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2", "@testing-library/react": "^16.3.2",
@@ -54,8 +55,8 @@
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.2", "@vitejs/plugin-react": "^5.1.2",
"autoprefixer": "^10.4.23", "autoprefixer": "^10.4.23",
"eslint": "^9.39.2", "eslint": "^8.57.1",
"eslint-config-next": "^16.1.6", "eslint-config-next": "15.1.6",
"husky": "^9.1.7", "husky": "^9.1.7",
"jsdom": "^27.4.0", "jsdom": "^27.4.0",
"lint-staged": "^16.2.7", "lint-staged": "^16.2.7",

457
pnpm-lock.yaml generated
View File

@@ -9,10 +9,10 @@ importers:
.: .:
dependencies: dependencies:
'@mintel/next-config': '@mintel/next-config':
specifier: 1.1.13 specifier: ^1.1.13
version: 1.1.13(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3)(webpack@5.104.1) version: 1.1.13(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3)(webpack@5.104.1)
'@mintel/next-utils': '@mintel/next-utils':
specifier: 1.1.13 specifier: ^1.1.13
version: 1.1.13(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) version: 1.1.13(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
'@sentry/nextjs': '@sentry/nextjs':
specifier: ^10.38.0 specifier: ^10.38.0
@@ -55,16 +55,16 @@ importers:
specifier: ^21.0.0 specifier: ^21.0.0
version: 21.0.0 version: 21.0.0
'@mintel/cli': '@mintel/cli':
specifier: 1.1.13 specifier: ^1.1.13
version: 1.1.13 version: 1.1.13
'@mintel/eslint-config': '@mintel/eslint-config':
specifier: 1.1.13 specifier: ^1.1.13
version: 1.1.13(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) version: 1.1.13(eslint@8.57.1)(typescript@5.9.3)
'@mintel/husky-config': '@mintel/husky-config':
specifier: 1.1.13 specifier: ^1.1.13
version: 1.1.13 version: 1.1.13
'@mintel/tsconfig': '@mintel/tsconfig':
specifier: 1.1.13 specifier: ^1.1.13
version: 1.1.13 version: 1.1.13
'@tailwindcss/postcss': '@tailwindcss/postcss':
specifier: ^4.1.18 specifier: ^4.1.18
@@ -94,11 +94,11 @@ importers:
specifier: ^10.4.23 specifier: ^10.4.23
version: 10.4.24(postcss@8.5.6) version: 10.4.24(postcss@8.5.6)
eslint: eslint:
specifier: ^9.39.2 specifier: ^8.57.1
version: 9.39.2(jiti@2.6.1) version: 8.57.1
eslint-config-next: eslint-config-next:
specifier: ^16.1.6 specifier: 15.1.6
version: 16.1.6(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) version: 15.1.6(eslint@8.57.1)(typescript@5.9.3)
husky: husky:
specifier: ^9.1.7 specifier: ^9.1.7
version: 9.1.7 version: 9.1.7
@@ -520,34 +520,22 @@ packages:
resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
'@eslint/config-array@0.21.1': '@eslint/eslintrc@2.1.4':
resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
'@eslint/config-helpers@0.4.2':
resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/core@0.17.0':
resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/eslintrc@3.3.3': '@eslint/eslintrc@3.3.3':
resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/js@8.57.1':
resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
'@eslint/js@9.39.2': '@eslint/js@9.39.2':
resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/object-schema@2.1.7':
resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/plugin-kit@0.4.1':
resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@exodus/bytes@1.11.0': '@exodus/bytes@1.11.0':
resolution: {integrity: sha512-wO3vd8nsEHdumsXrjGO/v4p6irbg7hy9kvIeR6i2AwylZSk4HJdWgL0FNaVquW1+AweJcdvU1IEpuIWk/WaPnA==} resolution: {integrity: sha512-wO3vd8nsEHdumsXrjGO/v4p6irbg7hy9kvIeR6i2AwylZSk4HJdWgL0FNaVquW1+AweJcdvU1IEpuIWk/WaPnA==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
@@ -575,21 +563,18 @@ packages:
'@formatjs/intl-localematcher@0.8.1': '@formatjs/intl-localematcher@0.8.1':
resolution: {integrity: sha512-xwEuwQFdtSq1UKtQnyTZWC+eHdv7Uygoa+H2k/9uzBVQjDyp9r20LNDNKedWXll7FssT3GRHvqsdJGYSUWqYFA==} resolution: {integrity: sha512-xwEuwQFdtSq1UKtQnyTZWC+eHdv7Uygoa+H2k/9uzBVQjDyp9r20LNDNKedWXll7FssT3GRHvqsdJGYSUWqYFA==}
'@humanfs/core@0.19.1': '@humanwhocodes/config-array@0.13.0':
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==}
engines: {node: '>=18.18.0'} engines: {node: '>=10.10.0'}
deprecated: Use @eslint/config-array instead
'@humanfs/node@0.16.7':
resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==}
engines: {node: '>=18.18.0'}
'@humanwhocodes/module-importer@1.0.1': '@humanwhocodes/module-importer@1.0.1':
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
engines: {node: '>=12.22'} engines: {node: '>=12.22'}
'@humanwhocodes/retry@0.4.3': '@humanwhocodes/object-schema@2.0.3':
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==}
engines: {node: '>=18.18'} deprecated: Use @eslint/object-schema instead
'@img/colour@1.0.0': '@img/colour@1.0.0':
resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==}
@@ -887,9 +872,6 @@ packages:
'@next/eslint-plugin-next@15.1.6': '@next/eslint-plugin-next@15.1.6':
resolution: {integrity: sha512-+slMxhTgILUntZDGNgsKEYHUvpn72WP1YTlkmEhS51vnVd7S9jEEy0n9YAMcI21vUG4akTw9voWH02lrClt/yw==} resolution: {integrity: sha512-+slMxhTgILUntZDGNgsKEYHUvpn72WP1YTlkmEhS51vnVd7S9jEEy0n9YAMcI21vUG4akTw9voWH02lrClt/yw==}
'@next/eslint-plugin-next@16.1.6':
resolution: {integrity: sha512-/Qq3PTagA6+nYVfryAtQ7/9FEr/6YVyvOtl6rZnGsbReGLf0jZU6gkpr1FuChAQpvV46a78p4cmHOVP8mbfSMQ==}
'@next/swc-darwin-arm64@15.1.6': '@next/swc-darwin-arm64@15.1.6':
resolution: {integrity: sha512-u7lg4Mpl9qWpKgy6NzEkz/w0/keEHtOybmIl0ykgItBxEM5mYotS5PmqTpo+Rhg8FiOiWgwr8USxmKQkqLBCrw==} resolution: {integrity: sha512-u7lg4Mpl9qWpKgy6NzEkz/w0/keEHtOybmIl0ykgItBxEM5mYotS5PmqTpo+Rhg8FiOiWgwr8USxmKQkqLBCrw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@@ -2245,6 +2227,9 @@ packages:
resolution: {integrity: sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==} resolution: {integrity: sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@ungap/structured-clone@1.3.0':
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
'@unrs/resolver-binding-android-arm-eabi@1.11.1': '@unrs/resolver-binding-android-arm-eabi@1.11.1':
resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==}
cpu: [arm] cpu: [arm]
@@ -2838,6 +2823,10 @@ packages:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
doctrine@3.0.0:
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
engines: {node: '>=6.0.0'}
dom-accessibility-api@0.5.16: dom-accessibility-api@0.5.16:
resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==}
@@ -2957,15 +2946,6 @@ packages:
typescript: typescript:
optional: true optional: true
eslint-config-next@16.1.6:
resolution: {integrity: sha512-vKq40io2B0XtkkNDYyleATwblNt8xuh3FWp8SpSz3pt7P01OkBFlKsJZ2mWt5WsCySlDQLckb1zMY9yE9Qy0LA==}
peerDependencies:
eslint: '>=9.0.0'
typescript: '>=3.3.1'
peerDependenciesMeta:
typescript:
optional: true
eslint-import-resolver-node@0.3.9: eslint-import-resolver-node@0.3.9:
resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
@@ -3025,12 +3005,6 @@ packages:
peerDependencies: peerDependencies:
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0
eslint-plugin-react-hooks@7.0.1:
resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==}
engines: {node: '>=18'}
peerDependencies:
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0
eslint-plugin-react@7.37.5: eslint-plugin-react@7.37.5:
resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==}
engines: {node: '>=4'} engines: {node: '>=4'}
@@ -3041,9 +3015,9 @@ packages:
resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
engines: {node: '>=8.0.0'} engines: {node: '>=8.0.0'}
eslint-scope@8.4.0: eslint-scope@7.2.2:
resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
eslint-visitor-keys@3.4.3: eslint-visitor-keys@3.4.3:
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
@@ -3053,20 +3027,20 @@ packages:
resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
eslint@9.39.2: eslint@8.57.1:
resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options.
hasBin: true hasBin: true
peerDependencies:
jiti: '*'
peerDependenciesMeta:
jiti:
optional: true
espree@10.4.0: espree@10.4.0:
resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
espree@9.6.1:
resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
esquery@1.7.0: esquery@1.7.0:
resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==}
engines: {node: '>=0.10'} engines: {node: '>=0.10'}
@@ -3138,9 +3112,9 @@ packages:
picomatch: picomatch:
optional: true optional: true
file-entry-cache@8.0.0: file-entry-cache@6.0.1:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
engines: {node: '>=16.0.0'} engines: {node: ^10.12.0 || >=12.0.0}
fill-range@7.1.1: fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
@@ -3150,9 +3124,9 @@ packages:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'} engines: {node: '>=10'}
flat-cache@4.0.1: flat-cache@3.2.0:
resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
engines: {node: '>=16'} engines: {node: ^10.12.0 || >=12.0.0}
flatted@3.3.3: flatted@3.3.3:
resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
@@ -3259,23 +3233,26 @@ packages:
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
hasBin: true hasBin: true
glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
deprecated: Glob versions prior to v9 are no longer supported
glob@9.3.5: glob@9.3.5:
resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==}
engines: {node: '>=16 || 14 >=14.17'} engines: {node: '>=16 || 14 >=14.17'}
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
global-directory@4.0.1: global-directory@4.0.1:
resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==}
engines: {node: '>=18'} engines: {node: '>=18'}
globals@13.24.0:
resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
engines: {node: '>=8'}
globals@14.0.0: globals@14.0.0:
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
globals@16.4.0:
resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==}
engines: {node: '>=18'}
globalthis@1.0.4: globalthis@1.0.4:
resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -3287,6 +3264,9 @@ packages:
graceful-fs@4.2.11: graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
graphemer@1.4.0:
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
has-bigints@1.1.0: has-bigints@1.1.0:
resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -3317,12 +3297,6 @@ packages:
help-me@5.0.0: help-me@5.0.0:
resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==}
hermes-estree@0.25.1:
resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==}
hermes-parser@0.25.1:
resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==}
hoist-non-react-statics@3.3.2: hoist-non-react-statics@3.3.2:
resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
@@ -3379,6 +3353,13 @@ packages:
resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
engines: {node: '>=8'} engines: {node: '>=8'}
inflight@1.0.6:
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
ini@4.1.1: ini@4.1.1:
resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
@@ -3479,6 +3460,10 @@ packages:
resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==}
engines: {node: '>=8'} engines: {node: '>=8'}
is-path-inside@3.0.3:
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
engines: {node: '>=8'}
is-plain-obj@4.1.0: is-plain-obj@4.1.0:
resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
engines: {node: '>=12'} engines: {node: '>=12'}
@@ -4024,6 +4009,10 @@ packages:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'} engines: {node: '>=8'}
path-is-absolute@1.0.1:
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
engines: {node: '>=0.10.0'}
path-key@3.1.1: path-key@3.1.1:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -4244,6 +4233,11 @@ packages:
rfdc@1.4.1: rfdc@1.4.1:
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
rimraf@3.0.2:
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
deprecated: Rimraf versions prior to v4 are no longer supported
hasBin: true
rollup@3.29.5: rollup@3.29.5:
resolution: {integrity: sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==} resolution: {integrity: sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==}
engines: {node: '>=14.18.0', npm: '>=8.0.0'} engines: {node: '>=14.18.0', npm: '>=8.0.0'}
@@ -4528,6 +4522,9 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
hasBin: true hasBin: true
text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
thread-stream@4.0.0: thread-stream@4.0.0:
resolution: {integrity: sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==} resolution: {integrity: sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==}
engines: {node: '>=20'} engines: {node: '>=20'}
@@ -4585,6 +4582,10 @@ packages:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
type-fest@0.20.2:
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
engines: {node: '>=10'}
type-fest@0.7.1: type-fest@0.7.1:
resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==} resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -4865,12 +4866,6 @@ packages:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'} engines: {node: '>=10'}
zod-validation-error@4.0.2:
resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==}
engines: {node: '>=18.0.0'}
peerDependencies:
zod: ^3.25.0 || ^4.0.0
zod@3.25.76: zod@3.25.76:
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
@@ -5254,29 +5249,27 @@ snapshots:
'@esbuild/win32-x64@0.27.2': '@esbuild/win32-x64@0.27.2':
optional: true optional: true
'@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@2.6.1))': '@eslint-community/eslint-utils@4.9.1(eslint@8.57.1)':
dependencies: dependencies:
eslint: 9.39.2(jiti@2.6.1) eslint: 8.57.1
eslint-visitor-keys: 3.4.3 eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.2': {} '@eslint-community/regexpp@4.12.2': {}
'@eslint/config-array@0.21.1': '@eslint/eslintrc@2.1.4':
dependencies: dependencies:
'@eslint/object-schema': 2.1.7 ajv: 6.12.6
debug: 4.4.3 debug: 4.4.3
espree: 9.6.1
globals: 13.24.0
ignore: 5.3.2
import-fresh: 3.3.1
js-yaml: 4.1.1
minimatch: 3.1.2 minimatch: 3.1.2
strip-json-comments: 3.1.1
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@eslint/config-helpers@0.4.2':
dependencies:
'@eslint/core': 0.17.0
'@eslint/core@0.17.0':
dependencies:
'@types/json-schema': 7.0.15
'@eslint/eslintrc@3.3.3': '@eslint/eslintrc@3.3.3':
dependencies: dependencies:
ajv: 6.12.6 ajv: 6.12.6
@@ -5291,15 +5284,10 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@eslint/js@8.57.1': {}
'@eslint/js@9.39.2': {} '@eslint/js@9.39.2': {}
'@eslint/object-schema@2.1.7': {}
'@eslint/plugin-kit@0.4.1':
dependencies:
'@eslint/core': 0.17.0
levn: 0.4.1
'@exodus/bytes@1.11.0': {} '@exodus/bytes@1.11.0': {}
'@formatjs/ecma402-abstract@3.1.1': '@formatjs/ecma402-abstract@3.1.1':
@@ -5333,16 +5321,17 @@ snapshots:
'@formatjs/fast-memoize': 3.1.0 '@formatjs/fast-memoize': 3.1.0
tslib: 2.8.1 tslib: 2.8.1
'@humanfs/core@0.19.1': {} '@humanwhocodes/config-array@0.13.0':
'@humanfs/node@0.16.7':
dependencies: dependencies:
'@humanfs/core': 0.19.1 '@humanwhocodes/object-schema': 2.0.3
'@humanwhocodes/retry': 0.4.3 debug: 4.4.3
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
'@humanwhocodes/module-importer@1.0.1': {} '@humanwhocodes/module-importer@1.0.1': {}
'@humanwhocodes/retry@0.4.3': {} '@humanwhocodes/object-schema@2.0.3': {}
'@img/colour@1.0.0': '@img/colour@1.0.0':
optional: true optional: true
@@ -5556,12 +5545,12 @@ snapshots:
fs-extra: 11.3.3 fs-extra: 11.3.3
prompts: 2.4.2 prompts: 2.4.2
'@mintel/eslint-config@1.1.13(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': '@mintel/eslint-config@1.1.13(eslint@8.57.1)(typescript@5.9.3)':
dependencies: dependencies:
'@eslint/eslintrc': 3.3.3 '@eslint/eslintrc': 3.3.3
'@eslint/js': 9.39.2 '@eslint/js': 9.39.2
eslint-config-next: 15.1.6(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint-config-next: 15.1.6(eslint@8.57.1)(typescript@5.9.3)
typescript-eslint: 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) typescript-eslint: 8.54.0(eslint@8.57.1)(typescript@5.9.3)
transitivePeerDependencies: transitivePeerDependencies:
- eslint - eslint
- eslint-import-resolver-webpack - eslint-import-resolver-webpack
@@ -5625,10 +5614,6 @@ snapshots:
dependencies: dependencies:
fast-glob: 3.3.1 fast-glob: 3.3.1
'@next/eslint-plugin-next@16.1.6':
dependencies:
fast-glob: 3.3.1
'@next/swc-darwin-arm64@15.1.6': '@next/swc-darwin-arm64@15.1.6':
optional: true optional: true
@@ -7060,7 +7045,7 @@ snapshots:
'@types/pg-pool@2.0.6': '@types/pg-pool@2.0.6':
dependencies: dependencies:
'@types/pg': 8.6.1 '@types/pg': 8.15.6
'@types/pg-pool@2.0.7': '@types/pg-pool@2.0.7':
dependencies: dependencies:
@@ -7092,15 +7077,15 @@ snapshots:
dependencies: dependencies:
'@types/node': 25.2.0 '@types/node': 25.2.0
'@typescript-eslint/eslint-plugin@8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': '@typescript-eslint/eslint-plugin@8.54.0(@typescript-eslint/parser@8.54.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)':
dependencies: dependencies:
'@eslint-community/regexpp': 4.12.2 '@eslint-community/regexpp': 4.12.2
'@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': 8.54.0(eslint@8.57.1)(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.54.0 '@typescript-eslint/scope-manager': 8.54.0
'@typescript-eslint/type-utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/type-utils': 8.54.0(eslint@8.57.1)(typescript@5.9.3)
'@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/utils': 8.54.0(eslint@8.57.1)(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.54.0 '@typescript-eslint/visitor-keys': 8.54.0
eslint: 9.39.2(jiti@2.6.1) eslint: 8.57.1
ignore: 7.0.5 ignore: 7.0.5
natural-compare: 1.4.0 natural-compare: 1.4.0
ts-api-utils: 2.4.0(typescript@5.9.3) ts-api-utils: 2.4.0(typescript@5.9.3)
@@ -7108,14 +7093,14 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': '@typescript-eslint/parser@8.54.0(eslint@8.57.1)(typescript@5.9.3)':
dependencies: dependencies:
'@typescript-eslint/scope-manager': 8.54.0 '@typescript-eslint/scope-manager': 8.54.0
'@typescript-eslint/types': 8.54.0 '@typescript-eslint/types': 8.54.0
'@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.54.0 '@typescript-eslint/visitor-keys': 8.54.0
debug: 4.4.3 debug: 4.4.3
eslint: 9.39.2(jiti@2.6.1) eslint: 8.57.1
typescript: 5.9.3 typescript: 5.9.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -7138,13 +7123,13 @@ snapshots:
dependencies: dependencies:
typescript: 5.9.3 typescript: 5.9.3
'@typescript-eslint/type-utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': '@typescript-eslint/type-utils@8.54.0(eslint@8.57.1)(typescript@5.9.3)':
dependencies: dependencies:
'@typescript-eslint/types': 8.54.0 '@typescript-eslint/types': 8.54.0
'@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/utils': 8.54.0(eslint@8.57.1)(typescript@5.9.3)
debug: 4.4.3 debug: 4.4.3
eslint: 9.39.2(jiti@2.6.1) eslint: 8.57.1
ts-api-utils: 2.4.0(typescript@5.9.3) ts-api-utils: 2.4.0(typescript@5.9.3)
typescript: 5.9.3 typescript: 5.9.3
transitivePeerDependencies: transitivePeerDependencies:
@@ -7167,13 +7152,13 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': '@typescript-eslint/utils@8.54.0(eslint@8.57.1)(typescript@5.9.3)':
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1)
'@typescript-eslint/scope-manager': 8.54.0 '@typescript-eslint/scope-manager': 8.54.0
'@typescript-eslint/types': 8.54.0 '@typescript-eslint/types': 8.54.0
'@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3)
eslint: 9.39.2(jiti@2.6.1) eslint: 8.57.1
typescript: 5.9.3 typescript: 5.9.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -7183,6 +7168,8 @@ snapshots:
'@typescript-eslint/types': 8.54.0 '@typescript-eslint/types': 8.54.0
eslint-visitor-keys: 4.2.1 eslint-visitor-keys: 4.2.1
'@ungap/structured-clone@1.3.0': {}
'@unrs/resolver-binding-android-arm-eabi@1.11.1': '@unrs/resolver-binding-android-arm-eabi@1.11.1':
optional: true optional: true
@@ -7796,6 +7783,10 @@ snapshots:
dependencies: dependencies:
esutils: 2.0.3 esutils: 2.0.3
doctrine@3.0.0:
dependencies:
esutils: 2.0.3
dom-accessibility-api@0.5.16: {} dom-accessibility-api@0.5.16: {}
dom-accessibility-api@0.6.3: {} dom-accessibility-api@0.6.3: {}
@@ -7984,19 +7975,19 @@ snapshots:
escape-string-regexp@4.0.0: {} escape-string-regexp@4.0.0: {}
eslint-config-next@15.1.6(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): eslint-config-next@15.1.6(eslint@8.57.1)(typescript@5.9.3):
dependencies: dependencies:
'@next/eslint-plugin-next': 15.1.6 '@next/eslint-plugin-next': 15.1.6
'@rushstack/eslint-patch': 1.15.0 '@rushstack/eslint-patch': 1.15.0
'@typescript-eslint/eslint-plugin': 8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/eslint-plugin': 8.54.0(@typescript-eslint/parser@8.54.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)
'@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': 8.54.0(eslint@8.57.1)(typescript@5.9.3)
eslint: 9.39.2(jiti@2.6.1) eslint: 8.57.1
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1)
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1)
eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@8.57.1)
eslint-plugin-react-hooks: 5.2.0(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-react-hooks: 5.2.0(eslint@8.57.1)
optionalDependencies: optionalDependencies:
typescript: 5.9.3 typescript: 5.9.3
transitivePeerDependencies: transitivePeerDependencies:
@@ -8004,26 +7995,6 @@ snapshots:
- eslint-plugin-import-x - eslint-plugin-import-x
- supports-color - supports-color
eslint-config-next@16.1.6(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3):
dependencies:
'@next/eslint-plugin-next': 16.1.6
eslint: 9.39.2(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-react-hooks: 7.0.1(eslint@9.39.2(jiti@2.6.1))
globals: 16.4.0
typescript-eslint: 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
optionalDependencies:
typescript: 5.9.3
transitivePeerDependencies:
- '@typescript-eslint/parser'
- eslint-import-resolver-webpack
- eslint-plugin-import-x
- supports-color
eslint-import-resolver-node@0.3.9: eslint-import-resolver-node@0.3.9:
dependencies: dependencies:
debug: 3.2.7 debug: 3.2.7
@@ -8032,33 +8003,33 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)): eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1):
dependencies: dependencies:
'@nolyfill/is-core-module': 1.0.39 '@nolyfill/is-core-module': 1.0.39
debug: 4.4.3 debug: 4.4.3
eslint: 9.39.2(jiti@2.6.1) eslint: 8.57.1
get-tsconfig: 4.13.1 get-tsconfig: 4.13.1
is-bun-module: 2.0.0 is-bun-module: 2.0.0
stable-hash: 0.0.5 stable-hash: 0.0.5
tinyglobby: 0.2.15 tinyglobby: 0.2.15
unrs-resolver: 1.11.1 unrs-resolver: 1.11.1
optionalDependencies: optionalDependencies:
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)): eslint-module-utils@2.12.1(@typescript-eslint/parser@8.54.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1):
dependencies: dependencies:
debug: 3.2.7 debug: 3.2.7
optionalDependencies: optionalDependencies:
'@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': 8.54.0(eslint@8.57.1)(typescript@5.9.3)
eslint: 9.39.2(jiti@2.6.1) eslint: 8.57.1
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)): eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1):
dependencies: dependencies:
'@rtsao/scc': 1.1.0 '@rtsao/scc': 1.1.0
array-includes: 3.1.9 array-includes: 3.1.9
@@ -8067,9 +8038,9 @@ snapshots:
array.prototype.flatmap: 1.3.3 array.prototype.flatmap: 1.3.3
debug: 3.2.7 debug: 3.2.7
doctrine: 2.1.0 doctrine: 2.1.0
eslint: 9.39.2(jiti@2.6.1) eslint: 8.57.1
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.54.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
hasown: 2.0.2 hasown: 2.0.2
is-core-module: 2.16.1 is-core-module: 2.16.1
is-glob: 4.0.3 is-glob: 4.0.3
@@ -8081,13 +8052,13 @@ snapshots:
string.prototype.trimend: 1.0.9 string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0 tsconfig-paths: 3.15.0
optionalDependencies: optionalDependencies:
'@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': 8.54.0(eslint@8.57.1)(typescript@5.9.3)
transitivePeerDependencies: transitivePeerDependencies:
- eslint-import-resolver-typescript - eslint-import-resolver-typescript
- eslint-import-resolver-webpack - eslint-import-resolver-webpack
- supports-color - supports-color
eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.2(jiti@2.6.1)): eslint-plugin-jsx-a11y@6.10.2(eslint@8.57.1):
dependencies: dependencies:
aria-query: 5.3.2 aria-query: 5.3.2
array-includes: 3.1.9 array-includes: 3.1.9
@@ -8097,7 +8068,7 @@ snapshots:
axobject-query: 4.1.0 axobject-query: 4.1.0
damerau-levenshtein: 1.0.8 damerau-levenshtein: 1.0.8
emoji-regex: 9.2.2 emoji-regex: 9.2.2
eslint: 9.39.2(jiti@2.6.1) eslint: 8.57.1
hasown: 2.0.2 hasown: 2.0.2
jsx-ast-utils: 3.3.5 jsx-ast-utils: 3.3.5
language-tags: 1.0.9 language-tags: 1.0.9
@@ -8106,22 +8077,11 @@ snapshots:
safe-regex-test: 1.1.0 safe-regex-test: 1.1.0
string.prototype.includes: 2.0.1 string.prototype.includes: 2.0.1
eslint-plugin-react-hooks@5.2.0(eslint@9.39.2(jiti@2.6.1)): eslint-plugin-react-hooks@5.2.0(eslint@8.57.1):
dependencies: dependencies:
eslint: 9.39.2(jiti@2.6.1) eslint: 8.57.1
eslint-plugin-react-hooks@7.0.1(eslint@9.39.2(jiti@2.6.1)): eslint-plugin-react@7.37.5(eslint@8.57.1):
dependencies:
'@babel/core': 7.29.0
'@babel/parser': 7.29.0
eslint: 9.39.2(jiti@2.6.1)
hermes-parser: 0.25.1
zod: 4.3.6
zod-validation-error: 4.0.2(zod@4.3.6)
transitivePeerDependencies:
- supports-color
eslint-plugin-react@7.37.5(eslint@9.39.2(jiti@2.6.1)):
dependencies: dependencies:
array-includes: 3.1.9 array-includes: 3.1.9
array.prototype.findlast: 1.2.5 array.prototype.findlast: 1.2.5
@@ -8129,7 +8089,7 @@ snapshots:
array.prototype.tosorted: 1.1.4 array.prototype.tosorted: 1.1.4
doctrine: 2.1.0 doctrine: 2.1.0
es-iterator-helpers: 1.2.2 es-iterator-helpers: 1.2.2
eslint: 9.39.2(jiti@2.6.1) eslint: 8.57.1
estraverse: 5.3.0 estraverse: 5.3.0
hasown: 2.0.2 hasown: 2.0.2
jsx-ast-utils: 3.3.5 jsx-ast-utils: 3.3.5
@@ -8148,7 +8108,7 @@ snapshots:
esrecurse: 4.3.0 esrecurse: 4.3.0
estraverse: 4.3.0 estraverse: 4.3.0
eslint-scope@8.4.0: eslint-scope@7.2.2:
dependencies: dependencies:
esrecurse: 4.3.0 esrecurse: 4.3.0
estraverse: 5.3.0 estraverse: 5.3.0
@@ -8157,44 +8117,46 @@ snapshots:
eslint-visitor-keys@4.2.1: {} eslint-visitor-keys@4.2.1: {}
eslint@9.39.2(jiti@2.6.1): eslint@8.57.1:
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1)
'@eslint-community/regexpp': 4.12.2 '@eslint-community/regexpp': 4.12.2
'@eslint/config-array': 0.21.1 '@eslint/eslintrc': 2.1.4
'@eslint/config-helpers': 0.4.2 '@eslint/js': 8.57.1
'@eslint/core': 0.17.0 '@humanwhocodes/config-array': 0.13.0
'@eslint/eslintrc': 3.3.3
'@eslint/js': 9.39.2
'@eslint/plugin-kit': 0.4.1
'@humanfs/node': 0.16.7
'@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/module-importer': 1.0.1
'@humanwhocodes/retry': 0.4.3 '@nodelib/fs.walk': 1.2.8
'@types/estree': 1.0.8 '@ungap/structured-clone': 1.3.0
ajv: 6.12.6 ajv: 6.12.6
chalk: 4.1.2 chalk: 4.1.2
cross-spawn: 7.0.6 cross-spawn: 7.0.6
debug: 4.4.3 debug: 4.4.3
doctrine: 3.0.0
escape-string-regexp: 4.0.0 escape-string-regexp: 4.0.0
eslint-scope: 8.4.0 eslint-scope: 7.2.2
eslint-visitor-keys: 4.2.1 eslint-visitor-keys: 3.4.3
espree: 10.4.0 espree: 9.6.1
esquery: 1.7.0 esquery: 1.7.0
esutils: 2.0.3 esutils: 2.0.3
fast-deep-equal: 3.1.3 fast-deep-equal: 3.1.3
file-entry-cache: 8.0.0 file-entry-cache: 6.0.1
find-up: 5.0.0 find-up: 5.0.0
glob-parent: 6.0.2 glob-parent: 6.0.2
globals: 13.24.0
graphemer: 1.4.0
ignore: 5.3.2 ignore: 5.3.2
imurmurhash: 0.1.4 imurmurhash: 0.1.4
is-glob: 4.0.3 is-glob: 4.0.3
is-path-inside: 3.0.3
js-yaml: 4.1.1
json-stable-stringify-without-jsonify: 1.0.1 json-stable-stringify-without-jsonify: 1.0.1
levn: 0.4.1
lodash.merge: 4.6.2 lodash.merge: 4.6.2
minimatch: 3.1.2 minimatch: 3.1.2
natural-compare: 1.4.0 natural-compare: 1.4.0
optionator: 0.9.4 optionator: 0.9.4
optionalDependencies: strip-ansi: 6.0.1
jiti: 2.6.1 text-table: 0.2.0
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -8204,6 +8166,12 @@ snapshots:
acorn-jsx: 5.3.2(acorn@8.15.0) acorn-jsx: 5.3.2(acorn@8.15.0)
eslint-visitor-keys: 4.2.1 eslint-visitor-keys: 4.2.1
espree@9.6.1:
dependencies:
acorn: 8.15.0
acorn-jsx: 5.3.2(acorn@8.15.0)
eslint-visitor-keys: 3.4.3
esquery@1.7.0: esquery@1.7.0:
dependencies: dependencies:
estraverse: 5.3.0 estraverse: 5.3.0
@@ -8258,9 +8226,9 @@ snapshots:
optionalDependencies: optionalDependencies:
picomatch: 4.0.3 picomatch: 4.0.3
file-entry-cache@8.0.0: file-entry-cache@6.0.1:
dependencies: dependencies:
flat-cache: 4.0.1 flat-cache: 3.2.0
fill-range@7.1.1: fill-range@7.1.1:
dependencies: dependencies:
@@ -8271,10 +8239,11 @@ snapshots:
locate-path: 6.0.0 locate-path: 6.0.0
path-exists: 4.0.0 path-exists: 4.0.0
flat-cache@4.0.1: flat-cache@3.2.0:
dependencies: dependencies:
flatted: 3.3.3 flatted: 3.3.3
keyv: 4.5.4 keyv: 4.5.4
rimraf: 3.0.2
flatted@3.3.3: {} flatted@3.3.3: {}
@@ -8385,6 +8354,15 @@ snapshots:
package-json-from-dist: 1.0.1 package-json-from-dist: 1.0.1
path-scurry: 1.11.1 path-scurry: 1.11.1
glob@7.2.3:
dependencies:
fs.realpath: 1.0.0
inflight: 1.0.6
inherits: 2.0.4
minimatch: 3.1.2
once: 1.4.0
path-is-absolute: 1.0.1
glob@9.3.5: glob@9.3.5:
dependencies: dependencies:
fs.realpath: 1.0.0 fs.realpath: 1.0.0
@@ -8396,9 +8374,11 @@ snapshots:
dependencies: dependencies:
ini: 4.1.1 ini: 4.1.1
globals@14.0.0: {} globals@13.24.0:
dependencies:
type-fest: 0.20.2
globals@16.4.0: {} globals@14.0.0: {}
globalthis@1.0.4: globalthis@1.0.4:
dependencies: dependencies:
@@ -8409,6 +8389,8 @@ snapshots:
graceful-fs@4.2.11: {} graceful-fs@4.2.11: {}
graphemer@1.4.0: {}
has-bigints@1.1.0: {} has-bigints@1.1.0: {}
has-flag@4.0.0: {} has-flag@4.0.0: {}
@@ -8433,12 +8415,6 @@ snapshots:
help-me@5.0.0: {} help-me@5.0.0: {}
hermes-estree@0.25.1: {}
hermes-parser@0.25.1:
dependencies:
hermes-estree: 0.25.1
hoist-non-react-statics@3.3.2: hoist-non-react-statics@3.3.2:
dependencies: dependencies:
react-is: 16.13.1 react-is: 16.13.1
@@ -8505,6 +8481,13 @@ snapshots:
indent-string@4.0.0: {} indent-string@4.0.0: {}
inflight@1.0.6:
dependencies:
once: 1.4.0
wrappy: 1.0.2
inherits@2.0.4: {}
ini@4.1.1: {} ini@4.1.1: {}
internal-slot@1.1.0: internal-slot@1.1.0:
@@ -8610,6 +8593,8 @@ snapshots:
is-obj@2.0.0: {} is-obj@2.0.0: {}
is-path-inside@3.0.3: {}
is-plain-obj@4.1.0: {} is-plain-obj@4.1.0: {}
is-potential-custom-element-name@1.0.1: {} is-potential-custom-element-name@1.0.1: {}
@@ -9154,6 +9139,8 @@ snapshots:
path-exists@4.0.0: {} path-exists@4.0.0: {}
path-is-absolute@1.0.1: {}
path-key@3.1.1: {} path-key@3.1.1: {}
path-parse@1.0.7: {} path-parse@1.0.7: {}
@@ -9381,6 +9368,10 @@ snapshots:
rfdc@1.4.1: {} rfdc@1.4.1: {}
rimraf@3.0.2:
dependencies:
glob: 7.2.3
rollup@3.29.5: rollup@3.29.5:
optionalDependencies: optionalDependencies:
fsevents: 2.3.3 fsevents: 2.3.3
@@ -9763,6 +9754,8 @@ snapshots:
commander: 2.20.3 commander: 2.20.3
source-map-support: 0.5.21 source-map-support: 0.5.21
text-table@0.2.0: {}
thread-stream@4.0.0: thread-stream@4.0.0:
dependencies: dependencies:
real-require: 0.2.0 real-require: 0.2.0
@@ -9815,6 +9808,8 @@ snapshots:
dependencies: dependencies:
prelude-ls: 1.2.1 prelude-ls: 1.2.1
type-fest@0.20.2: {}
type-fest@0.7.1: {} type-fest@0.7.1: {}
typed-array-buffer@1.0.3: typed-array-buffer@1.0.3:
@@ -9850,13 +9845,13 @@ snapshots:
possible-typed-array-names: 1.1.0 possible-typed-array-names: 1.1.0
reflect.getprototypeof: 1.0.10 reflect.getprototypeof: 1.0.10
typescript-eslint@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): typescript-eslint@8.54.0(eslint@8.57.1)(typescript@5.9.3):
dependencies: dependencies:
'@typescript-eslint/eslint-plugin': 8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/eslint-plugin': 8.54.0(@typescript-eslint/parser@8.54.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)
'@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': 8.54.0(eslint@8.57.1)(typescript@5.9.3)
'@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/utils': 8.54.0(eslint@8.57.1)(typescript@5.9.3)
eslint: 9.39.2(jiti@2.6.1) eslint: 8.57.1
typescript: 5.9.3 typescript: 5.9.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -10143,10 +10138,6 @@ snapshots:
yocto-queue@0.1.0: {} yocto-queue@0.1.0: {}
zod-validation-error@4.0.2(zod@4.3.6):
dependencies:
zod: 4.3.6
zod@3.25.76: {} zod@3.25.76: {}
zod@4.3.6: {} zod@4.3.6: {}

View File

@@ -7,26 +7,36 @@ import { createCollection, createField, updateSettings } from "@directus/sdk";
const client = createMintelDirectusClient(); const client = createMintelDirectusClient();
async function setupBranding() { async function setupBranding() {
const prjName = process.env.PROJECT_NAME || "Mintel Project"; const prjName = process.env.PROJECT_NAME || "MB Grid Solutions";
const prjColor = process.env.PROJECT_COLOR || "#82ed20"; const prjColor = process.env.PROJECT_COLOR || "#82ed20";
console.log(`🎨 Setup Directus Branding for ${prjName}...`); console.log(`🎨 Refining Directus Branding for ${prjName}...`);
await ensureDirectusAuthenticated(client); await ensureDirectusAuthenticated(client);
const cssInjection = ` const cssInjection = `
<style> <style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700&display=swap');
body, .v-app { font-family: 'Inter', sans-serif !important; }
body, .v-app { font-family: 'Outfit', sans-serif !important; }
.public-view .v-card { .public-view .v-card {
backdrop-filter: blur(20px); backdrop-filter: blur(20px);
background: rgba(255, 255, 255, 0.9) !important;
border-radius: 32px !important; border-radius: 32px !important;
box-shadow: 0 50px 100px -20px rgba(0, 0, 0, 0.4) !important; box-shadow: 0 50px 100px -20px rgba(0, 0, 0, 0.4) !important;
border: 1px solid rgba(255, 255, 255, 0.3) !important;
} }
.v-navigation-drawer { background: #000c24 !important; } .v-navigation-drawer { background: #000c24 !important; }
.v-list-item--active {
color: ${prjColor} !important;
background: rgba(130, 237, 32, 0.1) !important;
}
</style> </style>
<div style="font-family: 'Inter', sans-serif; text-align: center; margin-top: 24px;"> <div style="font-family: 'Outfit', sans-serif; text-align: center; margin-top: 24px;">
<p style="color: rgba(255,255,255,0.7); font-size: 14px; margin-bottom: 4px; font-weight: 500;">MINTEL INFRASTRUCTURE ENGINE</p> <p style="color: rgba(255,255,255,0.6); font-size: 11px; letter-spacing: 2px; margin-bottom: 4px; font-weight: 600; text-transform: uppercase;">Mintel Infrastructure Engine</p>
<h1 style="color: #ffffff; font-size: 18px; font-weight: 700; margin: 0;">${prjName.toUpperCase()} <span style="color: ${prjColor};">RELIABILITY.</span></h1> <h1 style="color: #ffffff; font-size: 20px; font-weight: 700; margin: 0; letter-spacing: -0.5px;">${prjName.toUpperCase()} <span style="color: ${prjColor};">SYNC.</span></h1>
</div> </div>
`; `;
@@ -36,24 +46,23 @@ async function setupBranding() {
project_name: prjName, project_name: prjName,
project_color: prjColor, project_color: prjColor,
public_note: cssInjection, public_note: cssInjection,
module_bar_background: "#00081a",
theme_light_overrides: { theme_light_overrides: {
primary: prjColor, primary: prjColor,
borderRadius: "16px", borderRadius: "12px",
navigationBackground: "#000c24", navigationBackground: "#000c24",
navigationForeground: "#ffffff", navigationForeground: "#ffffff",
moduleBarBackground: "#00081a",
}, },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any), } as any),
); );
console.log("✨ Branding applied!"); console.log("✨ Branding applied!");
try { await createCollectionAndFields();
await createCollectionAndFields(); console.log("🏗️ Schema alignment complete!");
console.log("🏗️ Schema alignment complete!");
} catch (error) {
console.error("❌ Error aligning schema:", error);
}
} catch (error) { } catch (error) {
console.error("❌ Error setting up branding:", error); console.error("❌ Error during bootstrap:", error);
} }
} }
@@ -68,8 +77,11 @@ async function createCollectionAndFields() {
meta: { meta: {
icon: "contact_mail", icon: "contact_mail",
display_template: "{{name}} <{{email}}>", display_template: "{{name}} <{{email}}>",
group: null,
sort: null,
collapse: "open",
}, },
} as any), }),
); );
// Add ID field // Add ID field
@@ -82,27 +94,57 @@ async function createCollectionAndFields() {
}), }),
); );
console.log(`✅ Collection ${collectionName} created.`); console.log(`✅ Collection ${collectionName} created.`);
} catch (e: any) { } catch {
console.log(` Collection ${collectionName} status: ${e.message}`); console.log(` Collection ${collectionName} exists.`);
} }
const safeAddField = async (field: string, type: string, meta: any = {}) => { const safeAddField = async (
field: string,
type: string,
meta: Record<string, unknown> = {},
) => {
try { try {
await client.request(createField(collectionName, { field, type, meta })); await client.request(createField(collectionName, { field, type, meta }));
console.log(`✅ Field ${field} added.`); console.log(`✅ Field ${field} added.`);
} catch (e: any) { } catch {
// Ignore if exists // Ignore if exists
} }
}; };
await safeAddField("name", "string", { interface: "input" }); await safeAddField("name", "string", {
await safeAddField("email", "string", { interface: "input" }); interface: "input",
await safeAddField("company", "string", { interface: "input" }); display: "raw",
await safeAddField("message", "text", { interface: "textarea" }); width: "half",
});
await safeAddField("email", "string", {
interface: "input",
display: "raw",
width: "half",
});
await safeAddField("company", "string", {
interface: "input",
display: "raw",
width: "half",
});
await safeAddField("message", "text", {
interface: "textarea",
display: "raw",
width: "full",
});
await safeAddField("date_created", "timestamp", { await safeAddField("date_created", "timestamp", {
interface: "datetime", interface: "datetime",
special: ["date-created"], special: ["date-created"],
display: "datetime",
display_options: { relative: true },
width: "half",
}); });
} }
setupBranding(); setupBranding()
.then(() => {
process.exit(0);
})
.catch((err) => {
console.error("🚨 Fatal bootstrap error:", err);
process.exit(1);
});

View File

@@ -1,15 +1,13 @@
#!/bin/bash #!/bin/bash
# Mintel Directus Sync Engine # Configuration
# Synchronizes Directus Data (Postgres + Uploads) between Local and Remote
REMOTE_HOST="${SSH_HOST:-root@alpha.mintel.me}" REMOTE_HOST="${SSH_HOST:-root@alpha.mintel.me}"
ACTION=$1 ACTION=$1
ENV=$2 ENV=$2
# Help # Help
if [ -z "$ACTION" ] || [ -z "$ENV" ]; then if [ -z "$ACTION" ] || [ -z "$ENV" ]; then
echo "Usage: mintel-sync [push|pull] [testing|staging|production]" echo "Usage: ./scripts/sync-directus.sh [push|pull] [testing|staging|production]"
echo "" echo ""
echo "Commands:" echo "Commands:"
echo " push Sync LOCAL data -> REMOTE" echo " push Sync LOCAL data -> REMOTE"
@@ -20,7 +18,10 @@ if [ -z "$ACTION" ] || [ -z "$ENV" ]; then
exit 1 exit 1
fi fi
PRJ_ID=$(jq -r .name package.json | sed 's/@mintel\///') # Project Configuration (extracted from package.json and aligned with deploy.yml)
PRJ_ID=$(jq -r .name package.json | sed 's/@mintel\///' | sed 's/\.com$//')
REMOTE_DIR="/home/deploy/sites/${PRJ_ID}.com"
case $ENV in case $ENV in
testing) PROJECT_NAME="${PRJ_ID}-testing"; ENV_FILE=".env.testing" ;; testing) PROJECT_NAME="${PRJ_ID}-testing"; ENV_FILE=".env.testing" ;;
staging) PROJECT_NAME="${PRJ_ID}-staging"; ENV_FILE=".env.staging" ;; staging) PROJECT_NAME="${PRJ_ID}-staging"; ENV_FILE=".env.staging" ;;
@@ -28,41 +29,90 @@ case $ENV in
*) echo "❌ Invalid environment: $ENV"; exit 1 ;; *) echo "❌ Invalid environment: $ENV"; exit 1 ;;
esac esac
REMOTE_DIR="/home/deploy/sites/${PRJ_ID}.com" # DB Details (matching docker-compose defaults)
# DB Details
DB_USER="directus" DB_USER="directus"
DB_NAME="directus" DB_NAME="directus"
echo "🔍 Detecting local database..." echo "🔍 Detecting local database..."
LOCAL_DB_CONTAINER=$(docker compose ps -q directus-db) LOCAL_DB_CONTAINER=$(docker compose ps -q directus-db)
if [ -z "$LOCAL_DB_CONTAINER" ]; then if [ -z "$LOCAL_DB_CONTAINER" ]; then
echo "❌ Local directus-db container not found. Running?" echo "❌ Local directus-db container not found. Is it running? (npm run dev)"
exit 1 exit 1
fi fi
if [ "$ACTION" == "push" ]; then if [ "$ACTION" == "push" ]; then
echo "🚀 Pushing LOCAL -> $ENV ($PROJECT_NAME)..." echo "🚀 Pushing LOCAL -> $ENV ($PROJECT_NAME)..."
# 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 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" 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-db") REMOTE_DB_CONTAINER=$(ssh "$REMOTE_HOST" "cd $REMOTE_DIR && docker compose -p $PROJECT_NAME ps -q directus-db")
if [ -z "$REMOTE_DB_CONTAINER" ]; then
echo "❌ Remote $ENV-db container not found!"
exit 1
fi
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" 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/directus/uploads/" rsync -avz --progress ./directus/uploads/ "$REMOTE_HOST:$REMOTE_DIR/directus/uploads/"
# Clean up
rm dump.sql rm dump.sql
ssh "$REMOTE_HOST" "rm $REMOTE_DIR/dump.sql" ssh "$REMOTE_HOST" "rm $REMOTE_DIR/dump.sql"
echo "✨ Push complete!"
# 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 elif [ "$ACTION" == "pull" ]; then
echo "📥 Pulling $ENV -> LOCAL..." echo "📥 Pulling $ENV Data -> 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-db") REMOTE_DB_CONTAINER=$(ssh "$REMOTE_HOST" "cd $REMOTE_DIR && docker compose -p $PROJECT_NAME ps -q directus-db")
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" 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 scp "$REMOTE_HOST:$REMOTE_DIR/dump.sql" dump.sql
# 3. Restore Locally
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 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/directus/uploads/" ./directus/uploads/ rsync -avz --progress "$REMOTE_HOST:$REMOTE_DIR/directus/uploads/" ./directus/uploads/
# Clean up
rm dump.sql rm dump.sql
ssh "$REMOTE_HOST" "rm $REMOTE_DIR/dump.sql" ssh "$REMOTE_HOST" "rm $REMOTE_DIR/dump.sql"
echo "✨ Pull complete!"
echo "✨ Pull to Local complete!"
fi fi

View File

@@ -3,6 +3,6 @@ import { validateMintelEnv } from "@mintel/next-utils";
try { try {
validateMintelEnv(); validateMintelEnv();
console.log("✅ Environment variables validated"); console.log("✅ Environment variables validated");
} catch (error) { } catch {
process.exit(1); process.exit(1);
} }