Files
WPS3Media/ui/js/routes.js
Malin 3248cbb029 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
2026-03-03 12:30:18 +01:00

117 lines
2.7 KiB
JavaScript

import {derived, writable, get} from "svelte/store";
import {wrap} from "svelte-spa-router/wrap";
/**
* Creates store of default pages.
*
* Having a title means inclusion in main tabs.
*
* @return {Object}
*/
function createPages() {
// NOTE: get() only resolves after initialization, hence arrow functions for getting titles.
const { subscribe, set, update } = writable( [] );
return {
subscribe,
set,
add( page ) {
update( $pages => {
return [...$pages, page]
.sort( ( a, b ) => {
return a.position - b.position;
} );
} );
},
withPrefix( prefix = null ) {
return get( this ).filter( ( page ) => {
return (prefix && page.route.startsWith( prefix )) || !prefix;
} );
},
routes( prefix = null ) {
let defaultComponent = null;
let defaultUserData = null;
const routes = new Map();
// If a page can be enabled/disabled, check whether it is enabled before displaying.
const conditions = [
( detail ) => {
if (
detail.hasOwnProperty( "userData" ) &&
detail.userData.hasOwnProperty( "page" ) &&
detail.userData.page.hasOwnProperty( "enabled" )
) {
return detail.userData.page.enabled();
}
return true;
}
];
for ( const page of this.withPrefix( prefix ) ) {
const userData = { page: page };
let route = page.route;
if ( prefix && route !== prefix + "/*" ) {
route = route.replace( prefix, "" );
}
routes.set( route, wrap( {
component: page.component,
userData: userData,
conditions: conditions
} ) );
if ( !defaultComponent && page.default ) {
defaultComponent = page.component;
defaultUserData = userData;
}
}
if ( defaultComponent ) {
routes.set( "*", wrap( {
component: defaultComponent,
userData: defaultUserData,
conditions: conditions
} ) );
}
return routes;
},
handleRouteEvent( detail ) {
if ( detail.hasOwnProperty( "event" ) ) {
if ( !detail.hasOwnProperty( "data" ) ) {
detail.data = {};
}
// Find the first page that wants to handle the event
// , but also let other pages see the event
// so they can set any initial state etc.
let route = false;
for ( const page of get( this ).values() ) {
if ( page.events && page.events[ detail.event ] && page.events[ detail.event ]( detail.data ) && !route ) {
route = page.route;
}
}
if ( route ) {
return route;
}
}
if ( detail.hasOwnProperty( "default" ) ) {
return detail.default;
}
return false;
}
};
}
export const pages = createPages();
// Convenience readable store of all routes.
export const routes = derived( pages, () => {
return pages.routes();
} );