Files
microdao-daarion/docs/INFRA_ASSETS_MINIO.md
Apple 8e8f95e9ef 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
2025-12-02 01:56:39 -08:00

390 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 працює