tools-update-cron: sync 2026-08-16 — 16 skill(s) updated
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
---
|
||||
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 [email protected] '<command>'
|
||||
sshpass -p 'passw0rd' scp -o StrictHostKeyChecking=no <src> [email protected]:<dst>
|
||||
```
|
||||
|
||||
## 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 [email protected] 'echo passw0rd | sudo -S tar czf /tmp/site.tar.gz -C /root/html .'
|
||||
|
||||
# Pull to local, push to .39
|
||||
scp [email protected]:/tmp/site.tar.gz /tmp/
|
||||
scp /tmp/site.tar.gz [email protected]:/tmp/
|
||||
|
||||
# On .39: extract
|
||||
ssh [email protected] '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 [email protected] '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 [email protected] '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/<genre>/`, 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 [email protected] '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/<genre>/`. 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 `<div class="hidden md:flex space-x-8">`.
|
||||
- **Count badge visibility**: default gray-on-transparent styling (0.7rem, 500 weight, 0.7 opacity) is nearly invisible. Use indigo text (#6366f1), subtle indigo background pill, 600 weight, centered below title. See `references/download-counter.md` for the full CSS.
|
||||
- **Sync from production to staging**: when .17 is behind, pull from .39 via local machine as relay — `ssh .39 tar → scp to local → scp to .17 → extract`. Direct .39→.17 scp may not work if they can't reach each other.
|
||||
- **304 Not Modified not counted**: counter only matches status 200 and 206. Browser-cached replays return 304 and are invisible. A user who plays a track, refreshes, and plays again from cache generates only one count. Fix: add `'304'` to the status check in `parse_new_entries()`.
|
||||
- **Query string pollution in counter**: access log captures query strings (`/backup/downloads.json?t=12345`). Regex captures the full string including `?t=...`, so `.json` exclusion checks fail. Fix: `.split('?', 1)[0]` on all captured paths before validation.
|
||||
- **Leading slash mismatch in counter**: some log paths have leading `/` (e.g. `/electronic/Heavy-Weight.mp3`). Frontend looks up without slash. Fix: `.lstrip('/')` on all captured paths. Clean existing data too.
|
||||
- **nginx types{} block overrides ALL MIME types**: adding `types {}` in a server/location context replaces inherited MIME types from `mime.types`, causing `.html` to serve as `application/octet-stream` (download prompt). Never use `types {}` in server blocks — add types to http-level config only.
|
||||
- **Grok validation loop**: dispatch to Grok for adversarial validation after building, fix findings, re-validate. Grok caught range-request inflation, log rotation loss, invalid HTML, path traversal, query string pollution, leading slash mismatch, and types{} MIME override — all real bugs.
|
||||
- **Counter script must strip query strings**: access log captures `?t=...` cache busters. Use `.split('?', 1)[0]` on all captured paths before validation and key storage.
|
||||
- **Counter script must strip leading slashes**: some log paths have leading `/`. Use `.lstrip('/')` on all captured paths. Frontend lookup keys have no leading slash.
|
||||
- **Backup path validation**: `is_valid_backup()` must exclude `.json` files and paths ending in `/` from download counts. Apply AFTER stripping query strings.
|
||||
- **Persistent accumulation over full-log re-parse**: load existing counts.json, only process new entries via state file (inode+offset). Survives log rotation without re-parsing compressed logs.
|
||||
- **nginx types{} block is dangerous**: adding `types {}` in a server/location context replaces ALL inherited MIME types from `mime.types`, causing `.html` to serve as `application/octet-stream` (download prompt). Never use `types {}` in server blocks.
|
||||
Reference in New Issue
Block a user