72b6879f4b
- Refactored max_bot from nested packages to flat module structure - Q2: Extended BotUser model (patronymic, email, org, address, vcf_raw, contact_hash, phone_verified, email_verified, last_interaction, total_conversations, total_tickets) - Q2: VCF parser (FN, N, TEL, EMAIL, ORG, ADR), upsert on re-contact, NLP history context (_get_user_history_context -> YandexGPT) - Q1: Broadcast preview modal with 10s confirmation timer - Q3: CSS var(--white)->var(--bg-card), var(--text)->var(--text-primary) - Q4: bot_settings showNotification(), editable max_bot_id - Q5: Webhook secret passthrough via X-Max-Bot-Api-Secret - Masking sensitive keys, dialog_cleared handler, migrate via _add_column_if_not_exists() - Rate limit (asyncio.sleep 0.5 per 10), dead code removed, conv.intent context in contact.py - Portal pages: bot_consent, bot_kb (edit), bot_settings, bot_test, bot_tickets, portal_settings - Tests: 21/21 passing, added test_yandex_gpt.py, test_email_sender.py - Deploy: deploy_full.sh, schema.sql, seed_knowledge_base.sql
290 lines
8.6 KiB
Markdown
290 lines
8.6 KiB
Markdown
# VPS Infrastructure Guide for AI Agents
|
|
|
|
## Overview
|
|
|
|
This document describes the VPS infrastructure at **81.177.141.34** to help AI agents migrate or deploy new projects (e.g., `voideaai.ru`).
|
|
|
|
---
|
|
|
|
## 1. VPS Specifications
|
|
|
|
| Property | Value |
|
|
|---|---|
|
|
| IP | 81.177.141.34 |
|
|
| User | `angel` |
|
|
| Password | `.-SHGWa_` |
|
|
| Sudo | passwordless via `/etc/sudoers.d/angel` |
|
|
| SSH key | added to `~/.ssh/authorized_keys` |
|
|
| OS | Ubuntu 24.04 (Noble) |
|
|
| Kernel | OpenVZ/LXC container — limited kernel modules |
|
|
| Network interface | `venet0` (no bridge NAT support) |
|
|
| Disk | 20GB total, ~13GB available |
|
|
| Docker | 29.5.0, iptables in nft mode (`iptables-nft`) |
|
|
|
|
---
|
|
|
|
## 2. Docker Constraints
|
|
|
|
### Critical: `network_mode: host` only
|
|
|
|
Bridge networking (`network_mode: bridge`) **does not work** on this VPS. The OpenVZ/LXC kernel does not support masquerade for bridge interfaces through `venet0`. All containers **must** use:
|
|
|
|
```yaml
|
|
network_mode: host
|
|
```
|
|
|
|
### Docker daemon config (`/etc/docker/daemon.json`)
|
|
|
|
```json
|
|
{"dns": ["8.8.8.8", "8.8.4.4"]}
|
|
```
|
|
|
|
### Build constraint
|
|
|
|
All Docker builds require:
|
|
|
|
```yaml
|
|
build:
|
|
network: host
|
|
```
|
|
|
|
Without this, `apt-get` and `pip` cannot download packages (no bridge NAT).
|
|
|
|
### Docker Hub connectivity issues
|
|
|
|
Pulling large images (>100MB) from Docker Hub may time out (`failed to copy: read tcp... connection timed out`). Smaller images (nginx:alpine, postgres:16-alpine) work. Gitea and Portainer images **cannot be pulled** reliably.
|
|
|
|
**Solutions attempted:**
|
|
- Retry with `--platform linux/amd64`
|
|
- Increase `max-download-attempts` (not supported in Docker 29.5)
|
|
- Clean build cache with `docker builder prune -af`
|
|
- All fail on large image layers
|
|
|
|
**Workaround:** Pull images locally and SCP (`docker save → scp → docker load`), or use a registry mirror.
|
|
|
|
---
|
|
|
|
## 3. Port Allocation (all on host)
|
|
|
|
| Port | Service | Container |
|
|
|---|---|---|
|
|
| 80 | nginx-proxy HTTP | `nginx-proxy` |
|
|
| 443 | nginx-proxy HTTPS | `nginx-proxy` |
|
|
| 8080 | PHP nginx (aegisone.ru) | `aegisone-php-nginx` |
|
|
| 9000 | PHP-FPM | `aegisone-php-fpm` |
|
|
| 8000 | FastAPI (service.aegisone.ru) | `aegisone-app` |
|
|
| 5432 | PostgreSQL (shared, aegisone) | `aegisone-postgres` |
|
|
| 5433 | PostgreSQL (Gitea) | `gitea-db` (planned) |
|
|
| 3000 | Gitea | `gitea` (planned) |
|
|
| 9001 | Portainer HTTP | `portainer` (planned) |
|
|
| 9443 | Portainer HTTPS | `portainer` (planned) |
|
|
|
|
### Address resolution between containers
|
|
|
|
Since all containers use `network_mode: host`, they communicate via `localhost:PORT`. **Docker DNS names do not resolve.** In `nginx.conf` and app configs, use:
|
|
|
|
```nginx
|
|
proxy_pass http://localhost:8080; # NOT container_name:port
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Project Structure
|
|
|
|
```
|
|
/opt/projects/
|
|
nginx-proxy/ # Reverse proxy (nginx + certbot)
|
|
docker-compose.yml
|
|
nginx.conf
|
|
conf.d/ # Per-domain configs
|
|
certs/ # SSL certificates
|
|
html/ # Webroot for certbot
|
|
aegisone-php/ # PHP frontend (aegisone.ru)
|
|
docker-compose.yml
|
|
Dockerfile
|
|
nginx.conf # PHP nginx config (listen 8080)
|
|
src/ # PHP source files
|
|
aegisone-py/ # FastAPI backend (service.aegisone.ru)
|
|
docker-compose.yml
|
|
Dockerfile
|
|
app/ # Python source
|
|
sql/ # DB schema
|
|
gitea/ # Git hosting (planned)
|
|
docker-compose.yml
|
|
portainer/ # Docker UI (planned)
|
|
docker-compose.yml
|
|
```
|
|
|
|
---
|
|
|
|
## 5. nginx-proxy Configuration Pattern
|
|
|
|
### Global config (`nginx.conf`)
|
|
- `resolver 127.0.0.11` — not used in host mode, kept for compatibility
|
|
- SSL termination handled by nginx-proxy
|
|
- Upstream protocols are HTTP (nginx-proxy terminates SSL and proxies via HTTP)
|
|
|
|
### Conf.d file structure
|
|
Each domain has one `.conf` file with two server blocks:
|
|
|
|
```nginx
|
|
# HTTP (no redirect — temporary, until LE certs)
|
|
server {
|
|
listen 80;
|
|
server_name example.ru;
|
|
|
|
location / {
|
|
proxy_pass http://localhost:PORT;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
}
|
|
}
|
|
|
|
# HTTPS (self-signed, for future LE)
|
|
server {
|
|
listen 443 ssl;
|
|
http2 on;
|
|
server_name example.ru;
|
|
|
|
ssl_certificate /etc/nginx/certs/live/aegisone.ru/fullchain.pem;
|
|
ssl_certificate_key /etc/nginx/certs/live/aegisone.ru/privkey.pem;
|
|
|
|
# ... same location block
|
|
}
|
|
```
|
|
|
|
Note: HTTP→HTTPS redirects (`return 301 https://...`) caused **redirect loops** externally due to provider/proxy intercepting port 443. If you add them, test thoroughly.
|
|
|
|
### SSL certificates
|
|
Currently using self-signed certs generated via:
|
|
```bash
|
|
sudo openssl req -x509 -nodes -days 30 -newkey rsa:2048 \
|
|
-keyout /opt/projects/nginx-proxy/certs/live/aegisone.ru/privkey.pem \
|
|
-out /opt/projects/nginx-proxy/certs/live/aegisone.ru/fullchain.pem \
|
|
-subj '/CN=aegisone.ru'
|
|
```
|
|
|
|
For Let's Encrypt:
|
|
```bash
|
|
cd /opt/projects/nginx-proxy
|
|
docker compose run --rm certbot certonly --webroot \
|
|
--webroot-path=/var/www/certbot \
|
|
-d example.ru \
|
|
--email admin@example.ru \
|
|
--agree-tos --non-interactive
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Deployment Workflow
|
|
|
|
### Initial setup
|
|
1. Prepare files locally (this repo)
|
|
2. Pack into tarball: `tar czf project.tar.gz .`
|
|
3. SCP to VPS: `scp project.tar.gz angel@81.177.141.34:/home/angel/`
|
|
4. SSH and extract: `ssh angel@81.177.141.34 "tar xzf project.tar.gz -C /opt/projects/project-name/"`
|
|
5. Build and start: `ssh angel@81.177.141.34 "cd /opt/projects/project-name && docker compose up -d --build"`
|
|
|
|
### Updating a running project
|
|
1. Edit files locally
|
|
2. SCP changed files to `/home/angel/` on VPS
|
|
3. SSH and copy to correct location: `sudo cp /home/angel/file /opt/projects/project-name/`
|
|
4. Restart container: `docker compose -p project-name restart service-name`
|
|
|
|
### Copying files into a running container (alternative to rebuild)
|
|
```bash
|
|
docker cp /home/angel/file.py container-name:/app/path/file.py
|
|
docker compose -p project-name restart app
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Database
|
|
|
|
### PostgreSQL (aegisone shared DB)
|
|
- Host: `localhost:5432`
|
|
- User: `aegisone`
|
|
- Password: `aegisone_pass`
|
|
- Database: `aegisone`
|
|
- Container: `aegisone-postgres` (postgres:16-alpine)
|
|
|
|
### Schema initialization
|
|
SQL files in `/opt/projects/aegisone-py/sql/`:
|
|
- `schema_postgresql.sql` — full schema
|
|
- `seed_data.sql` — seed data
|
|
|
|
Init scripts run only on first volume creation. To add missing tables later:
|
|
```bash
|
|
docker exec -i aegisone-postgres psql -U aegisone -d aegisone < /opt/projects/aegisone-py/sql/schema_postgresql.sql
|
|
```
|
|
|
|
### Key schema pattern
|
|
- All tables use `BOOLEAN` for `is_active` (not integer)
|
|
- Timestamps use `TIMESTAMP NOT NULL DEFAULT NOW()`
|
|
- `updated_at` triggers via `update_updated_at_column()` function
|
|
|
|
---
|
|
|
|
## 8. Useful Commands
|
|
|
|
```bash
|
|
# Check running containers
|
|
docker ps -a
|
|
|
|
# Check compose projects
|
|
docker compose ls
|
|
|
|
# View logs
|
|
docker logs container-name --tail 50
|
|
|
|
# Restart service
|
|
docker compose -p project-name restart service-name
|
|
|
|
# Rebuild and restart
|
|
docker compose -p project-name up -d --build
|
|
|
|
# Clean build cache
|
|
docker builder prune -af
|
|
|
|
# Test nginx config
|
|
docker compose -p nginx-proxy exec nginx nginx -t
|
|
|
|
# Reload nginx
|
|
docker compose -p nginx-proxy exec nginx nginx -s reload
|
|
|
|
# Check listening ports
|
|
ss -tlnp | grep -E ':(80|443|8080|8000|9000|5432)'
|
|
|
|
# Check disk
|
|
df -h /
|
|
docker system df
|
|
```
|
|
|
|
---
|
|
|
|
## 9. Adding a New Project (e.g., voideaai.ru)
|
|
|
|
### Steps:
|
|
1. Create directory: `mkdir -p /opt/projects/voideaai`
|
|
2. Create `docker-compose.yml` with `network_mode: host` and `build.network: host`
|
|
3. Create `Dockerfile` (use `--only-binary :all:` for pip if avoiding apt)
|
|
4. Create nginx-proxy config: `nginx/conf.d/voideaai.conf` (listen 80, proxy_pass localhost:PORT)
|
|
5. Generate cert if needed
|
|
6. Restart nginx-proxy: `docker compose -p nginx-proxy restart nginx`
|
|
7. Start project: `cd /opt/projects/voideaai && docker compose up -d --build`
|
|
|
|
### Port allocation for new project
|
|
Check existing ports first (`ss -tlnp`). Choose an unused port for the app and add it to the nginx-proxy config.
|
|
|
|
---
|
|
|
|
## 10. Known Issues
|
|
|
|
1. **Docker Hub timeouts on large images** — consider pulling on local machine and transferring with `docker save`/`docker load`
|
|
2. **HTTPS redirect loops externally** — port 443 may be intercepted by provider/proxy; test HTTP first
|
|
3. **Bridge networking doesn't work** — all containers must use `network_mode: host`
|
|
4. **Docker build needs `network: host`** — without it, apt-get/pip fail
|
|
5. **Config files with `$` in content** — when editing via SSH from PowerShell, `$variables` get expanded; use SCP (write locally, then copy)
|