# 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 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 працює