diff --git a/package.json b/package.json index e5f0d3b..0a5d552 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "pagespeed:test": "mintel pagespeed test", "check:http": "tsx ./scripts/check-http.ts", "check:apis": "tsx ./scripts/check-apis.ts", - "check:locale": "tsx ./scripts/check-locale.ts" + "check:locale": "tsx ./scripts/check-locale.ts", + "backup:db": "bash ./scripts/backup-db.sh" }, "keywords": [], "author": "", @@ -35,6 +36,8 @@ "@payloadcms/ui": "^3.77.0", "@react-email/components": "^1.0.8", "@sentry/nextjs": "^10.38.0", + "bcrypt": "^6.0.0", + "bcryptjs": "^3.0.3", "framer-motion": "^12.29.2", "graphql": "^16.13.0", "lucide-react": "^0.562.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0b50685..4734353 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,6 +44,12 @@ importers: '@sentry/nextjs': specifier: ^10.38.0 version: 10.38.0(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.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)(sass@1.77.4))(react@19.2.4)(webpack@5.104.1(esbuild@0.25.12)) + bcrypt: + specifier: ^6.0.0 + version: 6.0.0 + bcryptjs: + specifier: ^3.0.3 + version: 3.0.3 framer-motion: specifier: ^12.29.2 version: 12.30.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -3482,6 +3488,14 @@ packages: resolution: {integrity: sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==} hasBin: true + bcrypt@6.0.0: + resolution: {integrity: sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==} + engines: {node: '>= 18'} + + bcryptjs@3.0.3: + resolution: {integrity: sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==} + hasBin: true + bidi-js@1.0.3: resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} @@ -5453,6 +5467,10 @@ packages: node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-addon-api@8.6.0: + resolution: {integrity: sha512-gBVjCaqDlRUk0EwoPNKzIr9KkS9041G/q31IBShPs1Xz6UTA+EXdZADbzqAJQrpDRq71CIMnOP5VMut3SL0z5Q==} + engines: {node: ^18 || ^20 || >= 21} + node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -5462,6 +5480,10 @@ packages: encoding: optional: true + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} @@ -10757,6 +10779,13 @@ snapshots: baseline-browser-mapping@2.9.19: {} + bcrypt@6.0.0: + dependencies: + node-addon-api: 8.6.0 + node-gyp-build: 4.8.4 + + bcryptjs@3.0.3: {} + bidi-js@1.0.3: dependencies: require-from-string: 2.0.2 @@ -12986,10 +13015,14 @@ snapshots: node-addon-api@7.1.1: {} + node-addon-api@8.6.0: {} + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 + node-gyp-build@4.8.4: {} + node-releases@2.0.27: {} nodemailer@7.0.12: {} diff --git a/scripts/backup-db.sh b/scripts/backup-db.sh new file mode 100644 index 0000000..d315fcf --- /dev/null +++ b/scripts/backup-db.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# ──────────────────────────────────────────────────────────────────────────── +# Payload CMS Database Backup +# Creates a timestamped pg_dump of the Payload Postgres database. +# Usage: npm run backup:db +# ──────────────────────────────────────────────────────────────────────────── +set -euo pipefail + +# Load environment variables +if [ -f .env ]; then + set -a; source .env; set +a +fi + +# Fallback for local development if not in .env +DB_NAME="${POSTGRES_DB:-payload}" +DB_USER="${POSTGRES_USER:-postgres}" +# For production, we need the container name. +# We'll use the PROJECT_NAME to find it if possible, otherwise use a default. +PROJECT_NAME="${PROJECT_NAME:-mb-grid-solutions-production}" +DB_CONTAINER="${DB_CONTAINER:-${PROJECT_NAME}-mb-grid-db-1}" +BACKUP_DIR="./backups" +TIMESTAMP=$(date +"%Y%m%d_%H%M%S") +BACKUP_FILE="${BACKUP_DIR}/payload_${TIMESTAMP}.sql.gz" + +# Ensure backup directory exists +mkdir -p "$BACKUP_DIR" + +# Check if container is running +if ! docker ps --format '{{.Names}}' | grep -q "$DB_CONTAINER"; then + echo "❌ Database container '$DB_CONTAINER' is not running." + echo " Check your docker-compose status." + exit 1 +fi + +echo "📦 Backing up Payload database..." +echo " Container: $DB_CONTAINER" +echo " Database: $DB_NAME" +echo " Output: $BACKUP_FILE" + +# Run pg_dump inside the container and compress +# We use directus as user for now if we haven't fully switched to postgres user in all environments +# But the script should be consistent with the environment. +docker exec "$DB_CONTAINER" pg_dump -U "$DB_USER" -d "$DB_NAME" --clean --if-exists | gzip > "$BACKUP_FILE" + +# Show result +SIZE=$(du -h "$BACKUP_FILE" | cut -f1) +echo "" +echo "✅ Backup complete: $BACKUP_FILE ($SIZE)" +echo "" + +# Show existing backups +echo "📋 Available backups:" +ls -lh "$BACKUP_DIR"/*.sql.gz 2>/dev/null | awk '{print " " $NF " (" $5 ")"}' diff --git a/scripts/create-admin.ts b/scripts/create-admin.ts new file mode 100644 index 0000000..642673b --- /dev/null +++ b/scripts/create-admin.ts @@ -0,0 +1,49 @@ +import { getPayload } from "payload"; +import config from "./src/payload/payload.config"; + +const createAdmin = async () => { + const payload = await getPayload({ config }); + + const email = "marc@mintel.me"; + const password = "Tim300493."; + + console.log(`Creating/Updating admin: ${email}`); + + try { + // Check if user exists + const users = await payload.find({ + collection: "users", + where: { + email: { + equals: email, + }, + }, + }); + + if (users.totalDocs > 0) { + console.log("User already exists. Updating password."); + await payload.update({ + collection: "users", + id: users.docs[0].id, + data: { + password, + }, + }); + } else { + await payload.create({ + collection: "users", + data: { + email, + password, + }, + }); + console.log("Admin user created successfully."); + } + } catch (error) { + console.error("Error creating admin:", error); + process.exit(1); + } + process.exit(0); +}; + +createAdmin();