feat(db-hardening): Add database persistence, backups, and MinIO assets storage
Database Hardening: - Add docker-compose.db.yml with persistent PostgreSQL volume - Add automatic DB backups every 12h (7 days, 4 weeks, 6 months retention) - Add MinIO S3-compatible storage for assets Assets Migration: - Add MinIO client (lib/assets_client.py) for upload/delete - Update upload endpoint to use MinIO (with local fallback) - Add migration 043_asset_urls_to_text.sql for full HTTPS URLs - Simplify normalizeAssetUrl for S3 URLs Recovery: - Add seed_full_city_reset.py for emergency city recovery - Add DB_RESTORE.md with backup restore instructions - Add SEED_RECOVERY.md with recovery procedures - Add INFRA_ASSETS_MINIO.md with MinIO setup guide Task: TASK_PHASE_DATABASE_HARDENING_AND_ASSETS_MIGRATION_v1
This commit is contained in:
138
docs/DB_RESTORE.md
Normal file
138
docs/DB_RESTORE.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# DB_RESTORE — Як відновити базу daarion
|
||||
|
||||
## 1. Відновлення з останнього бекапу
|
||||
|
||||
### Крок 1: Зупинити сервіси
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.db.yml stop db
|
||||
docker compose -f docker-compose.web.yml stop city-service web
|
||||
```
|
||||
|
||||
### Крок 2: Відновити базу з бекапу
|
||||
|
||||
```bash
|
||||
# Знайти останній бекап
|
||||
ls -lt db_backups/ | head -5
|
||||
|
||||
# Відновити базу
|
||||
docker compose -f docker-compose.db.yml exec db psql -U postgres -d postgres -c "DROP DATABASE IF EXISTS daarion;"
|
||||
docker compose -f docker-compose.db.yml exec db psql -U postgres -d postgres -c "CREATE DATABASE daarion;"
|
||||
docker compose -f docker-compose.db.yml exec -T db psql -U postgres -d daarion < db_backups/daarion_YYYY-MM-DD_HH-MM.sql
|
||||
```
|
||||
|
||||
Або через docker exec:
|
||||
|
||||
```bash
|
||||
# Скопіювати бекап в контейнер
|
||||
docker cp db_backups/daarion_2025-12-02_09-00.sql daarion-postgres:/tmp/backup.sql
|
||||
|
||||
# Відновити
|
||||
docker exec daarion-postgres psql -U postgres -d postgres -c "DROP DATABASE IF EXISTS daarion;"
|
||||
docker exec daarion-postgres psql -U postgres -d postgres -c "CREATE DATABASE daarion;"
|
||||
docker exec -i daarion-postgres psql -U postgres -d daarion < /tmp/backup.sql
|
||||
```
|
||||
|
||||
### Крок 3: Перезапустити сервіси
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.db.yml up -d db
|
||||
docker compose -f docker-compose.web.yml up -d city-service web
|
||||
```
|
||||
|
||||
### Крок 4: Перевірити
|
||||
|
||||
```bash
|
||||
# Перевірити що база працює
|
||||
docker exec daarion-postgres psql -U postgres -d daarion -c "SELECT COUNT(*) FROM microdao;"
|
||||
|
||||
# Перевірити API
|
||||
curl https://daarion.space/api/city/microdao?limit=1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Якщо бекапу немає (аварійне відновлення)
|
||||
|
||||
### Крок 1: Застосувати міграції
|
||||
|
||||
```bash
|
||||
cd /opt/microdao-daarion
|
||||
for f in migrations/*.sql; do
|
||||
echo "Applying: $f"
|
||||
docker exec -i daarion-postgres psql -U postgres -d daarion < "$f"
|
||||
done
|
||||
```
|
||||
|
||||
### Крок 2: Запустити seed-скрипт
|
||||
|
||||
```bash
|
||||
python3 scripts/seed_full_city_reset.py
|
||||
```
|
||||
|
||||
### Крок 3: Відновити DAGI-агентів NODE2
|
||||
|
||||
```bash
|
||||
python3 scripts/sync-node2-dagi-agents.py
|
||||
```
|
||||
|
||||
### Крок 4: Перевірити
|
||||
|
||||
```bash
|
||||
# Перевірити MicroDAOs
|
||||
docker exec daarion-postgres psql -U postgres -d daarion -c "SELECT slug, name FROM microdao;"
|
||||
|
||||
# Перевірити агентів
|
||||
docker exec daarion-postgres psql -U postgres -d daarion -c "SELECT COUNT(*) FROM agents;"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Перевірка бекапів
|
||||
|
||||
### Список доступних бекапів
|
||||
|
||||
```bash
|
||||
ls -lh db_backups/
|
||||
```
|
||||
|
||||
### Структура бекапів
|
||||
|
||||
- `daarion_YYYY-MM-DD_HH-MM.sql` - щоденні бекапи (зберігаються 7 днів)
|
||||
- `daarion_YYYY-MM-DD_HH-MM.sql` - тижневі бекапи (зберігаються 4 тижні)
|
||||
- `daarion_YYYY-MM-DD_HH-MM.sql` - місячні бекапи (зберігаються 6 місяців)
|
||||
|
||||
### Створити бекап вручну
|
||||
|
||||
```bash
|
||||
docker exec daarion-postgres pg_dump -U postgres daarion > db_backups/manual_$(date +%F_%H-%M).sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Troubleshooting
|
||||
|
||||
### Проблема: "database does not exist"
|
||||
|
||||
```bash
|
||||
# Створити базу
|
||||
docker exec daarion-postgres psql -U postgres -d postgres -c "CREATE DATABASE daarion;"
|
||||
```
|
||||
|
||||
### Проблема: "connection refused"
|
||||
|
||||
```bash
|
||||
# Перевірити статус контейнера
|
||||
docker ps | grep postgres
|
||||
|
||||
# Перезапустити
|
||||
docker compose -f docker-compose.db.yml restart db
|
||||
```
|
||||
|
||||
### Проблема: "permission denied" при відновленні
|
||||
|
||||
```bash
|
||||
# Перевірити права на файл бекапу
|
||||
chmod 644 db_backups/daarion_*.sql
|
||||
```
|
||||
|
||||
389
docs/INFRA_ASSETS_MINIO.md
Normal file
389
docs/INFRA_ASSETS_MINIO.md
Normal file
@@ -0,0 +1,389 @@
|
||||
# INFRA_ASSETS_MINIO — Налаштування MinIO для Assets
|
||||
|
||||
## Огляд
|
||||
|
||||
DAARION використовує **MinIO** (S3-compatible object storage) для зберігання assets:
|
||||
- Логотипи MicroDAO
|
||||
- Банери MicroDAO
|
||||
- Аватарки агентів
|
||||
- Інші статичні файли
|
||||
|
||||
**Переваги:**
|
||||
- Assets не залежать від локального сервера
|
||||
- Можливість реплікації на зовнішній S3/R2
|
||||
- Масштабованість
|
||||
- Простота backup/restore
|
||||
|
||||
---
|
||||
|
||||
## 1. Архітектура
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ Frontend │ → https://assets.daarion.space/daarion-assets/...
|
||||
└─────────────┘
|
||||
│
|
||||
↓
|
||||
┌─────────────┐
|
||||
│ Caddy │ → Reverse proxy
|
||||
│ / NGINX │
|
||||
└─────────────┘
|
||||
│
|
||||
↓
|
||||
┌─────────────┐
|
||||
│ MinIO │ → S3 API (port 9000)
|
||||
│ (Docker) │ → Console (port 9001)
|
||||
└─────────────┘
|
||||
│
|
||||
↓
|
||||
┌─────────────┐
|
||||
│ Volume │ → minio_data (persistent)
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Docker Compose конфігурація
|
||||
|
||||
**Файл:** `docker-compose.db.yml`
|
||||
|
||||
```yaml
|
||||
services:
|
||||
minio:
|
||||
image: minio/minio:latest
|
||||
container_name: daarion-minio
|
||||
restart: unless-stopped
|
||||
command: server /data --console-address ":9001"
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
||||
ports:
|
||||
- "9000:9000" # S3 API
|
||||
- "9001:9001" # Web console
|
||||
volumes:
|
||||
- minio_data:/data
|
||||
```
|
||||
|
||||
**ENV змінні (.env):**
|
||||
```env
|
||||
MINIO_ROOT_USER=assets-admin
|
||||
MINIO_ROOT_PASSWORD=very-strong-password
|
||||
ASSETS_BUCKET=daarion-assets
|
||||
ASSETS_PUBLIC_BASE_URL=https://assets.daarion.space/daarion-assets
|
||||
MINIO_ENDPOINT=http://minio:9000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Початкове налаштування
|
||||
|
||||
### Крок 1: Запустити MinIO
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.db.yml up -d minio
|
||||
```
|
||||
|
||||
### Крок 2: Відкрити консоль
|
||||
|
||||
Відкрити в браузері: `http://localhost:9001` (або `https://minio.daarion.space`)
|
||||
|
||||
Логін:
|
||||
- Username: `assets-admin` (з .env)
|
||||
- Password: `very-strong-password` (з .env)
|
||||
|
||||
### Крок 3: Створити bucket
|
||||
|
||||
1. Натиснути "Create Bucket"
|
||||
2. Назва: `daarion-assets`
|
||||
3. Region: залишити за замовчуванням
|
||||
4. Натиснути "Create Bucket"
|
||||
|
||||
### Крок 4: Встановити public read policy
|
||||
|
||||
1. Відкрити bucket `daarion-assets`
|
||||
2. Перейти в "Access Policy"
|
||||
3. Вибрати "Public" або "Custom"
|
||||
4. Для Custom policy:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {"AWS": ["*"]},
|
||||
"Action": ["s3:GetObject"],
|
||||
"Resource": ["arn:aws:s3:::daarion-assets/*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. DNS налаштування
|
||||
|
||||
### A/AAAA записи
|
||||
|
||||
```
|
||||
assets.daarion.space → IP NODE1 (для публічного доступу)
|
||||
minio.daarion.space → IP NODE1 (опційно, для консолі)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Reverse Proxy (Caddy)
|
||||
|
||||
**Файл:** `Caddyfile`
|
||||
|
||||
```caddy
|
||||
# Assets public access
|
||||
assets.daarion.space {
|
||||
encode gzip
|
||||
|
||||
reverse_proxy minio:9000 {
|
||||
header_up Host {upstream_hostport}
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
}
|
||||
}
|
||||
|
||||
# MinIO console (опційно)
|
||||
minio.daarion.space {
|
||||
encode gzip
|
||||
|
||||
reverse_proxy minio:9001 {
|
||||
header_up Host {upstream_hostport}
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Docker Compose:**
|
||||
```yaml
|
||||
caddy:
|
||||
image: caddy:2
|
||||
container_name: daarion-caddy
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
depends_on:
|
||||
- minio
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Reverse Proxy (NGINX)
|
||||
|
||||
**Файл:** `nginx.conf` (фрагмент)
|
||||
|
||||
```nginx
|
||||
http {
|
||||
upstream minio_api {
|
||||
server minio:9000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name assets.daarion.space;
|
||||
|
||||
client_max_body_size 100M;
|
||||
|
||||
location / {
|
||||
proxy_pass http://minio_api;
|
||||
proxy_set_header Host $http_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Формат URL в БД
|
||||
|
||||
Після завантаження файлу через API, в БД зберігається повний HTTPS URL:
|
||||
|
||||
```
|
||||
https://assets.daarion.space/daarion-assets/microdao/logo/2025/12/02/abc123def456.png
|
||||
```
|
||||
|
||||
**Структура:**
|
||||
- `https://assets.daarion.space` - public domain
|
||||
- `/daarion-assets` - bucket name
|
||||
- `/microdao/logo` - prefix (тип asset)
|
||||
- `/2025/12/02` - дата завантаження
|
||||
- `/abc123def456.png` - унікальний ID + розширення
|
||||
|
||||
---
|
||||
|
||||
## 8. Backend інтеграція
|
||||
|
||||
### Upload endpoint (приклад)
|
||||
|
||||
```python
|
||||
from fastapi import UploadFile, File
|
||||
from lib.assets_client import upload_asset
|
||||
|
||||
@router.post("/microdao/{slug}/logo")
|
||||
async def upload_logo(slug: str, file: UploadFile = File(...)):
|
||||
url = upload_asset(
|
||||
file.file,
|
||||
file.content_type,
|
||||
prefix="microdao/logo",
|
||||
filename=file.filename
|
||||
)
|
||||
# Зберегти url в БД
|
||||
await repo.update_logo(slug=slug, logo_url=url)
|
||||
return {"logo_url": url}
|
||||
```
|
||||
|
||||
### Клієнт MinIO
|
||||
|
||||
**Файл:** `services/city-service/lib/assets_client.py`
|
||||
|
||||
Функції:
|
||||
- `upload_asset()` - завантажити файл, повернути URL
|
||||
- `delete_asset()` - видалити файл
|
||||
- `ensure_bucket()` - переконатися що bucket існує
|
||||
|
||||
---
|
||||
|
||||
## 9. Frontend інтеграція
|
||||
|
||||
**Файл:** `apps/web/src/lib/utils/assetUrl.ts`
|
||||
|
||||
```typescript
|
||||
export function normalizeAssetUrl(url?: string | null): string | null {
|
||||
if (!url) return null;
|
||||
|
||||
// Full HTTPS URLs (from MinIO) - return as-is
|
||||
if (url.startsWith('https://') || url.startsWith('http://')) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// Legacy local paths - handle fallback
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Backup MinIO
|
||||
|
||||
### Створити backup bucket
|
||||
|
||||
```bash
|
||||
# Використовуючи MinIO Client (mc)
|
||||
mc alias set local http://localhost:9000 assets-admin <password>
|
||||
mc mirror local/daarion-assets ./backups/minio/
|
||||
```
|
||||
|
||||
### Автоматичний backup (cron)
|
||||
|
||||
```bash
|
||||
# Додати в crontab
|
||||
0 2 * * * docker exec daarion-minio mc mirror minio/daarion-assets /backups/minio/$(date +\%F)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. Міграція з локальних файлів
|
||||
|
||||
### Крок 1: Завантажити існуючі файли в MinIO
|
||||
|
||||
```bash
|
||||
# Використовуючи mc
|
||||
mc cp ./services/city-service/static/uploads/microdao/logo/* local/daarion-assets/microdao/logo/
|
||||
mc cp ./services/city-service/static/uploads/microdao/banner/* local/daarion-assets/microdao/banner/
|
||||
```
|
||||
|
||||
### Крок 2: Оновити URL в БД
|
||||
|
||||
```sql
|
||||
UPDATE microdao
|
||||
SET logo_url = REPLACE(logo_url, '/api/static/uploads/', 'https://assets.daarion.space/daarion-assets/')
|
||||
WHERE logo_url LIKE '/api/static/uploads/%';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Troubleshooting
|
||||
|
||||
### Проблема: "Access Denied"
|
||||
|
||||
Перевірити:
|
||||
1. Bucket має public read policy
|
||||
2. URL правильний (включає bucket name)
|
||||
3. DNS налаштований правильно
|
||||
|
||||
### Проблема: "Connection refused"
|
||||
|
||||
```bash
|
||||
# Перевірити статус
|
||||
docker ps | grep minio
|
||||
|
||||
# Перезапустити
|
||||
docker compose -f docker-compose.db.yml restart minio
|
||||
```
|
||||
|
||||
### Проблема: "Bucket does not exist"
|
||||
|
||||
```bash
|
||||
# Створити через mc
|
||||
mc mb local/daarion-assets
|
||||
|
||||
# Або через консоль MinIO
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. Моніторинг
|
||||
|
||||
### Перевірка використання диска
|
||||
|
||||
```bash
|
||||
docker exec daarion-minio du -sh /data
|
||||
```
|
||||
|
||||
### Перевірка кількості об'єктів
|
||||
|
||||
Через MinIO Console → Bucket → Statistics
|
||||
|
||||
---
|
||||
|
||||
## 14. Реплікація на зовнішній S3 (опційно)
|
||||
|
||||
Для додаткової надійності можна налаштувати реплікацію на AWS S3 або Cloudflare R2:
|
||||
|
||||
```bash
|
||||
# Налаштувати remote
|
||||
mc alias set s3 https://s3.amazonaws.com ACCESS_KEY SECRET_KEY
|
||||
|
||||
# Налаштувати реплікацію
|
||||
mc replicate add local/daarion-assets --remote-bucket s3/daarion-assets-backup
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 15. Чекліст налаштування
|
||||
|
||||
- [ ] MinIO запущений (`docker ps | grep minio`)
|
||||
- [ ] Bucket `daarion-assets` створений
|
||||
- [ ] Public read policy встановлена
|
||||
- [ ] DNS `assets.daarion.space` налаштований
|
||||
- [ ] Caddy/NGINX проксує запити до MinIO
|
||||
- [ ] Backend використовує `assets_client.py`
|
||||
- [ ] Frontend відображає assets з HTTPS URLs
|
||||
- [ ] Тестовий upload працює
|
||||
|
||||
230
docs/SEED_RECOVERY.md
Normal file
230
docs/SEED_RECOVERY.md
Normal file
@@ -0,0 +1,230 @@
|
||||
# SEED_RECOVERY — Аварійне відновлення міста
|
||||
|
||||
## Коли використовувати
|
||||
|
||||
Цей процес використовується коли:
|
||||
- База даних була повністю втрачена
|
||||
- Немає доступних бекапів
|
||||
- Потрібно швидко відновити мінімально робочий стан DAARION City
|
||||
|
||||
---
|
||||
|
||||
## Крок 1: Переконатися що база порожня
|
||||
|
||||
```bash
|
||||
# Перевірити чи є дані
|
||||
docker exec daarion-postgres psql -U postgres -d daarion -c "SELECT COUNT(*) FROM microdao;"
|
||||
|
||||
# Якщо є дані - очистити (ОБЕРЕЖНО!)
|
||||
docker exec daarion-postgres psql -U postgres -d daarion -c "TRUNCATE TABLE microdao, agents, city_rooms CASCADE;"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Крок 2: Застосувати міграції
|
||||
|
||||
```bash
|
||||
cd /opt/microdao-daarion
|
||||
|
||||
# Застосувати всі міграції
|
||||
for f in migrations/*.sql; do
|
||||
echo "Applying: $f"
|
||||
docker exec -i daarion-postgres psql -U postgres -d daarion < "$f" 2>&1 | grep -v "ERROR\|NOTICE" || true
|
||||
done
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Крок 3: Запустити seed-скрипт
|
||||
|
||||
```bash
|
||||
# Встановити змінні оточення
|
||||
export DATABASE_URL="postgresql://postgres:postgres@localhost:5432/daarion"
|
||||
|
||||
# Запустити seed
|
||||
python3 scripts/seed_full_city_reset.py
|
||||
```
|
||||
|
||||
Очікуваний вивід:
|
||||
```
|
||||
🏙️ DAARION City Emergency Recovery
|
||||
============================================================
|
||||
|
||||
🔗 Connecting to database...
|
||||
✅ Connected
|
||||
|
||||
📦 Seeding MicroDAOs...
|
||||
✅ DAARION DAO
|
||||
✅ Energy Union
|
||||
✅ GreenFood DAO
|
||||
✅ Soul Retreat Hub
|
||||
✅ Seeded 4 MicroDAOs
|
||||
|
||||
🤖 Seeding core agents (NODE1)...
|
||||
✅ DAARWIZZ
|
||||
✅ Helion
|
||||
✅ GreenFood Bot
|
||||
✅ Soul Bot
|
||||
✅ Seeded 4 core agents
|
||||
|
||||
🔗 Linking agents to MicroDAOs...
|
||||
✅ Linked daarwizz → dao_daarion
|
||||
✅ Linked helion → dao_energy
|
||||
✅ Linked greenfood → dao_greenfood
|
||||
✅ Linked soul → dao_soul
|
||||
✅ Linked agents to MicroDAOs
|
||||
|
||||
============================================================
|
||||
✅ Recovery complete!
|
||||
MicroDAOs: 4
|
||||
Agents: 4
|
||||
|
||||
📝 Next steps:
|
||||
1. Run migrations if needed
|
||||
2. Run scripts/sync-node2-dagi-agents.py for NODE2 agents
|
||||
3. Upload logos/banners to MinIO if not already done
|
||||
============================================================
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Крок 4: Відновити DAGI-агентів NODE2
|
||||
|
||||
```bash
|
||||
python3 scripts/sync-node2-dagi-agents.py
|
||||
```
|
||||
|
||||
Це додасть ~50 DAGI-агентів для NODE2 з `config/agents_city_mapping.yaml`.
|
||||
|
||||
---
|
||||
|
||||
## Крок 5: Перевірити відновлення
|
||||
|
||||
### Перевірка MicroDAOs
|
||||
|
||||
```bash
|
||||
docker exec daarion-postgres psql -U postgres -d daarion -c "
|
||||
SELECT slug, name, district, is_platform
|
||||
FROM microdao
|
||||
ORDER BY pinned_weight;
|
||||
"
|
||||
```
|
||||
|
||||
Очікуваний результат:
|
||||
```
|
||||
slug | name | district | is_platform
|
||||
--------------+-----------------+----------+-------------
|
||||
daarion | DAARION DAO | Core | t
|
||||
energy-union | Energy Union | Energy | t
|
||||
greenfood | GreenFood DAO | Green | t
|
||||
soul-retreat | Soul Retreat Hub| Soul | t
|
||||
```
|
||||
|
||||
### Перевірка агентів
|
||||
|
||||
```bash
|
||||
docker exec daarion-postgres psql -U postgres -d daarion -c "
|
||||
SELECT COUNT(*) as total,
|
||||
COUNT(*) FILTER (WHERE node_id = 'node-1-hetzner-gex44') as node1,
|
||||
COUNT(*) FILTER (WHERE node_id = 'node-2-macbook-m4max') as node2
|
||||
FROM agents;
|
||||
"
|
||||
```
|
||||
|
||||
### Перевірка через API
|
||||
|
||||
```bash
|
||||
# MicroDAOs
|
||||
curl -s "https://daarion.space/api/city/microdao?limit=4" | python3 -m json.tool | head -20
|
||||
|
||||
# Agents
|
||||
curl -s "https://daarion.space/api/city/agents?limit=5" | python3 -m json.tool | head -20
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Крок 6: Завантажити логотипи/банери в MinIO (якщо потрібно)
|
||||
|
||||
Якщо логотипи ще не завантажені в MinIO:
|
||||
|
||||
1. Відкрити MinIO Console: `http://localhost:9001` (або `https://minio.daarion.space`)
|
||||
2. Створити bucket `daarion-assets` (якщо не існує)
|
||||
3. Завантажити файли:
|
||||
- `microdao/logo/daarion.png`
|
||||
- `microdao/logo/energy-union.png`
|
||||
- `microdao/logo/greenfood.png`
|
||||
- `microdao/logo/soul-retreat.png`
|
||||
4. Встановити public read policy для bucket
|
||||
|
||||
Або використати MinIO CLI:
|
||||
|
||||
```bash
|
||||
# Встановити mc (MinIO Client)
|
||||
# https://min.io/docs/minio/linux/reference/minio-mc.html
|
||||
|
||||
# Налаштувати alias
|
||||
mc alias set local http://localhost:9000 assets-admin <password>
|
||||
|
||||
# Завантажити файли
|
||||
mc cp ./assets/logos/daarion.png local/daarion-assets/microdao/logo/daarion.png
|
||||
mc cp ./assets/logos/energy-union.png local/daarion-assets/microdao/logo/energy-union.png
|
||||
# ... і т.д.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Що відновлюється
|
||||
|
||||
### ✅ Відновлюється seed-скриптом:
|
||||
|
||||
- 4 базові MicroDAOs (DAARION, Energy Union, GreenFood, Soul)
|
||||
- 4 core агентів NODE1 (DAARWIZZ, Helion, GreenFood Bot, Soul Bot)
|
||||
- Зв'язки агентів з MicroDAOs
|
||||
|
||||
### ❌ НЕ відновлюється автоматично:
|
||||
|
||||
- City Rooms (потрібно запустити `scripts/seed_city_rooms.py`)
|
||||
- DAGI-агенти NODE2 (потрібно запустити `scripts/sync-node2-dagi-agents.py`)
|
||||
- Node cache дані (відновляться через heartbeat)
|
||||
- Логотипи/банери файли (потрібно завантажити в MinIO)
|
||||
|
||||
---
|
||||
|
||||
## Швидкий чекліст
|
||||
|
||||
- [ ] База даних створена та порожня
|
||||
- [ ] Всі міграції застосовані
|
||||
- [ ] `seed_full_city_reset.py` виконано успішно
|
||||
- [ ] `sync-node2-dagi-agents.py` виконано (для NODE2)
|
||||
- [ ] Логотипи завантажені в MinIO
|
||||
- [ ] API повертає дані
|
||||
- [ ] Frontend відображає MicroDAOs та агентів
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Помилка: "relation does not exist"
|
||||
|
||||
Міграції не застосовані. Запустити Крок 2.
|
||||
|
||||
### Помилка: "duplicate key value"
|
||||
|
||||
Дані вже існують. Seed-скрипт використовує `ON CONFLICT`, тому це нормально.
|
||||
|
||||
### Помилка: "connection refused"
|
||||
|
||||
PostgreSQL не запущений:
|
||||
```bash
|
||||
docker compose -f docker-compose.db.yml up -d db
|
||||
```
|
||||
|
||||
### Логотипи не відображаються
|
||||
|
||||
1. Перевірити що файли в MinIO
|
||||
2. Перевірити що bucket має public read policy
|
||||
3. Перевірити що URL в БД правильні:
|
||||
```bash
|
||||
docker exec daarion-postgres psql -U postgres -d daarion -c "SELECT slug, logo_url FROM microdao;"
|
||||
```
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
# TASK_PHASE_DATABASE_HARDENING_AND_ASSETS_MIGRATION_v1
|
||||
|
||||
**Статус:** TODO
|
||||
**Пріоритет:** CRITICAL
|
||||
**Мета:** Захистити базу даних від втрати та перенести assets на S3-compatible storage
|
||||
|
||||
---
|
||||
|
||||
## 0. Проблема
|
||||
|
||||
1. **База даних зникла** - PostgreSQL був пересозданий, всі дані втрачені
|
||||
2. **Логотипи/банери зникли** - файли на диску є, але посилання в БД пропали
|
||||
3. **Немає автоматичних бекапів** - немає механізму відновлення
|
||||
4. **Assets залежать від локального сервера** - при втраті БД втрачаються посилання
|
||||
|
||||
---
|
||||
|
||||
## 1. Цілі
|
||||
|
||||
1. ✅ PostgreSQL → persistent Docker volume (не зникає при перезапуску)
|
||||
2. ✅ Автоматичні бекапи БД кожні 12 годин
|
||||
3. ✅ Assets (лого/банери/аватарки) → MinIO (S3-compatible)
|
||||
4. ✅ Спрощений `normalizeAssetUrl` для S3 URLs
|
||||
5. ✅ Seed-скрипт для аварійного відновлення міста
|
||||
|
||||
---
|
||||
|
||||
## 2. Кроки виконання
|
||||
|
||||
### 2.1. Міграція PostgreSQL → persistent storage
|
||||
|
||||
**Файл:** `docker-compose.yml` або `docker-compose.db.yml`
|
||||
|
||||
- [ ] Додати volume `pgdata:/var/lib/postgresql/data` до DB service
|
||||
- [ ] Створити volume `pgdata` в секції volumes
|
||||
- [ ] Перевірити що дані зберігаються після перезапуску
|
||||
|
||||
### 2.2. Backup система БД
|
||||
|
||||
**Файл:** `docker-compose.yml`
|
||||
|
||||
- [ ] Додати service `db-backup` з `prodrigestivill/postgres-backup-local`
|
||||
- [ ] Налаштувати `SCHEDULE: "@every 12h"`
|
||||
- [ ] Налаштувати retention (7 днів, 4 тижні, 6 місяців)
|
||||
- [ ] Створити директорію `db_backups/` для зберігання
|
||||
|
||||
### 2.3. MinIO для assets storage
|
||||
|
||||
**Файл:** `docker-compose.yml`
|
||||
|
||||
- [ ] Додати service `minio` з `minio/minio:latest`
|
||||
- [ ] Налаштувати volumes `minio_data:/data`
|
||||
- [ ] Налаштувати environment variables (MINIO_ROOT_USER, MINIO_ROOT_PASSWORD)
|
||||
- [ ] Відкрити порти 9000 (API) та 9001 (console)
|
||||
|
||||
### 2.4. Бекенд: інтеграція з MinIO
|
||||
|
||||
**Файли:**
|
||||
- `services/city-service/config.py` - додати налаштування MinIO
|
||||
- `services/city-service/lib/assets_client.py` - створити клієнт для MinIO
|
||||
- `services/city-service/routes_city.py` - оновити upload endpoints
|
||||
|
||||
- [ ] Додати залежність `minio` в `requirements.txt`
|
||||
- [ ] Створити `assets_client.py` з функцією `upload_asset()`
|
||||
- [ ] Оновити upload handlers для MicroDAO logo/banner
|
||||
- [ ] Оновити upload handlers для Agent avatar
|
||||
- [ ] Перевірити що URL зберігаються як повні HTTPS URLs
|
||||
|
||||
### 2.5. Міграція БД для asset полів
|
||||
|
||||
**Файл:** `migrations/043_asset_urls_to_text.sql`
|
||||
|
||||
- [ ] Змінити тип `logo_url`, `banner_url`, `avatar_url` на `text`
|
||||
- [ ] Додати коментарі про формат URL (HTTPS до MinIO)
|
||||
|
||||
### 2.6. Frontend: спрощений normalizeAssetUrl
|
||||
|
||||
**Файл:** `apps/web/src/lib/utils/assetUrl.ts`
|
||||
|
||||
- [ ] Спростити функцію - тільки passthrough для HTTPS URLs
|
||||
- [ ] Залишити fallback для старих `/api/static/` URLs
|
||||
- [ ] Прибрати всю складну логіку нормалізації
|
||||
|
||||
### 2.7. Seed-скрипт для аварійного відновлення
|
||||
|
||||
**Файл:** `scripts/seed_full_city_reset.py`
|
||||
|
||||
- [ ] Створити базові MicroDAO (DAARION, Energy Union, GreenFood, Soul)
|
||||
- [ ] Додати райони міста
|
||||
- [ ] Додати базові city rooms
|
||||
- [ ] Додати core агентів NODE1
|
||||
- [ ] Використовувати S3 URLs для логотипів/банерів
|
||||
|
||||
### 2.8. Документація
|
||||
|
||||
**Файли:**
|
||||
- `docs/DB_RESTORE.md` - інструкція по відновленню БД
|
||||
- `docs/SEED_RECOVERY.md` - інструкція по аварійному відновленню
|
||||
- `docs/INFRA_ASSETS_MINIO.md` - документація по MinIO setup
|
||||
|
||||
- [ ] Створити DB_RESTORE.md з командами відновлення
|
||||
- [ ] Створити SEED_RECOVERY.md з інструкціями
|
||||
- [ ] Створити INFRA_ASSETS_MINIO.md з конфігурацією
|
||||
|
||||
---
|
||||
|
||||
## 3. Перевірка після виконання
|
||||
|
||||
- [ ] PostgreSQL дані зберігаються після `docker compose restart db`
|
||||
- [ ] Бекапи створюються в `db_backups/` кожні 12 годин
|
||||
- [ ] MinIO доступний на `http://localhost:9000` (API) та `http://localhost:9001` (console)
|
||||
- [ ] Upload логотипу MicroDAO зберігається в MinIO та повертає HTTPS URL
|
||||
- [ ] Frontend відображає логотипи з MinIO URLs
|
||||
- [ ] Seed-скрипт відновлює базові дані міста
|
||||
|
||||
---
|
||||
|
||||
## 4. ENV змінні
|
||||
|
||||
Додати в `.env`:
|
||||
|
||||
```env
|
||||
# PostgreSQL
|
||||
POSTGRES_PASSWORD=super-secret
|
||||
|
||||
# MinIO
|
||||
MINIO_ROOT_USER=assets-admin
|
||||
MINIO_ROOT_PASSWORD=very-strong-password
|
||||
ASSETS_BUCKET=daarion-assets
|
||||
ASSETS_PUBLIC_BASE_URL=https://assets.daarion.space/daarion-assets
|
||||
MINIO_ENDPOINT=http://minio:9000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. DNS налаштування
|
||||
|
||||
- [ ] `assets.daarion.space` → IP NODE1 (для публічного доступу до assets)
|
||||
- [ ] `minio.daarion.space` → IP NODE1 (опційно, для консолі)
|
||||
|
||||
---
|
||||
|
||||
## 6. Caddy/NGINX конфігурація
|
||||
|
||||
Див. `docs/INFRA_ASSETS_MINIO.md` для деталей налаштування reverse proxy.
|
||||
|
||||
Reference in New Issue
Block a user