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 editoria
Go to file
Brasdrive 6a4949f181 2.1.0 2025-11-08 20:11:42 -04:00
config 2.1.0 2025-11-08 19:32:32 -04:00
src 2.1.0 2025-11-08 19:32:32 -04:00
.gitignore 2.0.0 2025-11-06 19:15:34 -04:00
README.md 2.1.0 2025-11-08 20:11:42 -04:00
composer.json 2.1.0 2025-11-08 19:32:32 -04:00
flysystem-offload.php 2.1.0 2025-11-08 19:32:32 -04:00

README.md

Flysystem Offload

Flysystem Offload es un plugin minimalista de WordPress que sustituye el almacenamiento local por un backend remoto usando Flysystem v3. 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

  • 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

  • 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

  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)

<?php
return [
    'driver' => 's3',
    'visibility' => 'public',

    'stream' => [
        'protocol' => 'flysystem',
        'root_prefix' => '',
        'host' => 'uploads',
    ],

    '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,
    ],

    'admin' => [
        'enabled' => false,
    ],

    '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',
    ],
];

Normalización de configuración

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.

Funcionamiento del stream wrapper

  • 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).

Cabeceras de caché

El adaptador S3 añade Cache-Control y Expires a través de las opciones por defecto de Flysystem:

$defaultOptions['CacheControl'] = 'public, max-age=31536000, immutable';
$defaultOptions['Expires'] = gmdate('D, d M Y H:i:s \G\M\T', time() + 31536000);

Puede personalizarse mediante el filtro flysystem_offload_s3_default_write_options.

Escenario bucket público (probado con IDrive e2)

  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).
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowPublicReadUploads",
      "Effect": "Allow",
      "Principal": { "AWS": "*" },
      "Action": [
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::flysystem-offload/wordpress/uploads/*"
      ]
    }
  ]
}
  1. Configurar CORS si el panel o el frontend cargan las imágenes desde otro origen.
[
  {
    "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
  }
]

Escenario recomendado: bucket privado + Cloudflare

  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):
{
  "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"
            // ...
          ]
        }
      }
    }
  ]
}
  1. Cloudflare

    • CNAME offload.brasdrive.com.br → endpoint IDrive (ej.u8k6.va11.idrivee2-9.com).
    • Proxy naranja activado; SSL universal.
    • Reglas WAF o Rate Limiting opcionales.
  2. WordPress

    • uploads.base_url = 'https://offload.brasdrive.com.br'.
    • Mantener prefer_local_for_missing = false.
  3. Automatización

    • Script/cron que consulte la API de Cloudflare, regenere la policy con los nuevos rangos IP y la aplique (controlando con etag).
  4. 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.