2022-07-07 05:19:05 +08:00
< script >
2022-10-30 16:28:14 +08:00
import { onMount } from "svelte";
2022-07-07 05:19:05 +08:00
import { slide } from "svelte/transition";
import ApiClient from "@/utils/ApiClient";
import CommonHelper from "@/utils/CommonHelper";
2022-07-19 00:44:10 +08:00
import { pageTitle } from "@/stores/app";
2022-07-07 05:19:05 +08:00
import { setErrors } from "@/stores/errors";
2022-08-21 19:30:36 +08:00
import { removeAllToasts , addWarningToast , addSuccessToast } from "@/stores/toasts";
2022-08-09 21:16:09 +08:00
import tooltip from "@/actions/tooltip";
import PageWrapper from "@/components/base/PageWrapper.svelte";
2022-07-07 05:19:05 +08:00
import Field from "@/components/base/Field.svelte";
import RedactedPasswordInput from "@/components/base/RedactedPasswordInput.svelte";
import SettingsSidebar from "@/components/settings/SettingsSidebar.svelte";
2022-07-19 00:44:10 +08:00
$pageTitle = "Files storage";
2022-08-21 19:30:36 +08:00
const testRequestKey = "s3_test_request";
2022-08-15 00:30:45 +08:00
let originalFormSettings = {} ;
let formSettings = {} ;
2022-07-07 05:19:05 +08:00
let isLoading = false;
let isSaving = false;
2022-08-21 19:30:36 +08:00
let isTesting = false;
let testS3Error = null;
let testS3TimeoutId = null;
2022-07-07 05:19:05 +08:00
2022-08-15 00:30:45 +08:00
$: initialHash = JSON.stringify(originalFormSettings);
$: hasChanges = initialHash != JSON.stringify(formSettings);
2022-07-07 05:19:05 +08:00
loadSettings();
async function loadSettings() {
isLoading = true;
try {
2022-08-02 22:00:14 +08:00
const settings = (await ApiClient.settings.getAll()) || {} ;
2022-07-07 05:19:05 +08:00
init(settings);
} catch (err) {
ApiClient.errorResponseHandler(err);
}
isLoading = false;
}
async function save() {
if (isSaving || !hasChanges) {
return;
}
isSaving = true;
try {
2022-08-21 19:30:36 +08:00
ApiClient.cancelRequest(testRequestKey);
2022-08-15 00:30:45 +08:00
const settings = await ApiClient.settings.update(CommonHelper.filterRedactedProps(formSettings));
2022-07-07 05:19:05 +08:00
setErrors({} );
2022-08-21 19:30:36 +08:00
await init(settings);
removeAllToasts();
if (testS3Error) {
addWarningToast("Successfully saved but failed to establish S3 connection.");
} else {
addSuccessToast("Successfully saved files storage settings.");
}
2022-07-07 05:19:05 +08:00
} catch (err) {
ApiClient.errorResponseHandler(err);
}
isSaving = false;
}
2022-08-21 19:30:36 +08:00
async function init(settings = {} ) {
2022-08-15 00:30:45 +08:00
formSettings = {
s3: settings?.s3 || {} ,
};
originalFormSettings = JSON.parse(JSON.stringify(formSettings));
2022-08-21 19:30:36 +08:00
await testS3();
2022-08-15 00:30:45 +08:00
}
2022-08-21 19:30:36 +08:00
async function reset() {
2022-08-15 00:30:45 +08:00
formSettings = JSON.parse(JSON.stringify(originalFormSettings || {} ));
2022-08-21 19:30:36 +08:00
await testS3();
2022-07-07 05:19:05 +08:00
}
2022-10-30 16:28:14 +08:00
async function testS3() {
testS3Error = null;
if (!formSettings.s3.enabled) {
return; // nothing to test
}
// auto cancel the test request after 30sec
ApiClient.cancelRequest(testRequestKey);
clearTimeout(testS3TimeoutId);
testS3TimeoutId = setTimeout(() => {
ApiClient.cancelRequest(testRequestKey);
addErrorToast("S3 test connection timeout.");
}, 30000);
isTesting = true;
try {
await ApiClient.settings.testS3({ $cancelKey : testRequestKey } );
} catch (err) {
testS3Error = err;
}
isTesting = false;
clearTimeout(testS3TimeoutId);
}
onMount(() => {
return () => {
clearTimeout(testS3TimeoutId);
};
});
2022-07-07 05:19:05 +08:00
< / script >
< SettingsSidebar / >
2022-08-09 21:16:09 +08:00
< PageWrapper >
2022-07-07 05:19:05 +08:00
< header class = "page-header" >
< nav class = "breadcrumbs" >
< div class = "breadcrumb-item" > Settings< / div >
2022-07-19 00:44:10 +08:00
< div class = "breadcrumb-item" > { $pageTitle } </ div >
2022-07-07 05:19:05 +08:00
< / nav >
< / header >
< div class = "wrapper" >
2022-07-12 03:23:22 +08:00
< form class = "panel" autocomplete = "off" on:submit | preventDefault = {() => save ()} >
2022-07-07 05:19:05 +08:00
< div class = "content txt-xl m-b-base" >
< p > By default PocketBase uses the local file system to store uploaded files.< / p >
< p >
If you have limited disk space, you could optionally connect to a S3 compatible storage.
< / p >
< / div >
{ #if isLoading }
< div class = "loader" / >
{ : else }
< Field class = "form-field form-field-toggle" let:uniqueId >
2022-08-15 00:30:45 +08:00
< input type = "checkbox" id = { uniqueId } required bind:checked = { formSettings . s3 . enabled } / >
2022-07-07 05:19:05 +08:00
< label for = { uniqueId } > Use S3 storage </ label >
< / Field >
2022-08-15 00:30:45 +08:00
{ #if originalFormSettings . s3 ? . enabled != formSettings . s3 . enabled }
2022-07-16 15:38:47 +08:00
< div transition:slide | local = {{ duration : 150 }} >
2022-07-12 03:23:22 +08:00
< div class = "alert alert-warning m-0" >
< div class = "icon" >
< i class = "ri-error-warning-line" / >
< / div >
< div class = "content" >
If you have existing uploaded files, you'll have to migrate them manually from
the
2022-08-15 00:30:45 +08:00
< strong >
{ originalFormSettings . s3 ? . enabled ? "S3 storage" : "local file system" }
< / strong >
2022-07-12 03:23:22 +08:00
to the
2022-08-15 00:30:45 +08:00
< strong > { formSettings . s3 . enabled ? "S3 storage" : "local file system" } < /strong
>.
2022-07-12 03:23:22 +08:00
< br / >
There are numerous command line tools that can help you, such as:
< a
href="https://github.com/rclone/rclone"
target="_blank"
2022-10-30 16:28:14 +08:00
rel="noopener noreferrer"
2022-07-12 03:23:22 +08:00
class="txt-bold"
>
rclone
< / a > ,
< a
href="https://github.com/peak/s5cmd"
target="_blank"
2022-10-30 16:28:14 +08:00
rel="noopener noreferrer"
2022-07-12 03:23:22 +08:00
class="txt-bold"
>
s5cmd
< / a > , etc.
< / div >
< / div >
< div class = "clearfix m-t-base" / >
< / div >
{ /if }
2022-08-15 00:30:45 +08:00
{ #if formSettings . s3 . enabled }
2022-07-07 05:19:05 +08:00
< div class = "grid" transition:slide | local = {{ duration : 150 }} >
2022-08-21 19:30:36 +08:00
< div class = "col-lg-6" >
2022-07-07 05:19:05 +08:00
< Field class = "form-field required" name = "s3.endpoint" let:uniqueId >
< label for = { uniqueId } > Endpoint</label >
2022-08-15 00:30:45 +08:00
< input
type="text"
id={ uniqueId }
required
bind:value={ formSettings . s3 . endpoint }
/>
2022-07-07 05:19:05 +08:00
< / Field >
< / div >
2022-08-21 19:30:36 +08:00
< div class = "col-lg-3" >
2022-07-07 05:19:05 +08:00
< Field class = "form-field required" name = "s3.bucket" let:uniqueId >
< label for = { uniqueId } > Bucket</label >
2022-08-15 00:30:45 +08:00
< input
type="text"
id={ uniqueId }
required
bind:value={ formSettings . s3 . bucket }
/>
2022-07-07 05:19:05 +08:00
< / Field >
< / div >
2022-08-21 19:30:36 +08:00
< div class = "col-lg-3" >
2022-07-07 05:19:05 +08:00
< Field class = "form-field required" name = "s3.region" let:uniqueId >
< label for = { uniqueId } > Region</label >
2022-08-15 00:30:45 +08:00
< input
type="text"
id={ uniqueId }
required
bind:value={ formSettings . s3 . region }
/>
2022-07-07 05:19:05 +08:00
< / Field >
< / div >
< div class = "col-lg-6" >
< Field class = "form-field required" name = "s3.accessKey" let:uniqueId >
< label for = { uniqueId } > Access key </ label >
2022-08-15 00:30:45 +08:00
< input
type="text"
id={ uniqueId }
required
bind:value={ formSettings . s3 . accessKey }
/>
2022-07-07 05:19:05 +08:00
< / Field >
< / div >
< div class = "col-lg-6" >
< Field class = "form-field required" name = "s3.secret" let:uniqueId >
< label for = { uniqueId } > Secret</label >
2022-08-15 00:30:45 +08:00
< RedactedPasswordInput
id={ uniqueId }
required
bind:value={ formSettings . s3 . secret }
/>
2022-07-07 05:19:05 +08:00
< / Field >
< / div >
2022-07-19 15:45:38 +08:00
< div class = "col-lg-12" >
< Field class = "form-field" name = "s3.forcePathStyle" let:uniqueId >
2022-08-15 00:30:45 +08:00
< input
type="checkbox"
id={ uniqueId }
bind:checked={ formSettings . s3 . forcePathStyle }
/>
2022-07-19 15:45:38 +08:00
< label for = { uniqueId } >
< span class = "txt" > Force path-style addressing< / span >
< i
class="ri-information-line link-hint"
use:tooltip={{
text: 'Forces the request to use path-style addressing, eg. "https://s3.amazonaws.com/BUCKET/KEY" instead of the default "https://BUCKET.s3.amazonaws.com/KEY".',
position: "top",
}}
/>
< / label >
< / Field >
< / div >
2022-07-07 05:19:05 +08:00
<!-- margin helper -->
< div class = "col-lg-12" / >
< / div >
{ /if }
< div class = "flex" >
< div class = "flex-fill" / >
2022-08-21 19:30:36 +08:00
{ #if formSettings . s3 ? . enabled && ! hasChanges && ! isSaving }
{ #if isTesting }
< span class = "loader loader-sm" / >
{ :else if testS3Error }
< div
class="label label-sm label-warning entrance-right"
use:tooltip={ testS3Error . data ? . message }
>
< i class = "ri-error-warning-line txt-warning" / >
< span class = "txt" > Failed to establish S3 connection< / span >
< / div >
{ : else }
< div class = "label label-sm label-success entrance-right" >
< i class = "ri-checkbox-circle-line txt-success" / >
< span class = "txt" > S3 connected successfully< / span >
< / div >
{ /if }
{ /if }
2022-08-15 00:30:45 +08:00
{ #if hasChanges }
< button
type="button"
2023-01-24 03:57:35 +08:00
class="btn btn-transparent btn-hint"
2022-08-15 00:30:45 +08:00
disabled={ isSaving }
on:click={() => reset ()}
>
< span class = "txt" > Cancel< / span >
< / button >
{ /if }
2022-08-21 19:30:36 +08:00
2022-07-07 05:19:05 +08:00
< button
type="submit"
class="btn btn-expanded"
class:btn-loading={ isSaving }
disabled={ ! hasChanges || isSaving }
on:click={() => save ()}
>
< span class = "txt" > Save changes< / span >
< / button >
< / div >
{ /if }
< / form >
< / div >
2022-08-09 21:16:09 +08:00
< / PageWrapper >