Object Storage S3 Distribuido (MinIO)
Para operar en clústeres balanceados bajo el principio de servidores web sin estado (Stateless), Mañuti no almacena archivos binarios en el disco duro local de los nodos. Todo archivo persistente (avatares, diagramas vectoriales, respaldos y exportaciones JSON) se delega a MinIO, un servidor de almacenamiento de objetos distribuido compatible al 100% con la API de Amazon S3.
1. Configuración de Entorno (.env)
El adaptador utiliza el driver s3 de Flysystem configurado contra la IP o clúster de MinIO:
FILESYSTEM_DISK=s3
AWS_ACCESS_KEY_ID=minioadmin
AWS_SECRET_ACCESS_KEY=tCT8H83o7U7Gm2
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=framedev-bucket
AWS_ENDPOINT=http://192.168.1.153:9000
AWS_URL=http://192.168.1.153:9000/framedev-bucket
AWS_USE_PATH_STYLE_ENDPOINT=true
Nota sobre AWS_USE_PATH_STYLE_ENDPOINT=true: Es obligatorio para MinIO y emuladores S3 locales, forzando la estructura http://endpoint/bucket/objeto en lugar de http://bucket.endpoint/objeto.
2. Segregación de Buckets y Políticas de Acceso
El almacenamiento se organiza por prefijos virtuales respetando políticas de seguridad:
flowchart LR
BUCKET["[framedev-bucket]"]
AVATARS["/avatars/<br/>(Política: Download Público / Fotos de perfil)"]
ARCH["/architecture/<br/>(Política: Download Público / Mapas SVG interactivos)"]
PRIV["/private/exports/<br/>(Política: Privada / URLs firmadas temporalmente de datos ARCO)"]
BUCKET --> AVATARS
BUCKET --> ARCH
BUCKET --> PRIV
Configuración de Descarga Anónima para Avatares (mc)
Utilizando el cliente oficial de MinIO (mc):
mc alias set local http://192.168.1.153:9000 minioadmin tCT8H83o7U7Gm2
mc anonymous set download local/framedev-bucket/avatars
mc anonymous set download local/framedev-bucket/architecture
3. Implementación en Código (Persistencia Sin Estado)
Carga de Avatares en el Perfil
if ($request->hasFile('photo')) {
$path = $request->file('photo')->storePublicly('avatars', 's3');
$user->setConfig('avatar', $path);
}
Carga de Diagramas Vectoriales con MIME Type Forzado
Storage::disk('s3')->put(
'architecture/full_flow_map.svg',
fopen($localPath, 'r+'),
[
'visibility' => 'public',
'ContentType' => 'image/svg+xml' // Garantiza renderizado directo en navegador
]
);