166 lines
6.0 KiB
PHP
166 lines
6.0 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace FlysystemOffload;
|
|
|
|
use FlysystemOffload\Admin\HealthCheck;
|
|
use FlysystemOffload\Config\ConfigLoader;
|
|
use FlysystemOffload\Filesystem\FilesystemFactory;
|
|
use FlysystemOffload\Media\MediaHooks;
|
|
use FlysystemOffload\Settings\SettingsPage;
|
|
use FlysystemOffload\StreamWrapper\FlysystemStreamWrapper;
|
|
use League\Flysystem\FilesystemOperator;
|
|
use League\Flysystem\Visibility;
|
|
|
|
final class Plugin {
|
|
private static bool $bootstrapped = false;
|
|
private static ?Plugin $instance = null;
|
|
|
|
private array $config;
|
|
private FilesystemOperator $filesystem;
|
|
private MediaHooks $mediaHooks;
|
|
private ?SettingsPage $settingsPage;
|
|
private HealthCheck $healthCheck;
|
|
|
|
private function __construct(
|
|
array $config,
|
|
FilesystemOperator $filesystem,
|
|
MediaHooks $mediaHooks,
|
|
?SettingsPage $settingsPage,
|
|
HealthCheck $healthCheck
|
|
) {
|
|
$this->config = $config;
|
|
$this->filesystem = $filesystem;
|
|
$this->mediaHooks = $mediaHooks;
|
|
$this->settingsPage = $settingsPage;
|
|
$this->healthCheck = $healthCheck;
|
|
}
|
|
|
|
public static function bootstrap(): void {
|
|
if (self::$bootstrapped) {
|
|
return;
|
|
}
|
|
|
|
$configDirectory = \defined('FLYSYSTEM_OFFLOAD_CONFIG_PATH')
|
|
? FLYSYSTEM_OFFLOAD_CONFIG_PATH
|
|
: \dirname(__DIR__) . '/config';
|
|
|
|
$configLoader = new ConfigLoader($configDirectory);
|
|
$config = self::normaliseConfig($configLoader->load());
|
|
|
|
$filesystemFactory = new FilesystemFactory();
|
|
$filesystem = $filesystemFactory->make($config);
|
|
|
|
FlysystemStreamWrapper::register(
|
|
$filesystem,
|
|
$config['stream']['protocol'],
|
|
$config['stream']['root_prefix'],
|
|
$config['visibility']
|
|
);
|
|
|
|
$mediaHooks = new MediaHooks($filesystem, $config);
|
|
$mediaHooks->register();
|
|
|
|
$settingsPage = null;
|
|
if (! empty($config['admin']['enabled']) && \is_admin()) {
|
|
$settingsPage = new SettingsPage($filesystem, $config);
|
|
$settingsPage->register();
|
|
}
|
|
|
|
$healthCheck = new HealthCheck($filesystem, $config);
|
|
$healthCheck->register();
|
|
|
|
self::$instance = new self($config, $filesystem, $mediaHooks, $settingsPage, $healthCheck);
|
|
self::$bootstrapped = true;
|
|
}
|
|
|
|
public static function instance(): ?self {
|
|
return self::$instance;
|
|
}
|
|
|
|
public function config(): array {
|
|
return $this->config;
|
|
}
|
|
|
|
public function filesystem(): FilesystemOperator {
|
|
return $this->filesystem;
|
|
}
|
|
|
|
private static function normaliseConfig(array $config): array {
|
|
$defaults = [
|
|
'driver' => 's3',
|
|
'visibility' => Visibility::PUBLIC,
|
|
'cache_ttl' => 900,
|
|
'stream' => [
|
|
'protocol' => 'flysystem',
|
|
'root_prefix' => '',
|
|
'host' => 'uploads',
|
|
],
|
|
'uploads' => [
|
|
'base_url' => '',
|
|
'delete_remote' => true,
|
|
'prefer_local_for_missing' => false,
|
|
'cache_control' => 'public, max-age=31536000, immutable',
|
|
'expires' => null,
|
|
'expires_ttl' => 31536000,
|
|
],
|
|
'admin' => [
|
|
'enabled' => false,
|
|
],
|
|
's3' => [
|
|
'acl_public' => 'public-read',
|
|
'acl_private' => 'private',
|
|
'default_options' => [],
|
|
],
|
|
];
|
|
|
|
$config = array_replace_recursive($defaults, $config);
|
|
|
|
$config['visibility'] = self::normaliseVisibility((string) ($config['visibility'] ?? Visibility::PUBLIC));
|
|
$config['stream']['protocol'] = self::sanitizeProtocol((string) $config['stream']['protocol']);
|
|
$config['stream']['root_prefix'] = self::normalizePathSegment((string) $config['stream']['root_prefix']);
|
|
$config['stream']['host'] = self::normalizePathSegment((string) $config['stream']['host']) ?: 'uploads';
|
|
|
|
if (empty($config['uploads']['base_url'])) {
|
|
$config['uploads']['base_url'] = rtrim(content_url('uploads'), '/');
|
|
} else {
|
|
$config['uploads']['base_url'] = rtrim((string) $config['uploads']['base_url'], '/');
|
|
}
|
|
|
|
$config['uploads']['delete_remote'] = (bool) $config['uploads']['delete_remote'];
|
|
$config['uploads']['prefer_local_for_missing'] = (bool) $config['uploads']['prefer_local_for_missing'];
|
|
$config['uploads']['cache_control'] = trim((string) $config['uploads']['cache_control']);
|
|
$config['uploads']['expires'] = $config['uploads']['expires']
|
|
? trim((string) $config['uploads']['expires'])
|
|
: null;
|
|
$config['uploads']['expires_ttl'] = max(0, (int) ($config['uploads']['expires_ttl'] ?? 0));
|
|
|
|
$config['s3']['acl_public'] = (string) ($config['s3']['acl_public'] ?? 'public-read');
|
|
$config['s3']['acl_private'] = (string) ($config['s3']['acl_private'] ?? 'private');
|
|
$config['s3']['default_options'] = is_array($config['s3']['default_options'])
|
|
? $config['s3']['default_options']
|
|
: [];
|
|
|
|
return $config;
|
|
}
|
|
|
|
private static function normaliseVisibility(string $visibility): string {
|
|
$visibility = \strtolower($visibility);
|
|
|
|
return $visibility === Visibility::PRIVATE
|
|
? Visibility::PRIVATE
|
|
: Visibility::PUBLIC;
|
|
}
|
|
|
|
private static function sanitizeProtocol(string $protocol): string {
|
|
$protocol = \preg_replace('/[^A-Za-z0-9_\-]/', '', $protocol) ?? 'flysystem';
|
|
$protocol = \strtolower($protocol);
|
|
|
|
return $protocol !== '' ? $protocol : 'flysystem';
|
|
}
|
|
|
|
private static function normalizePathSegment(string $segment): string {
|
|
return \trim($segment, " \t\n\r\0\x0B/");
|
|
}
|
|
}
|