Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b9fd583ac4 | |||
| bfdbaba0d0 | |||
| 4ea9cbc551 | |||
| d8c1a38c0d | |||
| b65b9a7fb2 | |||
| 858c7bbc39 | |||
| 149123ef90 | |||
| 6bc49d1c52 | |||
| 52ffe49019 | |||
| 73fa292528 | |||
| f2c0a4581c | |||
| 367c4d8404 | |||
| 587c88980f | |||
| fcdfdb4588 | |||
| 6bbaa8d105 | |||
| eccc084441 | |||
| da6b8aba64 | |||
| 290097b4e6 | |||
| 45894cce34 | |||
| 7195906da0 | |||
| dcb466f53b | |||
| 14089766ea | |||
| 6ecabe4a04 | |||
| b205220bde | |||
| 3d5a802c6e | |||
| b5d1272f85 | |||
| e152fb8171 |
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@mintel/mail": minor
|
||||
---
|
||||
|
||||
Initial release of the branded email system package.
|
||||
@@ -1,4 +1,5 @@
|
||||
# Project
|
||||
IMAGE_TAG=v1.7.3
|
||||
PROJECT_NAME=sample-website
|
||||
PROJECT_COLOR=#82ed20
|
||||
|
||||
@@ -12,8 +12,35 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
qa:
|
||||
name: 🧪 Quality Assurance
|
||||
prioritize:
|
||||
name: ⚡ Prioritize Release
|
||||
runs-on: docker
|
||||
container:
|
||||
image: catthehacker/ubuntu:act-latest
|
||||
steps:
|
||||
- name: 🛑 Cancel Redundant Runs
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
RUN_ID: ${{ github.run_id }}
|
||||
run: |
|
||||
echo "🚀 Release detected. Cancelling non-tag runs..."
|
||||
# Get all runs for this repo
|
||||
RUNS=$(curl -s -H "Authorization: token $GITEA_TOKEN" "https://git.infra.mintel.me/api/v1/repos/$REPO/actions/runs")
|
||||
|
||||
# Iterate and cancel in_progress/queued non-tag runs
|
||||
echo "$RUNS" | jq -c '.workflow_runs[] | select(.status == "in_progress" or .status == "queued") | select(.id | tostring != "'$RUN_ID'") | select(.event != "push" or .ref | contains("refs/tags/v") | not)' | while read run; do
|
||||
ID=$(echo "$run" | jq -r '.id')
|
||||
DESC=$(echo "$run" | jq -r '.display_title')
|
||||
echo "🛑 Cancelling redundant run $ID ($DESC)..."
|
||||
curl -X POST -s -H "Authorization: token $GITEA_TOKEN" "https://git.infra.mintel.me/api/v1/repos/$REPO/actions/runs/$ID/cancel"
|
||||
done
|
||||
|
||||
lint:
|
||||
name: 🧹 Lint
|
||||
needs: prioritize
|
||||
if: always() && !cancelled() && (needs.prioritize.result == 'success' || needs.prioritize.result == 'skipped')
|
||||
runs-on: docker
|
||||
container:
|
||||
image: catthehacker/ubuntu:act-latest
|
||||
@@ -22,32 +49,66 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node_version: 20
|
||||
|
||||
- name: Enable pnpm
|
||||
run: corepack enable && corepack prepare pnpm@10.2.0 --activate
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts --no-color
|
||||
- name: Lint
|
||||
run: pnpm lint
|
||||
|
||||
test:
|
||||
name: 🧪 Test
|
||||
needs: prioritize
|
||||
if: always() && !cancelled() && (needs.prioritize.result == 'success' || needs.prioritize.result == 'skipped')
|
||||
runs-on: docker
|
||||
container:
|
||||
image: catthehacker/ubuntu:act-latest
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node_version: 20
|
||||
- name: Enable pnpm
|
||||
run: corepack enable && corepack prepare pnpm@10.2.0 --activate
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts --no-color
|
||||
- name: Test
|
||||
run: pnpm test
|
||||
|
||||
build:
|
||||
name: 🏗️ Build
|
||||
needs: prioritize
|
||||
if: always() && !cancelled() && (needs.prioritize.result == 'success' || needs.prioritize.result == 'skipped')
|
||||
runs-on: docker
|
||||
container:
|
||||
image: catthehacker/ubuntu:act-latest
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node_version: 20
|
||||
- name: Enable pnpm
|
||||
run: corepack enable && corepack prepare pnpm@10.2.0 --activate
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts --no-color
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
|
||||
release:
|
||||
name: 🚀 Release
|
||||
needs: qa
|
||||
needs: [lint, test, build]
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: docker
|
||||
container:
|
||||
@@ -60,29 +121,24 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node_version: 20
|
||||
|
||||
- name: Enable pnpm
|
||||
run: corepack enable && corepack prepare pnpm@10.2.0 --activate
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts --no-color
|
||||
- name: 🏷️ Sync Versions (if Tagged)
|
||||
run: pnpm sync-versions
|
||||
- name: 🏷️ Release Packages (Tag-Driven)
|
||||
run: |
|
||||
echo "🏷️ Tag detected [${{ github.ref_name }}], performing sync release..."
|
||||
pnpm sync-versions
|
||||
pnpm release:tag
|
||||
|
||||
build-images:
|
||||
name: 🐳 Build ${{ matrix.name }}
|
||||
needs: qa
|
||||
needs: [lint, test, build]
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: docker
|
||||
container:
|
||||
|
||||
16
.husky/pre-push
Executable file
16
.husky/pre-push
Executable file
@@ -0,0 +1,16 @@
|
||||
|
||||
# Check if we are pushing a tag
|
||||
while read local_ref local_sha remote_ref remote_sha
|
||||
do
|
||||
if [[ "$remote_ref" == refs/tags/v* ]]; then
|
||||
TAG=${remote_ref#refs/tags/}
|
||||
echo "🏷️ Tag detected: $TAG, syncing versions..."
|
||||
pnpm sync-versions "$TAG"
|
||||
|
||||
# Stage the changed files (excluding ignored files like .env)
|
||||
git add package.json packages/*/package.json apps/*/package.json .env.example
|
||||
|
||||
echo "⚠️ package.json and .env files updated to match tag $TAG."
|
||||
echo "⚠️ Note: You might need to push again if these changes were not already in your commit/tag."
|
||||
fi
|
||||
done
|
||||
@@ -1,3 +0,0 @@
|
||||
import { nextConfig } from "@mintel/eslint-config/next";
|
||||
|
||||
export default nextConfig;
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sample-website",
|
||||
"version": "0.1.1",
|
||||
"version": "1.7.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -8,15 +8,9 @@
|
||||
"dev:local": "mintel dev --local",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"lint": "eslint src/",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "vitest run --passWithNoTests",
|
||||
"cms:bootstrap": "mintel directus bootstrap",
|
||||
"cms:push:testing": "mintel directus sync push testing",
|
||||
"cms:pull:testing": "mintel directus sync pull testing",
|
||||
"cms:push:staging": "mintel directus sync push staging",
|
||||
"cms:pull:staging": "mintel directus sync pull staging",
|
||||
"cms:push:prod": "mintel directus sync push production",
|
||||
"cms:pull:prod": "mintel directus sync pull production",
|
||||
"pagespeed:test": "mintel pagespeed"
|
||||
},
|
||||
@@ -24,8 +18,8 @@
|
||||
"@mintel/next-utils": "workspace:*",
|
||||
"@mintel/observability": "workspace:*",
|
||||
"@mintel/next-observability": "workspace:*",
|
||||
"@sentry/nextjs": "^8.55.0",
|
||||
"next": "15.1.6",
|
||||
"@sentry/nextjs": "10.38.0",
|
||||
"next": "16.1.6",
|
||||
"next-intl": "^4.8.2",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
|
||||
19
directus/schema/snapshot.yaml
Normal file
19
directus/schema/snapshot.yaml
Normal file
@@ -0,0 +1,19 @@
|
||||
version: 1
|
||||
directus: 11.15.1
|
||||
vendor: postgres
|
||||
collections: []
|
||||
fields: []
|
||||
systemFields:
|
||||
- collection: directus_activity
|
||||
field: timestamp
|
||||
schema:
|
||||
is_indexed: true
|
||||
- collection: directus_revisions
|
||||
field: activity
|
||||
schema:
|
||||
is_indexed: true
|
||||
- collection: directus_revisions
|
||||
field: parent
|
||||
schema:
|
||||
is_indexed: true
|
||||
relations: []
|
||||
0
directus/uploads/.gitkeep
Normal file
0
directus/uploads/.gitkeep
Normal file
@@ -1,17 +1,18 @@
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
context: ./apps/sample-website
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
NEXT_PUBLIC_BASE_URL: ${NEXT_PUBLIC_BASE_URL:-http://localhost:3000}
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${NEXT_PUBLIC_UMAMI_WEBSITE_ID}
|
||||
NEXT_PUBLIC_UMAMI_SCRIPT_URL: ${NEXT_PUBLIC_UMAMI_SCRIPT_URL}
|
||||
NEXT_PUBLIC_TARGET: ${TARGET:-development}
|
||||
DIRECTUS_URL: ${DIRECTUS_URL:-http://directus:8055}
|
||||
restart: always
|
||||
networks:
|
||||
- infra
|
||||
environment:
|
||||
- DIRECTUS_URL=${DIRECTUS_URL:-http://directus:8055}
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
@@ -22,7 +23,7 @@ services:
|
||||
- "traefik.http.services.sample-website.loadbalancer.server.port=3000"
|
||||
|
||||
directus:
|
||||
image: registry.infra.mintel.me/mintel/directus:latest
|
||||
image: registry.infra.mintel.me/mintel/directus:${IMAGE_TAG:-latest}
|
||||
restart: always
|
||||
networks:
|
||||
- infra
|
||||
@@ -46,6 +47,7 @@ services:
|
||||
volumes:
|
||||
- ./directus/uploads:/directus/uploads
|
||||
- ./directus/extensions:/directus/extensions
|
||||
- ./directus/schema:/directus/schema
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.sample-website-directus.rule=Host(`${DIRECTUS_HOST:-cms.sample-website.localhost}`)"
|
||||
@@ -8,6 +8,10 @@ export default [
|
||||
"packages/customer-manager/index.js",
|
||||
"**/*.db",
|
||||
"**/build/**",
|
||||
"**/data/**",
|
||||
"**/reference/**",
|
||||
"**/dist/**",
|
||||
"**/.next/**",
|
||||
],
|
||||
},
|
||||
...baseConfig,
|
||||
|
||||
19
package.json
19
package.json
@@ -5,11 +5,17 @@
|
||||
"scripts": {
|
||||
"build": "pnpm -r build",
|
||||
"dev": "pnpm -r dev",
|
||||
"lint": "pnpm -r lint",
|
||||
"lint": "pnpm -r --filter='./packages/**' --filter='./apps/**' lint",
|
||||
"test": "pnpm -r test",
|
||||
"changeset": "changeset",
|
||||
"version-packages": "changeset version",
|
||||
"sync-versions": "tsx scripts/sync-versions.ts",
|
||||
"sync-versions": "tsx scripts/sync-versions.ts --",
|
||||
"cms:push:infra": "./scripts/sync-directus.sh push infra",
|
||||
"cms:pull:infra": "./scripts/sync-directus.sh pull infra",
|
||||
"cms:schema:snapshot": "./scripts/cms-snapshot.sh",
|
||||
"cms:schema:apply": "./scripts/cms-apply.sh local",
|
||||
"cms:schema:apply:infra": "./scripts/cms-apply.sh infra",
|
||||
"dev:infra": "docker-compose up -d directus directus-db",
|
||||
"release": "pnpm build && changeset publish",
|
||||
"release:tag": "pnpm build && pnpm -r publish --no-git-checks --access public",
|
||||
"prepare": "husky"
|
||||
@@ -27,7 +33,7 @@
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react": "^5.1.2",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-plugin-next": "^0.0.0",
|
||||
"@next/eslint-plugin-next": "16.1.6",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"happy-dom": "^20.4.0",
|
||||
@@ -45,5 +51,12 @@
|
||||
"pino": "^10.3.1",
|
||||
"pino-pretty": "^13.1.3",
|
||||
"require-in-the-middle": "^8.0.1"
|
||||
},
|
||||
"version": "1.7.3",
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"next": "16.1.6",
|
||||
"@sentry/nextjs": "10.38.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/cli",
|
||||
"version": "1.0.1",
|
||||
"version": "1.7.3",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
@@ -28,4 +28,4 @@
|
||||
"@types/prompts": "^2.4.4",
|
||||
"@mintel/tsconfig": "workspace:*"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ program
|
||||
"pagespeed:test": "mintel pagespeed",
|
||||
},
|
||||
dependencies: {
|
||||
next: "15.1.6",
|
||||
next: "16.1.6",
|
||||
react: "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"@mintel/next-utils": "workspace:*",
|
||||
|
||||
0
packages/cms-infra/database/RELOAD_TEST
Normal file
0
packages/cms-infra/database/RELOAD_TEST
Normal file
Binary file not shown.
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "@mintel/cms-infra",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"up": "docker compose up -d",
|
||||
"down": "docker compose down",
|
||||
"logs": "docker compose logs -f"
|
||||
}
|
||||
}
|
||||
"name": "@mintel/cms-infra",
|
||||
"version": "1.7.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"up": "docker compose up -d",
|
||||
"down": "docker compose down",
|
||||
"logs": "docker compose logs -f"
|
||||
}
|
||||
}
|
||||
|
||||
1221
packages/cms-infra/schema/snapshot.yaml
Normal file
1221
packages/cms-infra/schema/snapshot.yaml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,29 +1,29 @@
|
||||
{
|
||||
"name": "customer-manager",
|
||||
"description": "Custom High-Fidelity Customer & Company Management for Directus",
|
||||
"icon": "supervisor_account",
|
||||
"version": "1.0.0",
|
||||
"keywords": [
|
||||
"directus",
|
||||
"directus-extension",
|
||||
"directus-extension-module"
|
||||
],
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"directus:extension": {
|
||||
"type": "module",
|
||||
"path": "index.js",
|
||||
"source": "src/index.ts",
|
||||
"host": "*",
|
||||
"name": "Customer Manager"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "directus-extension build",
|
||||
"dev": "directus-extension build -w"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@directus/extensions-sdk": "11.0.2",
|
||||
"vue": "^3.4.0"
|
||||
}
|
||||
}
|
||||
"name": "customer-manager",
|
||||
"description": "Custom High-Fidelity Customer & Company Management for Directus",
|
||||
"icon": "supervisor_account",
|
||||
"version": "1.7.3",
|
||||
"keywords": [
|
||||
"directus",
|
||||
"directus-extension",
|
||||
"directus-extension-module"
|
||||
],
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"directus:extension": {
|
||||
"type": "module",
|
||||
"path": "index.js",
|
||||
"source": "src/index.ts",
|
||||
"host": "*",
|
||||
"name": "Customer Manager"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "directus-extension build",
|
||||
"dev": "directus-extension build -w"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@directus/extensions-sdk": "11.0.2",
|
||||
"vue": "^3.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/eslint-config",
|
||||
"version": "1.0.1",
|
||||
"version": "1.7.3",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
@@ -20,8 +20,8 @@
|
||||
"dependencies": {
|
||||
"@eslint/eslintrc": "^3.0.0",
|
||||
"@eslint/js": "^9.39.2",
|
||||
"@next/eslint-plugin-next": "15.1.6",
|
||||
"eslint-config-next": "15.1.6",
|
||||
"@next/eslint-plugin-next": "16.1.6",
|
||||
"eslint-config-next": "16.1.6",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"typescript-eslint": "^8.54.0"
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
{
|
||||
"name": "@mintel/extension-feedback-commander",
|
||||
"description": "Custom High-Fidelity Feedback Management Extension for Directus",
|
||||
"icon": "view_kanban",
|
||||
"version": "1.0.0",
|
||||
"keywords": [
|
||||
"directus",
|
||||
"directus-extension",
|
||||
"directus-extension-module"
|
||||
],
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"directus:extension": {
|
||||
"type": "module",
|
||||
"path": "dist/index.js",
|
||||
"source": "src/index.ts",
|
||||
"host": "*",
|
||||
"name": "Feedback Commander"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "directus-extension build",
|
||||
"dev": "directus-extension build -w"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@directus/extensions-sdk": "11.0.2",
|
||||
"vue": "^3.4.0"
|
||||
}
|
||||
}
|
||||
"name": "@mintel/extension-feedback-commander",
|
||||
"description": "Custom High-Fidelity Feedback Management Extension for Directus",
|
||||
"icon": "view_kanban",
|
||||
"version": "1.7.3",
|
||||
"keywords": [
|
||||
"directus",
|
||||
"directus-extension",
|
||||
"directus-extension-module"
|
||||
],
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"directus:extension": {
|
||||
"type": "module",
|
||||
"path": "dist/index.js",
|
||||
"source": "src/index.ts",
|
||||
"host": "*",
|
||||
"name": "Feedback Commander"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "directus-extension build",
|
||||
"dev": "directus-extension build -w"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@directus/extensions-sdk": "11.0.2",
|
||||
"vue": "^3.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
{
|
||||
"name": "@mintel/gatekeeper",
|
||||
"version": "1.0.0",
|
||||
"version": "1.7.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"lint": "eslint src/",
|
||||
"test": "vitest run"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mintel/next-utils": "workspace:*",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.474.0",
|
||||
"next": "15.1.7",
|
||||
"next": "16.1.6",
|
||||
"next-intl": "^4.8.2",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
|
||||
@@ -44,7 +44,7 @@ export async function GET(req: NextRequest) {
|
||||
|
||||
return response;
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
// URL parsing failed, proceed with normal logic
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ export async function GET(req: NextRequest) {
|
||||
isAuthenticated = true;
|
||||
identity = payload.identity;
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
// Fallback or old format
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
export async function GET(_req: NextRequest) {
|
||||
const cookieStore = await cookies();
|
||||
const authCookieName =
|
||||
process.env.AUTH_COOKIE_NAME || "mintel_gatekeeper_session";
|
||||
@@ -17,7 +17,7 @@ export async function GET(req: NextRequest) {
|
||||
const payload = JSON.parse(session.value);
|
||||
identity = payload.identity || "Guest";
|
||||
company = payload.company || null;
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
// Old format probably just the password
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/husky-config",
|
||||
"version": "1.0.0",
|
||||
"version": "1.7.3",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
|
||||
@@ -53,7 +53,7 @@ services:
|
||||
- "traefik.http.services.${PROJECT_NAME}-gatekeeper.loadbalancer.server.port=3000"
|
||||
|
||||
directus:
|
||||
image: registry.infra.mintel.me/mintel/directus:latest
|
||||
image: registry.infra.mintel.me/mintel/directus:${IMAGE_TAG:-latest}
|
||||
restart: always
|
||||
networks:
|
||||
- infra
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/infra",
|
||||
"version": "1.0.1",
|
||||
"version": "1.7.3",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
|
||||
7
packages/mail/CHANGELOG.md
Normal file
7
packages/mail/CHANGELOG.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# @mintel/mail
|
||||
|
||||
## 1.7.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 96ec2c7: Initial release of the branded email system package.
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/mail",
|
||||
"version": "1.2.0",
|
||||
"version": "1.7.3",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @mintel/next-config
|
||||
|
||||
## 1.6.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Add `turbopack: {}` to support Next.js 16 default Turbopack behavior when a webpack config is present.
|
||||
|
||||
## 1.6.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Add `turbopack: {}` to support Next.js 16 default Turbopack behavior when a webpack config is present.
|
||||
|
||||
## 1.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -7,6 +7,7 @@ import path from "node:path";
|
||||
/** @type {import('next').NextConfig} */
|
||||
export const baseNextConfig = {
|
||||
output: "standalone",
|
||||
turbopack: {},
|
||||
images: {
|
||||
dangerouslyAllowSVG: true,
|
||||
contentDispositionType: "attachment",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/next-config",
|
||||
"version": "1.0.1",
|
||||
"version": "1.7.3",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
@@ -16,6 +16,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"next-intl": "^4.8.2",
|
||||
"@sentry/nextjs": "^8.0.0"
|
||||
"@sentry/nextjs": "^10.38.0",
|
||||
"next": "16.1.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/next-feedback",
|
||||
"version": "1.0.0",
|
||||
"version": "1.7.3",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
@@ -31,7 +31,7 @@
|
||||
"framer-motion": "^11.5.4",
|
||||
"html2canvas": "^1.4.1",
|
||||
"lucide-react": "^0.441.0",
|
||||
"next": "15.1.7",
|
||||
"next": "16.1.6",
|
||||
"tailwind-merge": "^2.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/next-observability",
|
||||
"version": "1.0.0",
|
||||
"version": "1.7.3",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
@@ -28,8 +28,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@mintel/observability": "workspace:*",
|
||||
"@sentry/nextjs": "^8.55.0",
|
||||
"next": "15.1.7"
|
||||
"@sentry/nextjs": "^10.38.0",
|
||||
"next": "16.1.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/next-utils",
|
||||
"version": "1.0.1",
|
||||
"version": "1.7.7",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@directus/sdk": "^21.0.0",
|
||||
"next": "15.1.7",
|
||||
"next": "16.1.6",
|
||||
"next-intl": "^4.8.2",
|
||||
"zod": "^3.0.0"
|
||||
},
|
||||
|
||||
@@ -12,13 +12,35 @@ export type MintelDirectusClient = DirectusClient<any> &
|
||||
AuthenticationClient<any>;
|
||||
|
||||
/**
|
||||
* Creates a Directus client configured with Mintel standards
|
||||
* Creates a Directus client configured with Mintel standards.
|
||||
* Automatically handles internal vs. external URLs based on environment.
|
||||
*/
|
||||
export function createMintelDirectusClient(url?: string): MintelDirectusClient {
|
||||
const directusUrl =
|
||||
url || process.env.DIRECTUS_URL || "http://localhost:8055";
|
||||
const isServer = typeof window === "undefined";
|
||||
|
||||
return createDirectus(directusUrl).with(rest()).with(authentication());
|
||||
// 1. If an explicit URL is provided, use it.
|
||||
if (url) {
|
||||
return createDirectus(url).with(rest()).with(authentication());
|
||||
}
|
||||
|
||||
// 2. On server: Prioritize INTERNAL_DIRECTUS_URL, fallback to DIRECTUS_URL
|
||||
if (isServer) {
|
||||
const directusUrl =
|
||||
process.env.INTERNAL_DIRECTUS_URL ||
|
||||
process.env.DIRECTUS_URL ||
|
||||
"http://localhost:8055";
|
||||
return createDirectus(directusUrl).with(rest()).with(authentication());
|
||||
}
|
||||
|
||||
// 3. In browser: Use a proxy path if we are on a different origin,
|
||||
// or use the current origin if no DIRECTUS_URL is set.
|
||||
const proxyPath = "/api/directus"; // Standard Mintel proxy path
|
||||
const browserUrl =
|
||||
typeof window !== "undefined"
|
||||
? `${window.location.origin}${proxyPath}`
|
||||
: proxyPath;
|
||||
|
||||
return createDirectus(browserUrl).with(rest()).with(authentication());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,10 +4,17 @@ export const mintelEnvSchema = {
|
||||
NODE_ENV: z
|
||||
.enum(["development", "production", "test"])
|
||||
.default("development"),
|
||||
NEXT_PUBLIC_BASE_URL: z.string().url(),
|
||||
NEXT_PUBLIC_BASE_URL: z.string().url().optional(),
|
||||
NEXT_PUBLIC_TARGET: z
|
||||
.enum(["development", "testing", "staging", "production"])
|
||||
.optional(),
|
||||
TARGET: z
|
||||
.enum(["development", "testing", "staging", "production"])
|
||||
.optional(),
|
||||
|
||||
// Analytics (Proxy Pattern)
|
||||
UMAMI_WEBSITE_ID: z.string().optional(),
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID: z.string().optional(),
|
||||
UMAMI_API_ENDPOINT: z
|
||||
.string()
|
||||
.url()
|
||||
@@ -23,6 +30,8 @@ export const mintelEnvSchema = {
|
||||
LOG_LEVEL: z
|
||||
.enum(["trace", "debug", "info", "warn", "error", "fatal"])
|
||||
.default("info"),
|
||||
|
||||
// Mail
|
||||
MAIL_HOST: z.string().optional(),
|
||||
MAIL_PORT: z.coerce.number().default(587),
|
||||
MAIL_USERNAME: z.string().optional(),
|
||||
@@ -32,13 +41,19 @@ export const mintelEnvSchema = {
|
||||
(val) => (typeof val === "string" ? val.split(",").filter(Boolean) : val),
|
||||
z.array(z.string()).default([]),
|
||||
),
|
||||
|
||||
// Directus
|
||||
DIRECTUS_URL: z.string().url().default("http://localhost:8055"),
|
||||
DIRECTUS_ADMIN_EMAIL: z.string().optional(),
|
||||
DIRECTUS_ADMIN_PASSWORD: z.string().optional(),
|
||||
DIRECTUS_API_TOKEN: z.string().optional(),
|
||||
INTERNAL_DIRECTUS_URL: z.string().url().optional(),
|
||||
};
|
||||
|
||||
export function validateMintelEnv(schemaExtension = {}) {
|
||||
const fullSchema = z.object({
|
||||
...mintelEnvSchema,
|
||||
...schemaExtension,
|
||||
});
|
||||
export function validateMintelEnv<
|
||||
T extends z.ZodRawShape = Record<string, never>,
|
||||
>(schemaExtension: T = {} as T) {
|
||||
const fullSchema = z.object(mintelEnvSchema).extend(schemaExtension);
|
||||
|
||||
const isBuildTime =
|
||||
process.env.NEXT_PHASE === "phase-production-build" ||
|
||||
@@ -51,7 +66,7 @@ export function validateMintelEnv(schemaExtension = {}) {
|
||||
console.warn(
|
||||
"⚠️ Some environment variables are missing during build, but skipping strict validation.",
|
||||
);
|
||||
// Return partial data to allow build to continue
|
||||
// Return process.env casted to the full schema type to unblock builds
|
||||
return process.env as unknown as z.infer<typeof fullSchema>;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/observability",
|
||||
"version": "1.0.0",
|
||||
"version": "1.7.3",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/tsconfig",
|
||||
"version": "1.0.1",
|
||||
"version": "1.7.3",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
|
||||
3391
pnpm-lock.yaml
generated
3391
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,3 @@
|
||||
packages:
|
||||
- 'packages/*'
|
||||
- 'apps/*'
|
||||
- '../klz-2026'
|
||||
|
||||
71
scripts/cms-apply.sh
Executable file
71
scripts/cms-apply.sh
Executable file
@@ -0,0 +1,71 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Configuration
|
||||
PROJECT="infra-cms"
|
||||
LOCAL_SCHEMA_PATH="./packages/cms-infra/schema/snapshot.yaml"
|
||||
REMOTE_HOST="root@infra.mintel.me"
|
||||
REMOTE_DIR="/opt/infra/directus"
|
||||
|
||||
ENV=$1
|
||||
|
||||
if [ -z "$ENV" ]; then
|
||||
echo "Usage: ./scripts/cms-apply.sh [local|infra]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case $ENV in
|
||||
local)
|
||||
PROJECT="infra-cms"
|
||||
CMD_PREFIX="docker-compose -f packages/cms-infra/docker-compose.yml"
|
||||
|
||||
LOCAL_CONTAINER=$($CMD_PREFIX ps -q $PROJECT)
|
||||
if [ -z "$LOCAL_CONTAINER" ]; then
|
||||
echo "❌ Local $PROJECT container not found. Is it running?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🚀 Applying schema to LOCAL $PROJECT..."
|
||||
docker exec "$LOCAL_CONTAINER" npx directus schema apply -y /directus/schema/snapshot.yaml
|
||||
;;
|
||||
infra)
|
||||
# 'infra' is the remote production server for at-mintel
|
||||
PROJECT="directus" # Remote project name
|
||||
|
||||
echo "🔍 Detecting remote container..."
|
||||
REMOTE_CONTAINER=$(ssh "$REMOTE_HOST" "docker ps --filter label=com.docker.compose.project=$PROJECT --filter label=com.docker.compose.service=directus -q")
|
||||
|
||||
if [ -z "$REMOTE_CONTAINER" ]; then
|
||||
# Fallback to older name if labels fail
|
||||
REMOTE_CONTAINER=$(ssh "$REMOTE_HOST" "docker ps -f name=directus-directus-1 -q")
|
||||
fi
|
||||
|
||||
if [ -z "$REMOTE_CONTAINER" ]; then
|
||||
echo "❌ Remote container for $ENV not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "📦 Syncing extensions to REMOTE $ENV..."
|
||||
# Ensure remote directory exists
|
||||
ssh "$REMOTE_HOST" "mkdir -p $REMOTE_DIR/extensions"
|
||||
rsync -avz --delete ./packages/cms-infra/extensions/ "$REMOTE_HOST:$REMOTE_DIR/extensions/"
|
||||
|
||||
echo "📤 Injecting snapshot directly into container $REMOTE_CONTAINER..."
|
||||
# Inject file via stdin to avoid needing a host-side mount or scp path matching
|
||||
ssh "$REMOTE_HOST" "docker exec -i $REMOTE_CONTAINER sh -c 'cat > /tmp/snapshot.yaml'" < "$LOCAL_SCHEMA_PATH"
|
||||
|
||||
echo "🚀 Applying schema to REMOTE $ENV..."
|
||||
ssh "$REMOTE_HOST" "docker exec $REMOTE_CONTAINER npx directus schema apply -y /tmp/snapshot.yaml"
|
||||
|
||||
echo "🔄 Restarting remote Directus to clear cache..."
|
||||
ssh "$REMOTE_HOST" "cd $REMOTE_DIR && docker compose restart directus"
|
||||
|
||||
# Cleanup
|
||||
ssh "$REMOTE_HOST" "docker exec $REMOTE_CONTAINER rm /tmp/snapshot.yaml"
|
||||
;;
|
||||
*)
|
||||
echo "❌ Invalid environment: $ENV. Supported: local, infra."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "✨ Schema apply complete!"
|
||||
23
scripts/cms-snapshot.sh
Executable file
23
scripts/cms-snapshot.sh
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Configuration
|
||||
PROJECT="infra-cms"
|
||||
SCHEMA_PATH="./packages/cms-infra/schema/snapshot.yaml"
|
||||
CMD_PREFIX="docker-compose -f packages/cms-infra/docker-compose.yml"
|
||||
|
||||
# Detect local container
|
||||
LOCAL_CONTAINER=$($CMD_PREFIX ps -q $PROJECT)
|
||||
|
||||
if [ -z "$LOCAL_CONTAINER" ]; then
|
||||
echo "❌ Local $PROJECT container not found. Is it running?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "📸 Creating schema snapshot for local $PROJECT..."
|
||||
# Note: we save it to the mounted volume path inside the container
|
||||
docker exec "$LOCAL_CONTAINER" npx directus schema snapshot -y /directus/schema/snapshot.yaml
|
||||
|
||||
echo "🛠️ Repairing snapshot for Postgres compatibility..."
|
||||
python3 ./scripts/fix_snapshot_v3.py
|
||||
|
||||
echo "✅ Snapshot saved and repaired at $SCHEMA_PATH"
|
||||
96
scripts/fix_snapshot_v3.py
Normal file
96
scripts/fix_snapshot_v3.py
Normal file
@@ -0,0 +1,96 @@
|
||||
import sys
|
||||
import os
|
||||
|
||||
path = '/Users/marcmintel/Projects/at-mintel/packages/cms-infra/schema/snapshot.yaml'
|
||||
if not os.path.exists(path):
|
||||
print(f"File not found: {path}")
|
||||
sys.exit(1)
|
||||
|
||||
with open(path, 'r') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
new_lines = []
|
||||
current_collection = None
|
||||
current_field = None
|
||||
in_schema = False
|
||||
|
||||
fix_fields = {'id', 'company', 'user_created', 'user_updated', 'screenshot', 'logo', 'feedback_id'}
|
||||
uuid_fields = {'id', 'company', 'user_created', 'user_updated'}
|
||||
|
||||
# For multi-pass logic
|
||||
snapshot_has_feedback_id = False
|
||||
|
||||
for line in lines:
|
||||
stripped = line.strip()
|
||||
|
||||
if stripped.startswith('- collection:'):
|
||||
current_collection = stripped.split(':')[-1].strip()
|
||||
in_schema = False
|
||||
elif stripped.startswith('field:'):
|
||||
current_field = stripped.split(':')[-1].strip()
|
||||
if current_collection == 'visual_feedback_comments' and current_field == 'feedback_id':
|
||||
snapshot_has_feedback_id = True
|
||||
elif stripped == 'schema:':
|
||||
in_schema = True
|
||||
elif stripped == 'meta:' or stripped.startswith('- collection:') or (not line.startswith(' ') and line.strip() and not line.startswith('-')):
|
||||
in_schema = False
|
||||
|
||||
# Top-level field type
|
||||
if not in_schema and stripped.startswith('type:') and current_field in uuid_fields:
|
||||
line = line.replace('type: string', 'type: uuid')
|
||||
|
||||
# Schema data type
|
||||
if in_schema and current_field in fix_fields:
|
||||
if 'data_type: char' in line or 'data_type: varchar' in line:
|
||||
line = line.replace('data_type: char', 'data_type: uuid').replace('data_type: varchar', 'data_type: uuid')
|
||||
if 'max_length:' in line:
|
||||
line = ' max_length: null\n'
|
||||
|
||||
new_lines.append(line)
|
||||
|
||||
# Handle Missing feedback_id Injection
|
||||
if not snapshot_has_feedback_id:
|
||||
# We find systemFields and inject before it
|
||||
injected = False
|
||||
final_lines = []
|
||||
feedback_id_block = """ - collection: visual_feedback_comments
|
||||
field: feedback_id
|
||||
type: integer
|
||||
meta:
|
||||
collection: visual_feedback_comments
|
||||
field: feedback_id
|
||||
interface: select-dropdown-m2o
|
||||
required: true
|
||||
sort: 4
|
||||
width: full
|
||||
schema:
|
||||
name: feedback_id
|
||||
table: visual_feedback_comments
|
||||
data_type: integer
|
||||
is_nullable: false
|
||||
is_indexed: true
|
||||
foreign_key_table: visual_feedback
|
||||
foreign_key_column: id
|
||||
"""
|
||||
for line in new_lines:
|
||||
if 'systemFields:' in line and not injected:
|
||||
final_lines.append(feedback_id_block)
|
||||
injected = True
|
||||
final_lines.append(line)
|
||||
new_lines = final_lines
|
||||
|
||||
# Second pass for primary key nullability
|
||||
final_lines = []
|
||||
for i in range(len(new_lines)):
|
||||
line = new_lines[i]
|
||||
if 'is_primary_key: true' in line:
|
||||
# Search backwards and forwards
|
||||
for j in range(max(0, i-10), min(len(new_lines), i+10)):
|
||||
if 'is_nullable: true' in new_lines[j]:
|
||||
new_lines[j] = new_lines[j].replace('is_nullable: true', 'is_nullable: false')
|
||||
final_lines.append(line)
|
||||
|
||||
with open(path, 'w') as f:
|
||||
f.writelines(new_lines)
|
||||
|
||||
print("SUCCESS: Full normalization and field injection complete.")
|
||||
123
scripts/sync-directus.sh
Executable file
123
scripts/sync-directus.sh
Executable file
@@ -0,0 +1,123 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Configuration
|
||||
REMOTE_HOST="root@infra.mintel.me"
|
||||
REMOTE_DIR="/opt/infra/directus"
|
||||
|
||||
# DB Details (matching docker-compose defaults)
|
||||
DB_USER="directus"
|
||||
DB_NAME="directus"
|
||||
|
||||
ACTION=$1
|
||||
ENV=$2
|
||||
|
||||
# Help
|
||||
if [ -z "$ACTION" ] || [ -z "$ENV" ]; then
|
||||
echo "Usage: ./scripts/sync-directus.sh [push|pull] [infra|testing|staging|production]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " push Sync LOCAL data -> REMOTE"
|
||||
echo " pull Sync REMOTE data -> LOCAL"
|
||||
echo ""
|
||||
echo "Environments:"
|
||||
echo " infra (infra.mintel.me)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Map Environment
|
||||
case $ENV in
|
||||
infra)
|
||||
PROJECT_NAME="directus"
|
||||
;;
|
||||
*)
|
||||
echo "❌ Invalid environment: $ENV. Only 'infra' is currently configured for monorepo sync."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Detect local containers
|
||||
echo "🔍 Detecting local database..."
|
||||
LOCAL_DB_CONTAINER=$(docker compose ps -q directus-db)
|
||||
if [ -z "$LOCAL_DB_CONTAINER" ]; then
|
||||
echo "❌ Local directus-db container not found. Is it running? (npm run dev)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$ACTION" == "push" ]; then
|
||||
echo "🚀 Pushing Local Data to $ENV..."
|
||||
|
||||
# 1. DB Dump
|
||||
echo "📦 Dumping local database..."
|
||||
docker exec "$LOCAL_DB_CONTAINER" pg_dump -U "$DB_USER" --clean --if-exists --no-owner --no-privileges "$DB_NAME" > dump.sql
|
||||
|
||||
# 2. Upload Dump
|
||||
echo "📤 Uploading dump to remote server..."
|
||||
scp dump.sql "$REMOTE_HOST:$REMOTE_DIR/dump.sql"
|
||||
|
||||
# 3. Restore on Remote
|
||||
echo "🔄 Restoring dump on $ENV..."
|
||||
REMOTE_DB_CONTAINER=$(ssh "$REMOTE_HOST" "cd $REMOTE_DIR && docker compose -p $PROJECT_NAME ps -q directus-postgres")
|
||||
|
||||
if [ -z "$REMOTE_DB_CONTAINER" ]; then
|
||||
echo "❌ Remote $ENV-db container not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Wipe remote DB clean before restore to avoid constraint errors
|
||||
echo "🧹 Wiping remote database schema..."
|
||||
ssh "$REMOTE_HOST" "docker exec $REMOTE_DB_CONTAINER psql -U $DB_USER $DB_NAME -c 'DROP SCHEMA public CASCADE; CREATE SCHEMA public;'"
|
||||
|
||||
echo "⚡ Restoring database..."
|
||||
ssh "$REMOTE_HOST" "docker exec -i $REMOTE_DB_CONTAINER psql -U $DB_USER $DB_NAME < $REMOTE_DIR/dump.sql"
|
||||
|
||||
# 4. Sync Uploads
|
||||
echo "📁 Syncing uploads (Local -> $ENV)..."
|
||||
rsync -avz --progress ./directus/uploads/ "$REMOTE_HOST:$REMOTE_DIR/uploads/"
|
||||
|
||||
# Clean up
|
||||
rm dump.sql
|
||||
ssh "$REMOTE_HOST" "rm $REMOTE_DIR/dump.sql"
|
||||
|
||||
# 5. Restart Directus to trigger migrations and refresh schema cache
|
||||
echo "🔄 Restarting remote Directus to apply migrations..."
|
||||
ssh "$REMOTE_HOST" "cd $REMOTE_DIR && docker compose -p $PROJECT_NAME restart directus"
|
||||
|
||||
echo "✨ Push to $ENV complete!"
|
||||
|
||||
elif [ "$ACTION" == "pull" ]; then
|
||||
echo "📥 Pulling $ENV Data to Local..."
|
||||
|
||||
# 1. DB Dump on Remote
|
||||
echo "📦 Dumping remote database ($ENV)..."
|
||||
REMOTE_DB_CONTAINER=$(ssh "$REMOTE_HOST" "cd $REMOTE_DIR && docker compose -p $PROJECT_NAME ps -q directus-postgres")
|
||||
|
||||
if [ -z "$REMOTE_DB_CONTAINER" ]; then
|
||||
echo "❌ Remote $ENV-db container not found!"
|
||||
exit 1
|
||||
fi
|
||||
ssh "$REMOTE_HOST" "docker exec $REMOTE_DB_CONTAINER pg_dump -U $DB_USER --clean --if-exists --no-owner --no-privileges $DB_NAME > $REMOTE_DIR/dump.sql"
|
||||
|
||||
# 2. Download Dump
|
||||
echo "📥 Downloading dump..."
|
||||
scp "$REMOTE_HOST:$REMOTE_DIR/dump.sql" dump.sql
|
||||
|
||||
# Wipe local DB clean before restore to avoid constraint errors
|
||||
echo "🧹 Wiping local database schema..."
|
||||
docker exec "$LOCAL_DB_CONTAINER" psql -U "$DB_USER" "$DB_NAME" -c 'DROP SCHEMA public CASCADE; CREATE SCHEMA public;'
|
||||
|
||||
echo "⚡ Restoring database locally..."
|
||||
docker exec -i "$LOCAL_DB_CONTAINER" psql -U "$DB_USER" "$DB_NAME" < dump.sql
|
||||
|
||||
# 4. Sync Uploads
|
||||
echo "📁 Syncing uploads ($ENV -> Local)..."
|
||||
rsync -avz --progress "$REMOTE_HOST:$REMOTE_DIR/uploads/" ./directus/uploads/
|
||||
|
||||
# Clean up
|
||||
rm dump.sql
|
||||
ssh "$REMOTE_HOST" "rm $REMOTE_DIR/dump.sql"
|
||||
|
||||
echo "✨ Pull to Local complete!"
|
||||
else
|
||||
echo "Invalid action: $ACTION. Use push or pull."
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,11 +1,51 @@
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
const tag = process.env.GITHUB_REF_NAME || process.env.TAG;
|
||||
import { execSync } from "child_process";
|
||||
|
||||
if (!tag || !tag.startsWith("v")) {
|
||||
console.error("❌ No valid tag found (must start with v, e.g., v1.0.0)");
|
||||
process.exit(1);
|
||||
/**
|
||||
* Gets the current version tag from arguments, environment or git.
|
||||
*/
|
||||
function getVersionTag() {
|
||||
// 0. Check arguments (passed from husky hook or manual run)
|
||||
const argTag = process.argv.slice(2).find((arg) => arg.startsWith("v"));
|
||||
if (argTag) {
|
||||
return argTag;
|
||||
}
|
||||
|
||||
// 1. Check CI environment variables
|
||||
if (
|
||||
process.env.GITHUB_REF_NAME &&
|
||||
process.env.GITHUB_REF_NAME.startsWith("v")
|
||||
) {
|
||||
return process.env.GITHUB_REF_NAME;
|
||||
}
|
||||
if (process.env.TAG && process.env.TAG.startsWith("v")) {
|
||||
return process.env.TAG;
|
||||
}
|
||||
|
||||
// 2. Try to get it from local git
|
||||
try {
|
||||
const gitTag = execSync("git describe --tags --abbrev=0", {
|
||||
encoding: "utf8",
|
||||
}).trim();
|
||||
if (gitTag && gitTag.startsWith("v")) {
|
||||
return gitTag;
|
||||
}
|
||||
} catch (e) {
|
||||
// Fallback or silence
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const tag = getVersionTag();
|
||||
|
||||
if (!tag) {
|
||||
console.log(
|
||||
"ℹ️ No version tag found (starting with v). Skipping version sync.",
|
||||
);
|
||||
process.exit(0); // Exit gracefully if no tag is present
|
||||
}
|
||||
|
||||
const version = tag.replace(/^v/, "");
|
||||
@@ -33,20 +73,50 @@ function updatePkg(pkgPath: string) {
|
||||
console.log(`✅ Updated ${pkg.name} to ${version}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the IMAGE_TAG in .env files.
|
||||
*/
|
||||
function updateEnv(envPath: string) {
|
||||
if (!fs.existsSync(envPath)) return;
|
||||
let content = fs.readFileSync(envPath, "utf-8");
|
||||
|
||||
if (content.includes("IMAGE_TAG=")) {
|
||||
content = content.replace(/IMAGE_TAG=.*/g, `IMAGE_TAG=${tag}`);
|
||||
} else {
|
||||
// Proactively add it if missing
|
||||
if (content.includes("# Project")) {
|
||||
content = content.replace("# Project", `# Project\nIMAGE_TAG=${tag}`);
|
||||
} else {
|
||||
content = `IMAGE_TAG=${tag}\n${content}`;
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync(envPath, content);
|
||||
console.log(`✅ Updated IMAGE_TAG in ${envPath} to ${tag}`);
|
||||
}
|
||||
|
||||
// Update root
|
||||
rootPkg.version = version;
|
||||
fs.writeFileSync("package.json", JSON.stringify(rootPkg, null, 2) + "\n");
|
||||
|
||||
// Update all packages
|
||||
const packages = fs.readdirSync(packagesDir);
|
||||
for (const p of packages) {
|
||||
updatePkg(path.join(packagesDir, p, "package.json"));
|
||||
if (fs.existsSync(packagesDir)) {
|
||||
const packages = fs.readdirSync(packagesDir);
|
||||
for (const p of packages) {
|
||||
updatePkg(path.join(packagesDir, p, "package.json"));
|
||||
}
|
||||
}
|
||||
|
||||
// Update all apps
|
||||
const apps = fs.readdirSync(appsDir);
|
||||
for (const a of apps) {
|
||||
updatePkg(path.join(appsDir, a, "package.json"));
|
||||
if (fs.existsSync(appsDir)) {
|
||||
const apps = fs.readdirSync(appsDir);
|
||||
for (const a of apps) {
|
||||
updatePkg(path.join(appsDir, a, "package.json"));
|
||||
}
|
||||
}
|
||||
|
||||
// Update .env files
|
||||
updateEnv(".env");
|
||||
updateEnv(".env.example");
|
||||
|
||||
console.log("✨ All versions synced!");
|
||||
|
||||
Reference in New Issue
Block a user