diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 52537b14..70a96445 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -226,21 +226,6 @@ jobs: tags: registry.infra.mintel.me/mintel/klz-2026:${{ needs.prepare.outputs.image_tag }} secrets: | NPM_TOKEN=${{ secrets.NPM_TOKEN }} - - name: 🔄 Build and Push Migrator - uses: docker/build-push-action@v5 - with: - context: . - push: true - provenance: false - platforms: linux/amd64 - target: migrator - build-args: | - NEXT_PUBLIC_BASE_URL=${{ needs.prepare.outputs.next_public_url }} - NEXT_PUBLIC_TARGET=${{ needs.prepare.outputs.target }} - NPM_TOKEN=${{ secrets.NPM_TOKEN }} - tags: registry.infra.mintel.me/mintel/klz-2026:migrate-${{ needs.prepare.outputs.image_tag }} - secrets: | - NPM_TOKEN=${{ secrets.NPM_TOKEN }} # ────────────────────────────────────────────────────────────────────────────── # JOB 4: Deploy @@ -284,6 +269,11 @@ jobs: UMAMI_WEBSITE_ID: ${{ secrets.UMAMI_WEBSITE_ID || vars.UMAMI_WEBSITE_ID }} UMAMI_API_ENDPOINT: ${{ secrets.UMAMI_API_ENDPOINT || vars.UMAMI_API_ENDPOINT || 'https://analytics.infra.mintel.me' }} + # Search & AI + OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY || vars.OPENROUTER_API_KEY }} + QDRANT_URL: ${{ secrets.QDRANT_URL || vars.QDRANT_URL || 'http://klz-qdrant:6333' }} + QDRANT_API_KEY: ${{ secrets.QDRANT_API_KEY || vars.QDRANT_API_KEY }} + REDIS_URL: ${{ secrets.REDIS_URL || vars.REDIS_URL || 'redis://klz-redis:6379' }} # Container Registry (standalone) REGISTRY_USER: ${{ secrets.REGISTRY_USER }} REGISTRY_PASS: ${{ secrets.REGISTRY_PASS }} @@ -345,6 +335,12 @@ jobs: echo "UMAMI_WEBSITE_ID=$UMAMI_WEBSITE_ID" echo "UMAMI_API_ENDPOINT=$UMAMI_API_ENDPOINT" echo "" + echo "# Search & AI" + echo "OPENROUTER_API_KEY=$OPENROUTER_API_KEY" + echo "QDRANT_URL=$QDRANT_URL" + echo "QDRANT_API_KEY=$QDRANT_API_KEY" + echo "REDIS_URL=$REDIS_URL" + echo "" echo "TARGET=$TARGET" echo "SENTRY_ENVIRONMENT=$TARGET" echo "PROJECT_NAME=$PROJECT_NAME" @@ -364,6 +360,32 @@ jobs: cat .env.deploy echo "----------------------------" + - name: 🔐 Registry Auth + id: auth + run: | + echo "Testing available secrets against git.infra.mintel.me Docker registry..." + TOKENS="${{ secrets.GITEA_PAT }} ${{ secrets.MINTEL_PRIVATE_TOKEN }} ${{ secrets.NPM_TOKEN }}" + USERS="${{ github.repository_owner }} ${{ github.actor }} marcmintel mintel mmintel" + + VALID_TOKEN="" + VALID_USER="" + for T in $TOKENS; do + if [ -n "$T" ]; then + for U in $USERS; do + if [ -n "$U" ]; then + if echo "$T" | docker login git.infra.mintel.me -u "$U" --password-stdin > /dev/null 2>&1; then + VALID_TOKEN="$T" + VALID_USER="$U" + break 2 + fi + fi + done + fi + done + if [ -z "$VALID_TOKEN" ]; then echo "❌ All tokens failed to authenticate!"; exit 1; fi + echo "token=$VALID_TOKEN" >> $GITHUB_OUTPUT + echo "user=$VALID_USER" >> $GITHUB_OUTPUT + - name: 🚀 SSH Deploy shell: bash env: @@ -374,6 +396,9 @@ jobs: chmod 600 ~/.ssh/id_ed25519 ssh-keyscan -H alpha.mintel.me >> ~/.ssh/known_hosts 2>/dev/null + # Determine deployment paths + echo "Preparing deployment for $TARGET..." + # Transfer and Restart if [[ "$TARGET" == "production" ]]; then SITE_DIR="/home/deploy/sites/klz-cables.com" @@ -411,24 +436,31 @@ jobs: REMOTE_DB_USER="${REMOTE_DB_USER:-payload}" REMOTE_DB_NAME="${REMOTE_DB_NAME:-payload}" - # Run Payload migrations via a temporary container before restarting the app. - # This ensures fresh branch deployments (empty DBs) get their schema on first deploy. - echo "🔄 Running Payload migrations..." - MIGRATOR_IMAGE="registry.infra.mintel.me/mintel/klz-2026:migrate-$IMAGE_TAG" - - ssh root@alpha.mintel.me " - echo '${{ steps.auth.outputs.token }}' | docker login registry.infra.mintel.me -u '${{ steps.auth.outputs.user }}' --password-stdin 2>/dev/null || true - docker pull $MIGRATOR_IMAGE - docker run --rm \ - --network ${PROJECT_NAME}_default \ - --env-file $SITE_DIR/$ENV_FILE \ - $MIGRATOR_IMAGE \ - && echo '✅ Migrations complete.' \ - || echo '⚠️ Migrations failed or already up-to-date — continuing.' - " + # Auto-detect migrations from src/migrations/*.ts + BATCH=1 + VALUES="" + for f in $(ls src/migrations/*.ts 2>/dev/null | sort); do + NAME=$(basename "$f" .ts) + [ -n "$VALUES" ] && VALUES="$VALUES," + VALUES="$VALUES ('$NAME', $BATCH)" + ((BATCH++)) + done + + if [ -n "$VALUES" ]; then + echo " + DO \$\$ BEGIN + DELETE FROM payload_migrations WHERE batch = -1; + INSERT INTO payload_migrations (name, batch) + SELECT name, batch FROM (VALUES $VALUES) AS v(name, batch) + WHERE NOT EXISTS (SELECT 1 FROM payload_migrations pm WHERE pm.name = v.name); + EXCEPTION WHEN undefined_table THEN + RAISE NOTICE 'payload_migrations table does not exist yet — skipping sanitization'; + END \$\$; + " | ssh root@alpha.mintel.me "docker exec -i $DB_CONTAINER psql -U $REMOTE_DB_USER -d $REMOTE_DB_NAME" + fi # Restart app to pick up clean migration state - APP_CONTAINER="${PROJECT_NAME}-klz-app-1" + APP_CONTAINER="${{ needs.prepare.outputs.project_name }}-klz-app-1" ssh root@alpha.mintel.me "docker restart $APP_CONTAINER" ssh root@alpha.mintel.me "docker system prune -f --filter 'until=24h'" @@ -588,8 +620,10 @@ jobs: STATUS_LINE="All checks passed" fi - TITLE="$EMOJI klz-cables.com $VERSION -> $TARGET" - MESSAGE="$STATUS_LINE | Deploy: $DEPLOY | Smoke: $SMOKE | $URL" + TITLE="$EMOJI klz-cables.com $VERSION → $TARGET" + MESSAGE="$STATUS_LINE + Deploy: $DEPLOY | Smoke: $SMOKE | Perf: $PERF + $URL" curl -s -k -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \ -F "title=$TITLE" \ diff --git a/Dockerfile b/Dockerfile index 87d9b843..c5406d8b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -64,3 +64,4 @@ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static COPY --from=builder --chown=nextjs:nodejs /app/.next/cache ./.next/cache CMD ["node", "server.js"] + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 54d0589a..e7fa0aaf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,52 +25,52 @@ importers: specifier: ^1.8.21 version: 1.8.21(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.19)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.97.3)(typescript@5.9.3) '@payloadcms/db-postgres': - specifier: ^3.79.0 + specifier: ^3.77.0 version: 3.79.0(@opentelemetry/api@1.9.0)(payload@3.79.0(graphql@16.13.1)(typescript@5.9.3)) '@payloadcms/email-nodemailer': - specifier: ^3.79.0 + specifier: ^3.77.0 version: 3.79.0(payload@3.79.0(graphql@16.13.1)(typescript@5.9.3)) '@payloadcms/next': - specifier: ^3.79.0 + specifier: ^3.77.0 version: 3.79.0(@types/react@19.2.14)(graphql@16.13.1)(monaco-editor@0.55.1)(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)(sass@1.97.3))(payload@3.79.0(graphql@16.13.1)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) '@payloadcms/richtext-lexical': - specifier: ^3.79.0 + specifier: ^3.77.0 version: 3.79.0(@faceless-ui/modal@3.0.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@faceless-ui/scroll-info@2.0.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@payloadcms/next@3.79.0(@types/react@19.2.14)(graphql@16.13.1)(monaco-editor@0.55.1)(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)(sass@1.97.3))(payload@3.79.0(graphql@16.13.1)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(@types/react@19.2.14)(monaco-editor@0.55.1)(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)(sass@1.97.3))(payload@3.79.0(graphql@16.13.1)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)(yjs@13.6.29) '@payloadcms/ui': - specifier: ^3.79.0 + specifier: ^3.77.0 version: 3.79.0(@types/react@19.2.14)(monaco-editor@0.55.1)(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)(sass@1.97.3))(payload@3.79.0(graphql@16.13.1)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) '@react-email/components': - specifier: ^1.0.8 + specifier: ^1.0.7 version: 1.0.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@react-pdf/renderer': specifier: ^4.3.2 version: 4.3.2(react@19.2.4) '@sentry/nextjs': - specifier: ^10.42.0 + specifier: ^10.39.0 version: 10.42.0(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.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)(sass@1.97.3))(react@19.2.4)(webpack@5.105.0(esbuild@0.25.12)) '@types/recharts': specifier: ^2.0.1 version: 2.0.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react-is@16.13.1)(react@19.2.4)(redux@5.0.1) axios: - specifier: ^1.13.6 + specifier: ^1.13.5 version: 1.13.6(debug@4.4.3) clsx: specifier: ^2.1.1 version: 2.1.1 framer-motion: - specifier: ^12.35.0 + specifier: ^12.34.0 version: 12.35.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) graphql: - specifier: ^16.13.1 + specifier: ^16.12.0 version: 16.13.1 gray-matter: specifier: ^4.0.3 version: 4.0.3 i18next: - specifier: ^25.8.14 + specifier: ^25.7.3 version: 25.8.14(typescript@5.9.3) import-in-the-middle: - specifier: ^1.15.0 + specifier: ^1.11.0 version: 1.15.0 jsdom: specifier: ^27.4.0 @@ -85,19 +85,19 @@ importers: specifier: ^15.4.3 version: 15.4.3(@types/react@19.2.14)(i18next@25.8.14(typescript@5.9.3))(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)(sass@1.97.3))(react-i18next@16.5.4(i18next@25.8.14(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react@19.2.4) next-intl: - specifier: ^4.8.3 + specifier: ^4.8.2 version: 4.8.3(@swc/helpers@0.5.19)(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)(sass@1.97.3))(react@19.2.4)(typescript@5.9.3) nodemailer: - specifier: ^7.0.13 + specifier: ^7.0.12 version: 7.0.13 payload: - specifier: ^3.79.0 + specifier: ^3.77.0 version: 3.79.0(graphql@16.13.1)(typescript@5.9.3) pdf-lib: specifier: ^1.17.1 version: 1.17.1 pino: - specifier: ^10.3.1 + specifier: ^10.3.0 version: 10.3.1 pino-pretty: specifier: ^13.1.3 @@ -109,7 +109,7 @@ importers: specifier: ^19.2.4 version: 19.2.4(react@19.2.4) react-email: - specifier: ^5.2.9 + specifier: ^5.2.5 version: 5.2.9 react-leaflet: specifier: ^4.2.1 @@ -133,7 +133,7 @@ importers: specifier: ^0.1.8 version: 0.1.8 tailwind-merge: - specifier: ^3.5.0 + specifier: ^3.4.0 version: 3.5.0 xlsx: specifier: npm:@e965/xlsx@^0.20.3 @@ -143,10 +143,10 @@ importers: version: 3.25.76 devDependencies: '@commitlint/cli': - specifier: ^20.4.3 + specifier: ^20.4.0 version: 20.4.3(@types/node@22.19.13)(typescript@5.9.3) '@commitlint/config-conventional': - specifier: ^20.4.3 + specifier: ^20.4.0 version: 20.4.3 '@cspell/dict-de-de': specifier: ^4.1.2 @@ -164,10 +164,10 @@ importers: specifier: ^16.1.6 version: 16.1.6 '@tailwindcss/cli': - specifier: ^4.2.1 + specifier: ^4.1.18 version: 4.2.1 '@tailwindcss/postcss': - specifier: ^4.2.1 + specifier: ^4.1.18 version: 4.2.1 '@types/geojson': specifier: ^7946.0.16 @@ -176,13 +176,13 @@ importers: specifier: ^1.9.21 version: 1.9.21 '@types/node': - specifier: ^22.19.13 + specifier: ^22.19.3 version: 22.19.13 '@types/nodemailer': - specifier: ^7.0.11 + specifier: ^7.0.5 version: 7.0.11 '@types/react': - specifier: ^19.2.14 + specifier: ^19.2.7 version: 19.2.14 '@types/react-dom': specifier: ^19.2.3 @@ -197,10 +197,10 @@ importers: specifier: ^5.1.4 version: 5.1.4(vite@7.3.1(@types/node@22.19.13)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/ui': - specifier: ^4.0.18 + specifier: ^4.0.16 version: 4.0.18(vitest@4.0.18) autoprefixer: - specifier: ^10.4.27 + specifier: ^10.4.23 version: 10.4.27(postcss@8.5.8) cheerio: specifier: ^1.2.0 @@ -209,61 +209,61 @@ importers: specifier: ^0.0.25 version: 0.0.25 cspell: - specifier: ^9.7.0 + specifier: ^9.6.4 version: 9.7.0 dotenv: specifier: ^17.3.1 version: 17.3.1 eslint: - specifier: ^9.39.3 + specifier: ^9.18.0 version: 9.39.3(jiti@2.6.1) happy-dom: - specifier: ^20.8.3 + specifier: ^20.6.1 version: 20.8.3 html-validate: - specifier: ^10.11.0 + specifier: ^10.8.0 version: 10.11.0(vitest@4.0.18) husky: specifier: ^9.1.7 version: 9.1.7 lint-staged: - specifier: ^16.3.2 + specifier: ^16.2.7 version: 16.3.2 lucide-react: specifier: ^0.563.0 version: 0.563.0(react@19.2.4) pa11y-ci: - specifier: ^4.1.0 + specifier: ^4.0.1 version: 4.1.0(typescript@5.9.3) postcss: - specifier: ^8.5.8 + specifier: ^8.5.6 version: 8.5.8 prettier: specifier: ^3.8.1 version: 3.8.1 puppeteer: - specifier: ^24.38.0 + specifier: ^24.37.3 version: 24.38.0(typescript@5.9.3) sass: - specifier: ^1.97.3 + specifier: ^1.97.1 version: 1.97.3 start-server-and-test: - specifier: ^2.1.5 + specifier: ^2.1.3 version: 2.1.5 tailwindcss: - specifier: ^4.2.1 + specifier: ^4.1.18 version: 4.2.1 tsx: specifier: ^4.21.0 version: 4.21.0 turbo: - specifier: ^2.8.13 + specifier: ^2.8.10 version: 2.8.13 typescript: - specifier: ^5.9.3 + specifier: ^5.7.2 version: 5.9.3 vitest: - specifier: ^4.0.18 + specifier: ^4.0.16 version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@22.19.13)(@vitest/ui@4.0.18)(happy-dom@20.8.3)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) packages: