feat: add S3-compatible storage provider (MinIO, Ceph, R2, etc.)

Adds a new 'S3-Compatible Storage' provider that works with any
S3-API-compatible object storage service, including MinIO, Ceph,
Cloudflare R2, Backblaze B2, and others.

Changes:
- New provider class: classes/providers/storage/s3-compatible-provider.php
  - Provider key: s3compatible
  - Reads user-configured endpoint URL from settings
  - Uses path-style URL access (required by most S3-compatible services)
  - Supports credentials via AS3CF_S3COMPAT_ACCESS_KEY_ID /
    AS3CF_S3COMPAT_SECRET_ACCESS_KEY wp-config.php constants
  - Disables AWS-specific features (Block Public Access, Object Ownership)
- New provider SVG icons (s3compatible.svg, -link.svg, -round.svg)
- Registered provider in main plugin class with endpoint setting support
- Updated StorageProviderSubPage to show endpoint URL input for S3-compatible
- Built pro settings bundle with rollup (Svelte 4.2.19)
- Added package.json and updated rollup.config.mjs for pro-only builds
This commit is contained in:
2026-03-03 12:30:18 +01:00
commit 3248cbb029
2086 changed files with 359427 additions and 0 deletions

109
ui/pro/Nav.svelte Normal file
View File

@@ -0,0 +1,109 @@
<script>
import {link} from "svelte-spa-router";
import {bucket_writable, counts, strings, urls} from "../js/stores";
import {licence, offloadRemainingWithCount, running, tools} from "./stores";
import Nav from "../components/Nav.svelte";
import OffloadStatus from "../components/OffloadStatus.svelte";
import ToolRunningStatus from "./ToolRunningStatus.svelte";
import OffloadStatusFlyout from "../components/OffloadStatusFlyout.svelte";
import PanelRow from "../components/PanelRow.svelte";
import Button from "../components/Button.svelte";
let flyoutButton;
let expanded = false;
let hasFocus = false;
/**
* Get a message describing why the offload remaining button is disabled, if it is.
*
* @param {Object} licence
* @param {Object} counts
*
* @return {string}
*/
function getOffloadRemainingDisabledMessage( licence, counts ) {
if ( !licence.is_set ) {
return $strings.no_licence;
}
if ( counts.total < 1 ) {
return $strings.no_media;
}
if ( counts.not_offloaded < 1 ) {
return $strings.all_media_offloaded;
}
if (
licence.limit_info.counts_toward_limit &&
licence.limit_info.total > 0 &&
licence.limit_info.limit > 0 &&
licence.limit_info.total >= licence.limit_info.limit
) {
if ( licence.limit_info.total > licence.limit_info.limit ) {
return $strings.licence_limit_exceeded;
}
return $strings.licence_limit_reached;
}
if ( ! $bucket_writable ) {
return $strings.disabled_tool_bucket_access;
}
return "";
}
$: offloadRemainingDisabledMessage = getOffloadRemainingDisabledMessage( $licence, $counts );
/**
* Close the flyout panel and kick off the offloader.
*
* The panel is closed so that it does not pop back open without focus on completion.
*/
function startOffload() {
expanded = false;
tools.start( $tools.uploader );
}
</script>
<Nav>
{#if !!$running}
<ToolRunningStatus/>
{:else}
<OffloadStatus bind:flyoutButton bind:expanded bind:hasFocus>
<svelte:fragment slot="flyout">
<OffloadStatusFlyout bind:expanded bind:hasFocus bind:buttonRef={flyoutButton}>
<svelte:fragment slot="footer">
<PanelRow footer class="offload-remaining">
<Button
primary
disabled={offloadRemainingDisabledMessage}
title={offloadRemainingDisabledMessage}
on:click={startOffload}
>
{$offloadRemainingWithCount}
</Button>
</PanelRow>
<PanelRow footer class="licence">
<div class="details">
<p class="title">{$strings.plan_usage_title}</p>
<p>{$licence.plan_usage}</p>
</div>
{#if !$licence.is_set}
<a href="/license" use:link>
{$strings.activate_licence}
</a>
{:else if $licence.limit_info.limit !== 0}
<a href={$urls.licenses} target="_blank" class="upgrade">
{$strings.upgrade_plan_cta}
</a>
{/if}
</PanelRow>
</svelte:fragment>
</OffloadStatusFlyout>
</svelte:fragment>
</OffloadStatus>
{/if}
</Nav>