This commit is contained in:
Brasdrive 2025-11-08 20:11:42 -04:00
parent 388f5112de
commit 6a4949f181
1 changed files with 213 additions and 104 deletions

269
README.md
View File

@ -1,116 +1,225 @@
# Flysystem Offload — Almacenamiento universal para WordPress
### Flysystem Offload
Flysystem Offload sustituye el sistema de archivos local de WordPress por un backend remoto operado con Flysystem v3. Los medios se suben, sirven y eliminan directamente desde el proveedor seleccionado (S3 y compatibles en la primera versión) sin modificar el flujo editorial.
Flysystem Offload es un plugin minimalista de WordPress que sustituye el almacenamiento local por un backend remoto usando [Flysystem v3](https://flysystem.thephpleague.com/v3/docs/). Inspirado por el estilo ligero de “S3 Uploads”, se concentra en proporcionar una integración transparente, sin panel de administración, y con compatibilidad total con el flujo estándar de medios de WordPress.
## Características
### Características
- **Proveedor seleccionable:** Amazon S3 y endpoints compatibles (MinIO, DigitalOcean Spaces, Wasabi, etc.).
- **Integración transparente:** hooks de `upload_dir`, `wp_get_attachment_url`, stream wrapper `fly://` y borrado sincronizado.
- **Arquitectura modular:** preparada para añadir SFTP, WebDAV y otros adaptadores en iteraciones futuras.
- **Panel de ajustes:** selector de proveedor y credenciales gestionadas desde la administración.
- Registro temprano del stream wrapper para redirigir `wp-content/uploads` a almacenamiento remoto.
- Generación automática de URLs públicas coherentes con el prefijo de bucket.
- Cabeceras `Cache-Control` y `Expires` aplicadas en las subidas.
- Limpieza remota de adjuntos al eliminarlos desde WordPress.
- Configuración declarativa vía `config/flysystem-offload.php`.
## Requisitos
### Requisitos
- PHP 8.0+
- WordPress 6.0+
- Extensiones PHP: `curl`, `mbstring`, `xml`
- Acceso a Composer durante la construcción del paquete (o usar la imagen Docker proporcionada)
- Credenciales válidas de S3 o servicio compatible
- WordPress 6.5 o superior.
- PHP 8.2 o superior (probado en PHP 8.4).
- Extensiones PHP: `json`, `curl`, `mbstring`.
- Credenciales válidas para un servicio S3 compatible.
- Composer para instalar dependencias (AWS SDK).
## Instalación
### Instalación
### Opción A · Proyecto existente de WordPress
1. Clona el repositorio en `wp-content/plugins/flysystem-offload`.
2. Ejecuta `composer install` dentro del plugin si el SDK no está en el proyecto.
3. Copia `config/flysystem-offload.example.php` a `config/flysystem-offload.php`.
4. Ajusta las claves del archivo de configuración.
5. Activa el plugin desde el dashboard de WordPress o vía `wp plugin activate flysystem-offload`.
### Configuración básica (`config/flysystem-offload.php`)
1. Clona este repositorio dentro del árbol de tu sitio:
`git clone https://git.brasdrive.com.br/Brasdrive/flysystem-offload.git`
2. Entra en la carpeta del plugin y ejecuta Composer para traer las dependencias:
```php
<?php
return [
'driver' => 's3',
'visibility' => 'public',
```bash
cd flysystem-offload
composer install --no-dev --optimize-autoloader
```
'stream' => [
'protocol' => 'flysystem',
'root_prefix' => '',
'host' => 'uploads',
],
3. Empaqueta el plugin con la carpeta `vendor/` incluida y súbelo a `/wp-content/plugins/` del sitio que corresponda (vía SCP, rsync o panel de hosting).
4. Activa **Flysystem Offload** desde **Plugins > Plugins instalados** en el escritorio de WordPress.
'uploads' => [
'base_url' => getenv('FLYSYSTEM_OFFLOAD_BASE_URL') ?: 'https://offload.brasdrive.com.br',
'delete_remote' => true,
'prefer_local_for_missing' => false,
'cache_control' => 'public, max-age=31536000, immutable',
'expires_ttl' => 31536000,
'expires' => null,
],
### Opción B · Imagen Docker (multi-stage)
'admin' => [
'enabled' => false,
],
El repositorio incluye un Dockerfile que construye una imagen basada en `wordpress:6.8.3-php8.4-apache` y prepara el plugin en tiempo de build:
's3' => [
'key' => getenv('AWS_ACCESS_KEY_ID') ?: null,
'secret' => getenv('AWS_SECRET_ACCESS_KEY') ?: null,
'session_token' => getenv('AWS_SESSION_TOKEN') ?: null,
'region' => getenv('AWS_DEFAULT_REGION') ?: 'us-east-1',
'bucket' => getenv('FLYSYSTEM_OFFLOAD_BUCKET') ?: 'your-bucket-name',
'prefix' => getenv('FLYSYSTEM_OFFLOAD_PREFIX') ?: 'wordpress',
'endpoint' => getenv('FLYSYSTEM_OFFLOAD_ENDPOINT') ?: null,
'use_path_style_endpoint' => (bool) (getenv('AWS_USE_PATH_STYLE_ENDPOINT') ?: false),
'version' => 'latest',
'options' => [],
'default_options' => [],
'acl_public' => 'public-read',
'acl_private' => 'private',
],
];
```
```Dockerfile
# Etapa 1: composer:2.8.12 instala las dependencias en /app/wp-content/plugins/flysystem-offload
# Etapa 2: copia el plugin con vendor/ dentro de /usr/src/wordpress y /var/www/html
# Instala redis, WP-CLI y algunas utilidades
# Habilita módulos de Apache y carga configuraciones personalizadas de PHP/Apache
```
### Normalización de configuración
Pasos para usarla:
`Plugin::normaliseConfig()` establece valores por defecto, garantiza que `uploads.base_url` termine sin `/`, y fusiona `s3.prefix`, `stream.root_prefix` y `stream.host` para producir el prefijo remoto final.
```bash
# Desde la raíz del repositorio
docker build -t flysystem-offload-wp .
# Arranca el contenedor exponiendo el puerto 80 (puedes convertirlo en un stack Compose si lo prefieres)
docker run --rm -p 8080:80 flysystem-offload-wp
```
### Funcionamiento del stream wrapper
La imagen resultante ya contiene:
- El filtro `upload_dir` devuelve rutas como `flysystem://uploads/2025/01`.
- `MediaHooks` reescribe automáticamente `wp_get_attachment_url` para apuntar a la ruta remota (`base_url + prefijo + archivo`).
- El prefijo remoto resultante combina `s3.prefix`, `stream.root_prefix` y `stream.host` para reflejar exactamente la clave almacenada en el bucket (ej.: `wordpress/uploads/2025/01/file.jpg`).
- El plugin con todas sus dependencias PHP en `/usr/src/wordpress/wp-content/plugins/flysystem-offload`.
- Copia pre-sincronizada en `/var/www/html/wp-content/plugins` para que esté disponible desde el primer arranque.
- Extensión Redis habilitada, WP-CLI disponible y módulos `rewrite`, `headers`, `expires`, `deflate` activos.
### Cabeceras de caché
### Nota sobre Composer
El adaptador S3 añade `Cache-Control` y `Expires` a través de las opciones por defecto de Flysystem:
En entornos que no usan Docker, asegúrate de ejecutar `<plugin>/composer install` antes de empaquetar o desplegar. WordPress no ejecuta Composer automáticamente durante la activación de un plugin.
```php
$defaultOptions['CacheControl'] = 'public, max-age=31536000, immutable';
$defaultOptions['Expires'] = gmdate('D, d M Y H:i:s \G\M\T', time() + 31536000);
```
## Configuración inicial (S3 / compatible)
Puede personalizarse mediante el filtro `flysystem_offload_s3_default_write_options`.
### Escenario bucket público (probado con IDrive e2)
1. En el escritorio de WordPress abre **Ajustes > Flysystem Offload**.
2. Selecciona **Amazon S3 / Compatible**.
3. Completa los campos:
- Access Key
- Secret Key (permanece oculta tras guardarla)
- Región (`us-east-1`, `eu-west-1`, etc.)
- Bucket
- Prefijo opcional (subcarpeta dentro del bucket)
- Endpoint personalizado (solo para servicios compatibles con S3)
- URL CDN opcional (sustituye la URL pública del bucket por tu dominio CDN)
4. Guarda los cambios. El plugin reconstruye automáticamente el filesystem y el stream wrapper.
**Prefijo base:** Puedes definir un prefijo global (`wordpress/uploads/`) que se añadirá a todas las rutas remotas antes de delegar en el adaptador.
1. Marcar bucket como público en la consola de IDrive e2.
2. Aplicar policy simple (la UI sólo acepta una versión sin `Condition`).
## Flujo de funcionamiento
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPublicReadUploads",
"Effect": "Allow",
"Principal": { "AWS": "*" },
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::flysystem-offload/wordpress/uploads/*"
]
}
]
}
```
3. Configurar CORS si el panel o el frontend cargan las imágenes desde otro origen.
- WordPress sigue usando `wp_handle_upload()`.
- Los filtros de `upload_dir` cambian `basedir` a `fly://...`.
- El stream wrapper reenvía lecturas/escrituras a Flysystem y este al cliente S3.
- `wp_get_attachment_url` reescribe la URL base con el dominio del bucket o el CDN configurado.
- Al eliminar un adjunto, se borran el archivo principal y sus derivadas desde el almacenamiento remoto.
```json
[
{
"AllowedOrigins": [
"https://offload.brasdrive.com.br"
],
"AllowedMethods": [
"GET",
"HEAD"
],
"AllowedHeaders": [
"*"
],
"ExposeHeaders": [
"ETag",
"Last-Modified",
"x-amz-request-id",
"x-amz-id-2"
],
"AllowCredentials": false,
"MaxAgeSeconds": 86400
}
]
```
## Roadmap inmediato
### Escenario recomendado: bucket privado + Cloudflare
- Campos y validaciones para SFTP y WebDAV.
- Health check vía WP-CLI.
- Herramientas de migración para copiar la biblioteca existente al proveedor remoto.
- Adaptadores adicionales (GCS, Azure Blob) y conectores OAuth (Drive, OneDrive, Dropbox).
1. **Bucket privado**
- Mantenerlo cerrado; permitir `s3:GetObject` sólo a IPs de Cloudflare.
- Aplicar policy vía CLI (`aws s3api put-bucket-policy`). Ejemplo con `Condition` por IP (mantén la lista actualizada con la API `GET /client/v4/ips`):
## Contribuir
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCloudflareAccess",
"Effect": "Allow",
"Principal": { "AWS": "*" },
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::flysystem-offload/wordpress/uploads/*"
],
"Condition": {
"IpAddress": {
"aws:SourceIp": [
"173.245.48.0/20",
"103.21.244.0/22"
// ...
]
}
}
}
]
}
```
2. **Cloudflare**
1. Haz fork y crea una rama (`feature/tu-feature`).
2. Sigue los [WordPress Coding Standards](https://developer.wordpress.org/coding-standards/).
3. Ejecuta tests si están disponibles y actualiza la documentación si corresponde.
4. Abre un Pull Request describiendo el cambio.
- CNAME `offload.brasdrive.com.br` → endpoint IDrive (ej.`u8k6.va11.idrivee2-9.com`).
- Proxy naranja activado; SSL universal.
- Reglas WAF o Rate Limiting opcionales.
- Documentación: Este archivo.
- Issues/Contacto: jdavidcamejo@gmail.com
3. **WordPress**
## Licencia
- `uploads.base_url = 'https://offload.brasdrive.com.br'`.
- Mantener `prefer_local_for_missing = false`.
GPL v2. Consulta [LICENSE](LICENSE) para más detalles.
4. **Automatización**
---
- Script/cron que consulte la API de Cloudflare, regenere la policy con los nuevos rangos IP y la aplique (controlando con `etag`).
Desarrollado por [Brasdrive](https://brasdrive.com.br).
5. **Caché y purgas**
- Cloudflare respeta las cabeceras `Cache-Control` y `Expires`.
- Purgar desde la consola de Cloudflare cuando sea necesario (por URL o caché completa).
### Compatibilidad de backends
Actualmente Flysystem Offload proporciona el adaptador **S3**. El código está preparado para admitir otros backends soportados por Flysystem (WebDAV, SFTP, etc.), pero aún deben agregarse adaptadores específicos (`WebdavAdapter`, `SftpAdapter`, etc.) y sus respectivas configuraciones. Esto forma parte del plan inmediato de ampliación: se crearán clases adicionales en `src/Filesystem/Adapters/`, se extenderá la factoría de adaptadores y se actualizará la documentación para cada backend.
### Notas sobre IDrive e2
- Para habilitar un bucket público reciente, es necesario contactar soporte.
- El editor web de políticas no soporta `Condition`; aplicar policies complejas via CLI/API.
- CORS sí puede configurarse desde la UI mientras se respete el formato simple.
- Usa el endpoint regional del bucket como `endpoint` en configuración y CLI.
### Solución de problemas
- URLs sin prefijo: actualizar a la versión reciente de `MediaHooks.php`.
- Confirmar `s3.prefix`, `stream.root_prefix`, `stream.host`.
- Falta de `Cache-Control`/`Expires`: revisar `config` y `s3.default_options`.
- Error `_.filter` en la UI: bug del formulario; usar CLI.
- Archivos inaccesibles con bucket privado: verificar policy, rangos Cloudflare, CNAME activo.
### Contribuciones
Se aceptan PRs. Mantén el enfoque minimalista, sin panel de administración, y prioriza compatibilidad con el flujo nativo de WordPress. Roadmap cercano: añadir adaptadores para otros drivers Flysystem.
### Licencia
MIT. Revisa el archivo `LICENSE`.