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
9.2 KiB
9.2 KiB
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
- Натиснути "Create Bucket"
- Назва:
daarion-assets - Region: залишити за замовчуванням
- Натиснути "Create Bucket"
Крок 4: Встановити public read policy
- Відкрити bucket
daarion-assets - Перейти в "Access Policy"
- Вибрати "Public" або "Custom"
- Для 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()- завантажити файл, повернути URLdelete_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"
Перевірити:
- Bucket має public read policy
- URL правильний (включає bucket name)
- 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 працює