Add booking stack, client deploys, and back-office updates

- deploy/booking: shared Easy!Appointments stack with brand-matched
  wizard (flatpickr recolor, single-tenant provider hide, iframe
  auto-fit height reporter)
- deploy/clients: per-client isolated nginx compose stacks with
  _template scaffold, new-client.sh, and happynails live site
- deploy/backup: smb-db backup script
- n8n: booking-sync workflow; onboarding tweaks
- playbooks: lead-to-customer lifecycle + outreach
- templates: nail-studio landing previews
- backoffice: app/db/init/compose updates

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 16:10:15 +02:00
parent 4257bd3e55
commit 156166b4e5
25 changed files with 2900 additions and 23 deletions
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
# Scaffold a new ISOLATED client stack from _template/.
#
# Usage: ./new-client.sh <slug> <domain> [source-site-dir]
# ./new-client.sh happynails happynails.de ../../templates/landing/preview-happynails
#
# Creates deploy/clients/<slug>/ with its own .env + site/, then prints the
# Caddy block and the deploy command. Run it from deploy/clients/.
set -euo pipefail
cd "$(dirname "$0")"
SLUG="${1:?usage: new-client.sh <slug> <domain> [source-site-dir]}"
DOMAIN="${2:?domain required (e.g. happynails.de)}"
SRC="${3:-}"
[[ "$SLUG" =~ ^[a-z0-9-]+$ ]] || { echo "slug must be lowercase letters/digits/dashes"; exit 1; }
[ -e "$SLUG" ] && { echo "client '$SLUG' already exists at deploy/clients/$SLUG"; exit 1; }
cp -r _template "$SLUG"
cat > "$SLUG/.env" <<EOF
CLIENT_SLUG=$SLUG
CLIENT_DOMAIN=$DOMAIN
EOF
if [ -n "$SRC" ]; then
[ -d "$SRC" ] || { echo "source dir '$SRC' not found"; exit 1; }
rm -f "$SLUG"/site/*.html
cp -r "$SRC"/. "$SLUG"/site/
echo "Seeded site/ from $SRC"
fi
cat <<EOF
✓ Created deploy/clients/$SLUG
1) Add this block to the homelab Caddyfile, then reload Caddy:
docker exec caddy caddy reload --config /etc/caddy/Caddyfile
$DOMAIN {
reverse_proxy client-$SLUG:80
encode zstd gzip
}
2) DNS: a *.mivanchenko.de subdomain already resolves (wildcard) — nothing to do.
For a client-owned domain, add their A record $DOMAIN -> 188.193.49.3.
3) Deploy the stack:
scp -r $SLUG mivanchenko@mivanchenko.de:/home/mivanchenko/clients/
ssh mivanchenko@mivanchenko.de 'cd ~/clients/$SLUG && docker compose up -d'
Remove this client later (isolated — touches nothing else):
ssh mivanchenko@mivanchenko.de 'cd ~/clients/$SLUG && docker compose down'
EOF