Skip to content

# 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`

    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):

    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

    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:

    {
      "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`

    # 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:

      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` (фрагмент)

    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 (приклад)

    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`

    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

    # Використовуючи MinIO Client (mc)
    mc alias set local http://localhost:9000 assets-admin <password>
    mc mirror local/daarion-assets ./backups/minio/

### Автоматичний backup (cron)

    # Додати в crontab
    0 2 * * * docker exec daarion-minio mc mirror minio/daarion-assets /backups/minio/$(date +\%F)

## 11. Міграція з локальних файлів

### Крок 1: Завантажити існуючі файли в MinIO

    # Використовуючи 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 в БД

    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"

    # Перевірити статус
    docker ps | grep minio

    # Перезапустити
    docker compose -f docker-compose.db.yml restart minio

### Проблема: "Bucket does not exist"

    # Створити через mc
    mc mb local/daarion-assets

    # Або через консоль MinIO

## 13. Моніторинг

### Перевірка використання диска

    docker exec daarion-minio du -sh /data

### Перевірка кількості об'єктів

Через MinIO Console → Bucket → Statistics

## 14. Реплікація на зовнішній S3 (опційно)

Для додаткової надійності можна налаштувати реплікацію на AWS S3 або Cloudflare R2:

    # Налаштувати 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 працює