food-market/src/food-market.api/Storage/StorageOptions.cs
nns 7de159d5f2
Some checks are pending
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker API / Build + push API (push) Waiting to run
Docker API / Deploy API on stage (push) Blocked by required conditions
feat(storage): IObjectStorage abstraction (Local + MinIO) — P2-15
Backend:
- src/food-market.api/Storage/IObjectStorage.cs — абстракция
  (SaveAsync, OpenAsync, DeleteAsync, PublicUrl). Имя реализации (Kind)
  для логов.
- LocalObjectStorage — ContentRoot/uploads/{key}. По умолчанию.
- MinioObjectStorage — S3-совместимый bucket, ключ-объекта совпадает с
  тем что хранится в БД (products/{id:N}/{guid}.png). PutObject с явным
  size (для NetworkStream копируем в MemoryStream).
- StorageOptions: Type=Local|Minio, Endpoint, AccessKey, SecretKey,
  UseSsl, Bucket=food-market-uploads.
- StorageBootstrap.AddObjectStorage — DI-регистрация с runtime fallback
  на Local если MinIO-config пустой; MinioBootstrap (IHostedService)
  создаёт bucket на старте, ловит ошибку и не валит API.
- UploadsController: GET /uploads/{**path} → стримит из IObjectStorage
  (cache-control 7 дней). Нужен когда MinIO активен — для Local nginx
  раздаёт быстрее, но фолбэк работает.
- ProductImagesController отрефакторен на IObjectStorage; URL'ы в БД
  остаются /uploads/products/{id}/{guid}.ext.

Тесты:
- StorageAbstractionTests (3/3 ✓): Local default, round-trip bytes,
  PublicUrl pattern.

Stage-готовность:
- deploy/docker-compose.yml на стейдже обновлён (через scp): добавлен
  minio container, depends_on в api, env переменные Storage__*.
  Bucket автосоздаётся.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 20:17:10 +05:00

32 lines
1.4 KiB
C#
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.

namespace foodmarket.Api.Storage;
/// <summary>Конфиг секции <c>Storage</c>. Биндится в Program.cs.</summary>
public sealed class StorageOptions
{
/// <summary>Какой backend использовать. <c>Local</c> (default) — диск
/// в ContentRoot/uploads. <c>Minio</c> — S3-совместимый bucket.</summary>
public StorageType Type { get; set; } = StorageType.Local;
/// <summary>Относительный путь под ContentRoot для Local-стораджа.</summary>
public string LocalRoot { get; set; } = "uploads";
/// <summary>S3/MinIO endpoint (без https). Например
/// <c>192.168.1.190:9000</c> или <c>minio:9000</c>. Прокидывается через
/// env <c>Storage__Endpoint</c>.</summary>
public string? Endpoint { get; set; }
/// <summary>S3 access key. env <c>Storage__AccessKey</c>.</summary>
public string? AccessKey { get; set; }
/// <summary>S3 secret key. env <c>Storage__SecretKey</c>.</summary>
public string? SecretKey { get; set; }
/// <summary>HTTPS или HTTP. На staging-vm у нас HTTP.</summary>
public bool UseSsl { get; set; }
/// <summary>Bucket. Создаётся автоматически на старте если не существует.</summary>
public string Bucket { get; set; } = "food-market-uploads";
}
public enum StorageType { Local, Minio }