316 lines
10 KiB
Svelte
316 lines
10 KiB
Svelte
|
|
<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>
|