Files
WPS3Media/ui/components/StorageProviderSubPage.svelte

316 lines
10 KiB
Svelte
Raw Permalink Normal View History

<script>
import {createEventDispatcher, getContext, hasContext} from "svelte";
import {writable} from "svelte/store";
import {
settings,
defined_settings,
strings,
storage_providers,
storage_provider,
counts,
current_settings,
needs_refresh,
revalidatingSettings,
state
} from "../js/stores";
import {
scrollNotificationsIntoView
} from "../js/scrollNotificationsIntoView";
import {needsRefresh} from "../js/needsRefresh";
import SubPage from "./SubPage.svelte";
import Panel from "./Panel.svelte";
import PanelRow from "./PanelRow.svelte";
import TabButton from "./TabButton.svelte";
import RadioButton from "./RadioButton.svelte";
import AccessKeysDefine from "./AccessKeysDefine.svelte";
import BackNextButtonsRow from "./BackNextButtonsRow.svelte";
import KeyFileDefine from "./KeyFileDefine.svelte";
import UseServerRolesDefine from "./UseServerRolesDefine.svelte";
import AccessKeysEntry from "./AccessKeysEntry.svelte";
import KeyFileEntry from "./KeyFileEntry.svelte";
import Notification from "./Notification.svelte";
export let params = {}; // Required for regex routes.
const _params = params; // Stops compiler warning about unused params export;
const dispatch = createEventDispatcher();
// Parent page may want to be locked.
let settingsLocked = writable( false );
if ( hasContext( "settingsLocked" ) ) {
settingsLocked = getContext( "settingsLocked" );
}
// Need to be careful about throwing unneeded warnings.
let initialSettings = $current_settings;
if ( hasContext( "initialSettings" ) ) {
initialSettings = getContext( "initialSettings" );
}
// As this page does not directly alter the settings store until done,
// we need to keep track of any changes made elsewhere and prompt
// the user to refresh the page.
let saving = false;
const previousSettings = { ...$current_settings };
const previousDefines = { ...$defined_settings };
$: {
$needs_refresh = $needs_refresh || needsRefresh( saving, previousSettings, $current_settings, previousDefines, $defined_settings );
}
/*
* 1. Select Storage Provider
*/
let storageProvider = { ...$storage_provider };
$: defined = $defined_settings.includes( "provider" );
$: disabled = defined || $needs_refresh || $settingsLocked;
/**
* Handles picking different storage provider.
*
* @param {Object} provider
*/
function handleChooseProvider( provider ) {
if ( disabled ) {
return;
}
storageProvider = provider;
// Now make sure authMethod is valid for chosen storage provider.
authMethod = getAuthMethod( storageProvider, authMethod );
}
$: changedWithOffloaded = initialSettings.provider !== storageProvider.provider_key_name && $counts.offloaded > 0;
/*
* 2. Select Authentication method
*/
let endpointUrl = $settings[ "endpoint" ] || "";
let accessKeyId = $settings[ "access-key-id" ];
let secretAccessKey = $settings[ "secret-access-key" ];
let keyFile = $settings[ "key-file" ] ? JSON.stringify( $settings[ "key-file" ] ) : "";
/**
* For the given current storage provider, determine the authentication method or fallback to currently selected.
* It's possible that the storage provider can be freely changed but the
* authentication method is defined (fixed) differently for each, or freely changeable too.
* The order of evaluation in this function is important and mirrors the server side evaluation order.
*
* @param {provider} provider
* @param {string} current auth method, one of "define", "server-role" or "database" if set.
*
* @return {string}
*/
function getAuthMethod( provider, current = "" ) {
if ( provider.use_access_keys_allowed && provider.used_access_keys_constants.length ) {
return "define";
}
if ( provider.use_key_file_allowed && provider.used_key_file_path_constants.length ) {
return "define";
}
if ( provider.use_server_roles_allowed && provider.used_server_roles_constants.length ) {
return "server-role";
}
if ( current === "server-role" && !provider.use_server_roles_allowed ) {
return "define";
}
if ( current.length === 0 ) {
if ( provider.use_access_keys_allowed && (accessKeyId || secretAccessKey) ) {
return "database";
}
if ( provider.use_key_file_allowed && keyFile ) {
return "database";
}
if ( provider.use_server_roles_allowed && $settings[ "use-server-roles" ] ) {
return "server-role";
}
// Default to most secure option.
return "define";
}
return current;
}
let authMethod = getAuthMethod( storageProvider );
// If auth method is not allowed to be database, then either define or server-role is being forced, likely by a define.
$: authDefined = "database" !== getAuthMethod( storageProvider, "database" );
$: authDisabled = authDefined || $needs_refresh || $settingsLocked;
/*
* 3. Save Authentication Credentials
*/
/**
* Returns a title string to be used for the credentials panel as appropriate for the auth method.
*
* @param {string} method
* @return {*}
*/
function getCredentialsTitle( method ) {
return $strings.auth_method_title[ method ];
}
$: saveCredentialsTitle = getCredentialsTitle( authMethod );
/*
* Do Something!
*/
/**
* Handles a Next button click.
*
* @return {Promise<void>}
*/
async function handleNext() {
saving = true;
state.pausePeriodicFetch();
$settings.provider = storageProvider.provider_key_name;
$settings[ "endpoint" ] = storageProvider.provider_key_name === "s3compatible" ? endpointUrl : "";
$settings[ "access-key-id" ] = accessKeyId;
$settings[ "secret-access-key" ] = secretAccessKey;
$settings[ "use-server-roles" ] = authMethod === "server-role";
$settings[ "key-file" ] = keyFile;
const result = await settings.save();
// If something went wrong, don't move onto next step.
if ( !result.hasOwnProperty( "saved" ) || !result.saved ) {
settings.reset();
saving = false;
await state.resumePeriodicFetch();
scrollNotificationsIntoView();
return;
}
$revalidatingSettings = true;
const statePromise = state.resumePeriodicFetch();
dispatch( "routeEvent", { event: "settings.save", data: result } );
// Just make sure periodic state fetch promise is done with,
// even though we don't really care about it.
await statePromise;
$revalidatingSettings = false;
}
</script>
<SubPage name="storage-provider-settings" route="/storage/provider">
{#if changedWithOffloaded}
<Notification inline warning heading={storageProvider.media_already_offloaded_warning.heading}>
<p>{@html storageProvider.media_already_offloaded_warning.message}</p>
</Notification>
{/if}
<Panel heading={$strings.select_storage_provider_title} defined={defined} multi>
<PanelRow class="body flex-row tab-buttons">
{#each Object.values( $storage_providers ) as provider}
<TabButton
active={provider.provider_key_name === storageProvider.provider_key_name}
{disabled}
icon={provider.icon}
iconDesc={provider.icon_desc}
text={provider.provider_service_name}
on:click={() => handleChooseProvider( provider )}
/>
{/each}
<Notification class="notice-qsg">
<p>{@html storageProvider.get_access_keys_help}</p>
</Notification>
</PanelRow>
</Panel>
{#if storageProvider.provider_key_name === "s3compatible"}
<Panel heading={$strings.s3compat_endpoint_title} multi>
<PanelRow class="body flex-column">
<label class="input-label" for="s3compat-endpoint">{$strings.s3compat_endpoint_label}</label>
<input
type="url"
id="s3compat-endpoint"
name="endpoint"
placeholder={$strings.s3compat_endpoint_placeholder}
bind:value={endpointUrl}
class:disabled
{disabled}
>
<p class="desc">{$strings.s3compat_endpoint_desc}</p>
</PanelRow>
</Panel>
{/if}
<Panel heading={$strings.select_auth_method_title} defined={authDefined} multi>
<PanelRow class="body flex-column">
<!-- define -->
{#if storageProvider.use_access_keys_allowed}
<RadioButton bind:selected={authMethod} disabled={authDisabled} value="define" desc={storageProvider.defined_auth_desc}>
{$strings.define_access_keys}
</RadioButton>
{:else if storageProvider.use_key_file_allowed}
<RadioButton bind:selected={authMethod} disabled={authDisabled} value="define" desc={storageProvider.defined_auth_desc}>
{$strings.define_key_file_path}
</RadioButton>
{/if}
<!-- server-role -->
{#if storageProvider.use_server_roles_allowed}
<RadioButton bind:selected={authMethod} disabled={authDisabled} value="server-role" desc={storageProvider.defined_auth_desc}>
{storageProvider.use_server_roles_title}
</RadioButton>
{/if}
<!-- database -->
{#if storageProvider.use_access_keys_allowed}
<RadioButton bind:selected={authMethod} disabled={authDisabled} value="database">
{$strings.store_access_keys_in_db}
</RadioButton>
{:else if storageProvider.use_key_file_allowed}
<RadioButton bind:selected={authMethod} disabled={authDisabled} value="database">
{$strings.store_key_file_in_db}
</RadioButton>
{/if}
</PanelRow>
</Panel>
{#if !authDefined}
<Panel heading={saveCredentialsTitle} multi>
<PanelRow class="body flex-column access-keys">
{#if authMethod === "define" && storageProvider.use_access_keys_allowed}
<AccessKeysDefine provider={storageProvider}/>
{:else if authMethod === "define" && storageProvider.use_key_file_allowed}
<KeyFileDefine provider={storageProvider}/>
{:else if authMethod === "server-role" && storageProvider.use_server_roles_allowed}
<UseServerRolesDefine provider={storageProvider}/>
{:else if authMethod === "database" && storageProvider.use_access_keys_allowed}
<AccessKeysEntry
provider={storageProvider}
bind:accessKeyId
bind:secretAccessKey
disabled={authDisabled}
/>
{:else if authMethod === "database" && storageProvider.use_key_file_allowed}
<KeyFileEntry provider={storageProvider} bind:value={keyFile}/>
{/if}
</PanelRow>
</Panel>
{/if}
<BackNextButtonsRow on:next={handleNext} nextDisabled={$needs_refresh || $settingsLocked} nextText={$strings.save_and_continue}/>
</SubPage>