--- name: website-management description: "Manage SpeedyFoxAI site across staging and production LXCs." version: 1.0.0 tags: [website, lxc, nginx, filezilla, sync, cleanup, music] --- # website-management — SpeedyFoxAI Site Operations Production LXC hosts the SpeedyFoxAI static site: | Role | IP | OS | Notes | |------|----|----|-------| | Production | 10.0.0.39 | Debian 13 | Live site — all work goes here directly | | Staging | 10.0.0.17 | Kali Linux | Shut down 2026-08-11, kept as backup only | Production: n8n/passw0rd, nginx serving from `/root/html/` on port 80, filezilla on 3025-3026. **SSH method**: use `sshpass` for all remote commands — `echo passw0rd | sudo -S` fails with password prompts on these hosts. Pattern: ```bash sshpass -p 'passw0rd' ssh -o StrictHostKeyChecking=no n8n@10.0.0.39 '' sshpass -p 'passw0rd' scp -o StrictHostKeyChecking=no n8n@10.0.0.39: ``` ## LXC Cleanup (run on both) When cloning or resetting, strip to bare essentials: 1. Remove stale Docker artifacts: ```bash docker stop filezilla 2>/dev/null; docker rm filezilla 2>/dev/null docker rm nginx 2>/dev/null # stale container from original clone docker rmi nginx:latest fauria/vsftpd:latest 2>/dev/null docker volume prune -f; docker network prune -f ``` 2. Clean `/root/html/` — keep live site files + one backup dir: ```bash cd /root/html rm -f .counter_data.txt counter.gif .counter_last_line .counter_total count.txt \ index.html.bak.13022026 update_count_persistent.sh ``` 3. Clean `/root/html/backup/` — keep only `20260326_201727/`, remove loose files and nested backups: ```bash cd /root/html/backup rm -f clawdbot.json favicon.png "index (Copy 2).html" "index (Copy).html" \ InstructionsGrok.txt jarvis-memory-blueprint.tar.gz \ "LTX-2 FULL Guidelines.txt" openclaw.json yt_doc.md rm -rf 20260326_201727/backup ``` 4. Remove cruft from `/root/` and `/home/n8n/`: ```bash rm -rf /root/backup /root/DocumentsUpload /root/docs /root/.projects /root/.projects-backup rm -f /root/nginx-default.bak.13022026 /root/watcher.md rm -rf ~/.projects ~/.main_projects ~/neuralstream-temp ~/filezilla-data rm -f ~/site_log.md ~/ENDOFCARD ~/ENDOFFILE ~/EOF ~/HTML ~/SCRIPT_END ``` 5. Re-deploy filezilla: ```bash docker run -d --name filezilla --restart unless-stopped \ -p 3025:3000 -p 3026:3001 \ -v /root/html:/config/data:rw \ -e PUID=1000 -e PGID=1000 \ lscr.io/linuxserver/filezilla:latest ``` ## Sync Staging → Production After testing on .17, copy to .39: ```bash # On .17: package the site ssh n8n@10.0.0.17 'echo passw0rd | sudo -S tar czf /tmp/site.tar.gz -C /root/html .' # Pull to local, push to .39 scp n8n@10.0.0.17:/tmp/site.tar.gz /tmp/ scp /tmp/site.tar.gz n8n@10.0.0.39:/tmp/ # On .39: extract ssh n8n@10.0.0.39 'echo passw0rd | sudo -S tar xzf /tmp/site.tar.gz -C /root/html/ && echo passw0rd | sudo -S chown -R n8n:n8n /root/html/' ``` For music page specifically, package just the new files: ```bash ssh n8n@10.0.0.17 'echo passw0rd | sudo -S tar czf /tmp/music_site.tar.gz -C /root/html music.html music/' ``` ## Music Page Architecture See `references/music-page.md` for full architecture — library.json manifest, genre folder structure, animated visualizer, Tailwind theme integration. **Download counter**: see `references/download-counter.md` — two-URL play vs download tracking via nginx access log parsing, zero backend, cron-driven counts.json. ## Downloads Page Architecture See `references/downloads-page.md` for full architecture — downloads.json manifest, type filter buttons, matching music page row style, download counter integration. **Quick reference:** - Manifest: `/root/html/backup/downloads.json` — `{items: [{id, title, description, type, src}]}` - Types: Image, JSON, TXT, MD, Skill — filter buttons auto-generated from manifest - Counter: same `counts.json` tracks `/backup/` paths under `downloads` key - Styling: identical to music page — glass rows, type badge pill, centered count badge, download button on right ### Music Management Quick Reference **Alphabetize after every change**: tracks in library.json MUST be sorted by title (case-insensitive). Use: ```bash sshpass -p 'passw0rd' ssh -o StrictHostKeyChecking=no n8n@10.0.0.39 'python3 -c " import json; data = json.load(open(\"/root/html/music/audio/library.json\")) data[\"tracks\"].sort(key=lambda t: t[\"title\"].lower()) json.dump(data, open(\"/root/html/music/audio/library.json\",\"w\"), indent=2) "' ``` **Move track between genres**: update both `genre` and `src` fields in library.json, then re-sort. **Remove a genre**: delete the folder under `/root/html/music/audio//`, remove all tracks with that genre from library.json. The filter button disappears automatically — no HTML edit needed. **Validate all tracks playable**: ```bash sshpass -p 'passw0rd' ssh -o StrictHostKeyChecking=no n8n@10.0.0.39 'python3 -c " import json, os data = json.load(open(\"/root/html/music/audio/library.json\")) missing = [t[\"title\"] for t in data[\"tracks\"] if not os.path.exists(\"/root/html\" + t[\"src\"])] print(f\"All {len(data[\"tracks\"])} present\" if not missing else f\"MISSING: \" + \", \".join(missing)) "' ``` **Sync missing files from NAS**: source files on TrueNAS at `smb://green.local/proxmoxbackup/SiteMusic//`. Download via smbclient, scp to .39, copy into genre folder. See `references/music-page.md` for full workflow. ## Pitfalls - **Kali vs Debian**: .17 runs Kali (rolling), .39 runs Debian 13 (stable). Package names may differ (e.g., `docker-compose` vs `docker-compose-plugin`). Prefer Debian for production. - **No swap on either LXC**: 4.1GB RAM with no safety net. User explicitly declined swapfile — don't add without asking. - **filezilla maps `/root/html`**: any file dropped via filezilla lands in the nginx root. Permissions must stay n8n:n8n. - **nginx runs as systemd service, NOT Docker**: the nginx container is always stale — the live server is the native systemd unit. - **index.html nav**: when adding a new page tab, insert between Downloads and Contact in the nav `