diff --git a/docker-compose.yml b/docker-compose.yml index f9625253..ca26f5bc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -74,14 +74,6 @@ services: - "traefik.http.routers.${PROJECT_NAME:-klz}-gatekeeper.service=${PROJECT_NAME:-klz}-gatekeeper-svc" - "traefik.http.routers.${PROJECT_NAME:-klz}-gatekeeper.priority=2001" - # Gatekeeper Public Router (Login/Auth UI) - - "traefik.http.routers.${PROJECT_NAME:-klz}-gatekeeper.rule=(${TRAEFIK_HOST_RULE:-Host(`${TRAEFIK_HOST:-klz-cables.com}`)}) && PathRegexp(`^/(login|gatekeeper)(/.*)?`)" - - "traefik.http.routers.${PROJECT_NAME:-klz}-gatekeeper.entrypoints=${TRAEFIK_ENTRYPOINT:-web}" - - "traefik.http.routers.${PROJECT_NAME:-klz}-gatekeeper.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-}" - - "traefik.http.routers.${PROJECT_NAME:-klz}-gatekeeper.tls=${TRAEFIK_TLS:-false}" - - "traefik.http.routers.${PROJECT_NAME:-klz}-gatekeeper.service=${PROJECT_NAME:-klz}-gatekeeper-svc" - - "traefik.http.routers.${PROJECT_NAME:-klz}-gatekeeper.priority=2001" - klz-db: image: postgres:15-alpine restart: unless-stopped diff --git a/package.json b/package.json index 57136e99..fd643417 100644 --- a/package.json +++ b/package.json @@ -110,15 +110,18 @@ "check:security": "tsx ./scripts/check-security.ts", "check:links": "bash ./scripts/check-links.sh", "check:assets": "tsx ./scripts/check-broken-assets.ts", - "cms:branding:local": "DIRECTUS_URL=${DIRECTUS_URL:-http://cms.klz.localhost} npx tsx --env-file=.env scripts/setup-directus-branding.ts", - "cms:branding:testing": "DIRECTUS_URL=https://cms.testing.klz-cables.com npx tsx --env-file=.env scripts/setup-directus-branding.ts", - "cms:branding:staging": "DIRECTUS_URL=https://cms.staging.klz-cables.com npx tsx --env-file=.env scripts/setup-directus-branding.ts", - "cms:branding:prod": "DIRECTUS_URL=https://cms.klz-cables.com npx tsx --env-file=.env scripts/setup-directus-branding.ts", - "cms:bootstrap": "pnpm run cms:branding:local", "pdf:datasheets": "tsx ./scripts/generate-pdf-datasheets.ts", "pdf:datasheets:legacy": "tsx ./scripts/generate-pdf-datasheets-pdf-lib.ts", "cms:migrate": "payload migrate", "cms:seed": "tsx ./scripts/seed-payload.ts", + "assets:push:testing": "bash ./scripts/assets-sync.sh local testing", + "assets:push:staging": "bash ./scripts/assets-sync.sh local staging", + "assets:push:prod": "bash ./scripts/assets-sync.sh local prod", + "assets:pull:testing": "bash ./scripts/assets-sync.sh testing local", + "assets:pull:staging": "bash ./scripts/assets-sync.sh staging local", + "assets:pull:prod": "bash ./scripts/assets-sync.sh prod local", + "assets:sync:testing-to-staging": "bash ./scripts/assets-sync.sh testing staging", + "assets:sync:staging-to-prod": "bash ./scripts/assets-sync.sh staging prod", "pagespeed:test": "tsx ./scripts/pagespeed-sitemap.ts", "pagespeed:audit": "./scripts/audit-local.sh", "pagespeed:urls": "tsx -e \"import sitemap from './app/sitemap'; sitemap().then(urls => console.log(urls.map(u => u.url).join('\\n')))\"", diff --git a/scripts/assets-sync.sh b/scripts/assets-sync.sh new file mode 100755 index 00000000..fdb30cc8 --- /dev/null +++ b/scripts/assets-sync.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# ──────────────────────────────────────────────────────────────────────────── +# Asset Sync Tool +# Syncs media files between environments without touching the database. +# ──────────────────────────────────────────────────────────────────────────── +set -euo pipefail + +# Load environment variables +if [ -f .env ]; then + set -a; source .env; set +a +fi + +# ── Configuration ────────────────────────────────────────────────────────── +SOURCE_ENV="${1:-}" # local | testing | staging | prod +TARGET_ENV="${2:-}" # testing | staging | prod +SSH_HOST="root@alpha.mintel.me" +LOCAL_MEDIA_DIR="./public/media" + +DRY_RUN="" +CHECKSUM="" +if [[ "$*" == *"--dry-run"* ]]; then + DRY_RUN="--dry-run" + echo "🏃 DRY RUN MODE ENABLED" +fi +if [[ "$*" == *"--checksum"* ]]; then + CHECKSUM="-c" + echo "🔍 CHECKSUM MODE ENABLED (Slower but more reliable)" +fi + +# ── Resolve Paths ────────────────────────────────────────────────────────── +get_media_path() { + case "$1" in + local) echo "$LOCAL_MEDIA_DIR" ;; + testing) echo "/var/lib/docker/volumes/klz-testing_klz_media_data/_data" ;; + staging) echo "/var/lib/docker/volumes/klz-staging_klz_media_data/_data" ;; + prod|production) echo "/var/lib/docker/volumes/klz-cablescom_klz_media_data/_data" ;; + *) echo "❌ Unknown environment: $1"; exit 1 ;; + esac +} + +get_app_container() { + case "$1" in + testing) echo "klz-testing-klz-app-1" ;; + staging) echo "klz-staging-klz-app-1" ;; + prod|production) echo "klz-cablescom-klz-app-1" ;; + *) echo "" ;; + esac +} + +SRC_PATH=$(get_media_path "$SOURCE_ENV") +TGT_PATH=$(get_media_path "$TARGET_ENV") +TGT_CONTAINER=$(get_app_container "$TARGET_ENV") + +echo "🚀 Syncing assets: $SOURCE_ENV → $TARGET_ENV" +echo "📂 Source: $SRC_PATH" +echo "📂 Target: $TGT_PATH" + +# ── Execution ────────────────────────────────────────────────────────────── + +if [[ ! -d "$SRC_PATH" ]] && [[ "$SOURCE_ENV" == "local" ]]; then + echo "❌ Source directory does not exist: $SRC_PATH" + exit 1 +fi + +if [[ "$SOURCE_ENV" == "local" ]]; then + # Local → Remote + echo "📡 Running rsync..." + rsync -avzi $CHECKSUM --delete --progress $DRY_RUN "$SRC_PATH/" "$SSH_HOST:$TGT_PATH/" +elif [[ "$TARGET_ENV" == "local" ]]; then + # Remote → Local + mkdir -p "$LOCAL_MEDIA_DIR" + echo "📡 Running rsync..." + rsync -avzi $CHECKSUM --delete --progress $DRY_RUN "$SSH_HOST:$SRC_PATH/" "$TGT_PATH/" +else + # Remote → Remote (e.g., testing → staging) + echo "📡 Running remote rsync..." + ssh "$SSH_HOST" "rsync -avzi $CHECKSUM --delete --progress $DRY_RUN $SRC_PATH/ $TGT_PATH/" +fi + +# Fix ownership on remote target if it's not local +if [[ "$TARGET_ENV" != "local" && -z "$DRY_RUN" ]]; then + echo "🔑 Fixing media file permissions on $TARGET_ENV..." + ssh "$SSH_HOST" "docker exec -u 0 $TGT_CONTAINER chown -R 1001:65533 /app/public/media/ 2>/dev/null || true" +fi + +echo "✅ Asset sync complete!" diff --git a/scripts/seed-payload.ts b/scripts/seed-payload.ts new file mode 100644 index 00000000..e182a1da --- /dev/null +++ b/scripts/seed-payload.ts @@ -0,0 +1,22 @@ +/** + * CLI wrapper for seeding the Payload CMS database. + * Usage: pnpm tsx scripts/seed-payload.ts + */ +import { getPayload } from 'payload'; +import configPromise from '../payload.config'; +import { seedDatabase } from '../src/payload/seed'; + +async function run() { + const payload = await getPayload({ config: configPromise }); + + console.log('🌱 Starting database seed...'); + await seedDatabase(payload); + console.log('✅ Seeding complete.'); + + process.exit(0); +} + +run().catch((err) => { + console.error('❌ Seeding failed:', err); + process.exit(1); +});