On this specific shared host, /etc/apache2/sites-enabled/*.conf is a hand-maintained copy of sites-available, not the usual a2ensite symlink. Apache loads sites-enabled, so patching sites-available alone produced exactly the observed bug: configtest passed, our Include sat in sites-available, and Apache kept 404-ing because the active config had no Include line. Script now: - lists sites-enabled paths first, sites-available second - readlink -f resolves each to its real path; a dedup set skips double-patching when the enabled entry IS a normal symlink - picks up both the plain HTTP vhost and any *-le-ssl.conf variant A symlink-based install keeps working (one file edited once); a divergent-copy install like this one gets both files patched. |
||
|---|---|---|
| .. | ||
| apache | ||
| deploy.sh | ||
| README.md | ||
Deploy — HP Studios AI Content Agent
Idempotent deployment to optical-dev.oliver.solutions (or any shared Apache + Docker Linux box).
Public URL: https://optical-dev.oliver.solutions/hp-content-agent/
First-time setup on the server
# SSH in, clone the repo somewhere you want it to live
ssh you@optical-dev.oliver.solutions
sudo git clone git@bitbucket.org:zlalani/hp-studios-ai-content-agent.git /opt/hp-studios-ai-content-agent
cd /opt/hp-studios-ai-content-agent
# Bootstrap .env (will error and tell you what secrets to fill)
sudo ./deploy/deploy.sh
# Edit /opt/hp-studios-ai-content-agent/.env and fill:
# ANTHROPIC_API_KEY=...
# OPENAI_API_KEY=... (or VOYAGE_API_KEY)
# Everything else is auto-populated.
# Re-run to actually deploy
sudo ./deploy/deploy.sh
The script handles everything else:
- Picks free host ports in 20000–29999, stable across reruns
- Builds + starts containers with project name
hp-studios-ai-content-agent - Runs Alembic migrations + seeds admin user (first run prints credentials)
- Renders the Apache snippet with the chosen ports
- Adds an
Includeline to the vhost and reloads Apache - Ensures UFW allows 22/80
Re-deploy (after a git pull)
cd /opt/hp-studios-ai-content-agent
sudo git pull
sudo ./deploy/deploy.sh
Safe to re-run any time. Existing ports, secrets, and the admin user are preserved.
Apache integration
The script writes a rendered proxy snippet to deploy/apache/hp-content-agent.conf and adds an Include line inside the vhost at /etc/apache2/sites-available/optical-dev.oliver.solutions.conf. If the file doesn't exist, the script prints the Include line for manual addition.
The snippet proxies:
/hp-content-agent/api/→127.0.0.1:$API_HOST_PORT/hp-content-agent/→127.0.0.1:$FRONTEND_HOST_PORT
Order matters in the rendered file — API rule precedes the catch-all frontend rule.
Common operations
# Status
docker compose -p hp-studios-ai-content-agent -f docker-compose.yml -f docker-compose.prod.yml ps
# Logs
docker compose -p hp-studios-ai-content-agent -f docker-compose.yml -f docker-compose.prod.yml logs -f api worker
# Shell into API container
docker compose -p hp-studios-ai-content-agent -f docker-compose.yml -f docker-compose.prod.yml exec api bash
# Rebuild just the frontend (after a merge that only changed frontend)
docker compose -p hp-studios-ai-content-agent -f docker-compose.yml -f docker-compose.prod.yml up -d --build frontend
# Tear down (keeps data volume; use -v to also wipe the DB)
docker compose -p hp-studios-ai-content-agent -f docker-compose.yml -f docker-compose.prod.yml down
Troubleshooting
- 404 at
/hp-content-agent/— check the Include line is in the vhost andapache2ctl configtestpasses. Checkdeploy/apache/hp-content-agent.confpoints to the ports indocker compose ps. - API calls from the SPA 404 — open the browser Network tab. If requests go to
/api/...instead of/hp-content-agent/api/..., the frontend wasn't rebuilt withVITE_API_URL=/hp-content-agent/api. Force a rebuild:docker compose … up -d --build frontend. - Port collisions — the script picks in 20000–29999 and writes to
.env. Delete the four*_HOST_PORTlines and re-run to reassign. - Cookies / auth fail — the JWT cookie is set on the root domain
optical-dev.oliver.solutionswith path/, so the subpath is fine. If requests fail with 401, check thatCORS_ORIGIN=https://optical-dev.oliver.solutionsis in.env(the script sets it).
What the script does not do
- TLS / Let's Encrypt — handled by whatever already serves the vhost.
- DNS — uses the existing
optical-dev.oliver.solutionshostname, no record changes. - Backup —
docker compose downkeeps the Postgres data volume by default. Dump withdocker compose … exec postgres pg_dump -U hp hp_content_agent > backup.sqlif you want a snapshot before a risky change.