initial public commit

This commit is contained in:
Gani Georgiev
2022-07-07 00:19:05 +03:00
commit 3d07f0211d
484 changed files with 92412 additions and 0 deletions
@@ -0,0 +1,115 @@
<script>
import { scale, slide } from "svelte/transition";
import tooltip from "@/actions/tooltip";
import { errors, removeError } from "@/stores/errors";
import CommonHelper from "@/utils/CommonHelper";
import Accordion from "@/components/base/Accordion.svelte";
import Field from "@/components/base/Field.svelte";
import RedactedPasswordInput from "@/components/base/RedactedPasswordInput.svelte";
export let key;
export let title;
export let icon = "";
export let config = {};
export let showSelfHostedFields = false;
let accordion;
$: hasErrors = !CommonHelper.isEmpty(CommonHelper.getNestedVal($errors, key));
$: if (!config.enabled) {
removeError(key);
}
export function expand() {
accordion?.expand();
}
export function collapse() {
accordion?.collapse();
}
</script>
<Accordion bind:this={accordion} on:expand on:collapse on:toggle {...$$restProps}>
<svelte:fragment slot="header">
<div class="inline-flex">
{#if icon}
<i class={icon} />
{/if}
<span class="txt">{title}</span>
</div>
{#if config.enabled}
<span class="label label-success">Enabled</span>
{:else}
<span class="label label-hint">Disabled</span>
{/if}
<div class="flex-fill" />
{#if hasErrors}
<i
class="ri-error-warning-fill txt-danger"
transition:scale={{ duration: 150, start: 0.7 }}
use:tooltip={{ text: "Has errors", position: "left" }}
/>
{/if}
</svelte:fragment>
<Field class="form-field form-field-toggle m-b-0" name="{key}.enabled" let:uniqueId>
<input type="checkbox" id={uniqueId} bind:checked={config.enabled} />
<label for={uniqueId}>Enable</label>
</Field>
{#if config.enabled}
<div class="grid" transition:slide|local={{ duration: 200 }}>
<div class="col-12 spacing" />
<div class="col-lg-6">
<Field class="form-field required" name="{key}.clientId" let:uniqueId>
<label for={uniqueId}>Client ID</label>
<input type="text" id={uniqueId} bind:value={config.clientId} required />
</Field>
</div>
<div class="col-lg-6">
<Field class="form-field required" name="{key}.clientSecret" let:uniqueId>
<label for={uniqueId}>Client Secret</label>
<RedactedPasswordInput bind:value={config.clientSecret} id={uniqueId} required />
</Field>
</div>
<div class="col-lg-12">
<Field class="form-field" name="{key}.allowRegistrations" let:uniqueId>
<input type="checkbox" id={uniqueId} bind:checked={config.allowRegistrations} />
<label for={uniqueId}>Allow registration for new users</label>
</Field>
</div>
{#if showSelfHostedFields}
<div class="col-lg-12">
<div class="section-title">Optional endpoints (if you self host the OAUTH2 service)</div>
<div class="grid">
<div class="col-lg-4">
<Field class="form-field" name="{key}.authUrl" let:uniqueId>
<label for={uniqueId}>Custom Auth URL</label>
<input type="url" id={uniqueId} bind:value={config.authUrl} />
</Field>
</div>
<div class="col-lg-4">
<Field class="form-field" name="{key}.tokenUrl" let:uniqueId>
<label for={uniqueId}>Custom Token URL</label>
<input type="text" id={uniqueId} bind:value={config.tokenUrl} />
</Field>
</div>
<div class="col-lg-4">
<Field class="form-field" name="{key}.userApiUrl" let:uniqueId>
<label for={uniqueId}>Custom User API URL</label>
<input type="text" id={uniqueId} bind:value={config.userApiUrl} />
</Field>
</div>
</div>
</div>
{/if}
</div>
{/if}
</Accordion>
@@ -0,0 +1,123 @@
<script>
import { scale, slide } from "svelte/transition";
import tooltip from "@/actions/tooltip";
import { errors } from "@/stores/errors";
import CommonHelper from "@/utils/CommonHelper";
import Accordion from "@/components/base/Accordion.svelte";
import Field from "@/components/base/Field.svelte";
import MultipleValueInput from "@/components/base/MultipleValueInput.svelte";
export let config = {}; // EmailAuthConfig
let accordion;
$: hasErrors = !CommonHelper.isEmpty($errors?.emailPassword);
export function expand() {
accordion?.expand();
}
export function collapse() {
accordion?.collapse();
}
export function collapseSiblings() {
accordion?.collapseSiblings();
}
</script>
<Accordion bind:this={accordion} on:expand on:collapse on:toggle {...$$restProps}>
<svelte:fragment slot="header">
<div class="inline-flex">
<i class="ri-mail-lock-line" />
<span class="txt">Email/Password</span>
</div>
{#if config.enabled}
<span class="label label-success">Enabled</span>
{:else}
<span class="label">Disabled</span>
{/if}
<div class="flex-fill" />
{#if hasErrors}
<i
class="ri-error-warning-fill txt-danger"
transition:scale={{ duration: 150, start: 0.7 }}
use:tooltip={{ text: "Has errors", position: "left" }}
/>
{/if}
</svelte:fragment>
<Field class="form-field form-field-toggle m-b-0" name="emailPassword.enabled" let:uniqueId>
<input type="checkbox" id={uniqueId} bind:checked={config.enabled} />
<label for={uniqueId}>Enable</label>
</Field>
{#if config.enabled}
<div class="grid" transition:slide|local={{ duration: 150 }}>
<div class="col-sm-12 m-t-sm">
<Field class="form-field required" name="emailPassword.minPasswordLength" let:uniqueId>
<label for={uniqueId}>Minimum password length</label>
<input
type="number"
id={uniqueId}
required
min="5"
max="200"
bind:value={config.minPasswordLength}
/>
</Field>
</div>
<div class="col-lg-6">
<Field
class="form-field {!CommonHelper.isEmpty(config.onlyDomains) ? 'disabled' : ''}"
name="emailPassword.exceptDomains"
let:uniqueId
>
<label for={uniqueId}>
<span class="txt">Except domains</span>
<i
class="ri-information-line link-hint"
use:tooltip={{
text: 'Email domains that are NOT allowed to sign up. \n This field is disabled if "Only domains" is set.',
position: "top",
}}
/>
</label>
<MultipleValueInput
id={uniqueId}
disabled={!CommonHelper.isEmpty(config.onlyDomains)}
bind:value={config.exceptDomains}
/>
<div class="help-block">Use comma as separator.</div>
</Field>
</div>
<div class="col-lg-6">
<Field
class="form-field {!CommonHelper.isEmpty(config.exceptDomains) ? 'disabled' : ''}"
name="emailPassword.onlyDomains"
let:uniqueId
>
<label for="{uniqueId}.config.onlyDomains">
<span class="txt">Only domains</span>
<i
class="ri-information-line link-hint"
use:tooltip={{
text: 'Email domains that are ONLY allowed to sign up. \n This field is disabled if "Except domains" is set.',
position: "top",
}}
/>
</label>
<MultipleValueInput
id="{uniqueId}.config.onlyDomains"
disabled={!CommonHelper.isEmpty(config.exceptDomains)}
bind:value={config.onlyDomains}
/>
<div class="help-block">Use comma as separator.</div>
</Field>
</div>
</div>
{/if}
</Accordion>
@@ -0,0 +1,115 @@
<script>
import ApiClient from "@/utils/ApiClient";
import CommonHelper from "@/utils/CommonHelper";
import { addSuccessToast } from "@/stores/toasts";
import Field from "@/components/base/Field.svelte";
import SettingsSidebar from "@/components/settings/SettingsSidebar.svelte";
let formSettings = {};
let isLoading = false;
let isSaving = false;
let initialHash = "";
$: hasChanges = initialHash != JSON.stringify(formSettings);
CommonHelper.setDocumentTitle("Application settings");
loadSettings();
async function loadSettings() {
isLoading = true;
try {
const settings = (await ApiClient.Settings.getAll()) || {};
init(settings);
} catch (err) {
ApiClient.errorResponseHandler(err);
}
isLoading = false;
}
async function save() {
if (isSaving || !hasChanges) {
return;
}
isSaving = true;
try {
const settings = await ApiClient.Settings.update(CommonHelper.filterRedactedProps(formSettings));
init(settings);
addSuccessToast("Successfully saved application settings.");
} catch (err) {
ApiClient.errorResponseHandler(err);
}
isSaving = false;
}
function init(settings = {}) {
formSettings = {
meta: settings?.meta || {},
logs: settings?.logs || {},
};
initialHash = JSON.stringify(formSettings);
}
</script>
<SettingsSidebar />
<main class="page-wrapper">
<header class="page-header">
<nav class="breadcrumbs">
<div class="breadcrumb-item">Settings</div>
<div class="breadcrumb-item">Application</div>
</nav>
</header>
<div class="wrapper">
<form class="panel" autocomplete="off" on:submit|preventDefault={save}>
{#if isLoading}
<div class="loader" />
{:else}
<div class="grid">
<div class="col-lg-6">
<Field class="form-field required" name="meta.appName" let:uniqueId>
<label for={uniqueId}>Application name</label>
<input
type="text"
id={uniqueId}
required
bind:value={formSettings.meta.appName}
/>
</Field>
</div>
<div class="col-lg-6">
<Field class="form-field required" name="meta.appUrl" let:uniqueId>
<label for={uniqueId}>Application url</label>
<input type="text" id={uniqueId} required bind:value={formSettings.meta.appUrl} />
</Field>
</div>
<Field class="form-field required" name="logs.maxDays" let:uniqueId>
<label for={uniqueId}>Logs max days retention</label>
<input type="number" id={uniqueId} required bind:value={formSettings.logs.maxDays} />
</Field>
<div class="col-lg-12 flex">
<div class="flex-fill" />
<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>
</div>
{/if}
</form>
</div>
</main>
@@ -0,0 +1,142 @@
<script>
import ApiClient from "@/utils/ApiClient";
import CommonHelper from "@/utils/CommonHelper";
import { setErrors } from "@/stores/errors";
import { addSuccessToast } from "@/stores/toasts";
import SettingsSidebar from "@/components/settings/SettingsSidebar.svelte";
import EmailAuthAccordion from "@/components/settings/EmailAuthAccordion.svelte";
import AuthProviderAccordion from "@/components/settings/AuthProviderAccordion.svelte";
let emailAuthAccordion;
let authSettings = {};
let isLoading = false;
let isSaving = false;
let initialHash = "";
$: hasChanges = initialHash != JSON.stringify(authSettings);
CommonHelper.setDocumentTitle("Auth providers");
loadSettings();
async function loadSettings() {
isLoading = true;
try {
const result = (await ApiClient.Settings.getAll()) || {};
initSettings(result);
} catch (err) {
ApiClient.errorResponseHandler(err);
}
isLoading = false;
}
async function save() {
if (isSaving || !hasChanges) {
return;
}
isSaving = true;
try {
const result = await ApiClient.Settings.update(CommonHelper.filterRedactedProps(authSettings));
initSettings(result);
setErrors({});
emailAuthAccordion?.collapseSiblings();
addSuccessToast("Successfully updated auth providers.");
} catch (err) {
ApiClient.errorResponseHandler(err);
}
isSaving = false;
}
function initSettings(data) {
data = data || {};
authSettings = {};
authSettings.emailAuth = Object.assign({ enabled: true }, data.emailAuth);
const providers = ["googleAuth", "facebookAuth", "githubAuth", "gitlabAuth"];
for (const provider of providers) {
authSettings[provider] = Object.assign(
{ enabled: false, allowRegistrations: true },
data[provider]
);
}
initialHash = JSON.stringify(authSettings);
}
</script>
<SettingsSidebar />
<main class="page-wrapper">
<header class="page-header">
<nav class="breadcrumbs">
<div class="breadcrumb-item">Settings</div>
<div class="breadcrumb-item">Auth providers</div>
</nav>
</header>
<div class="wrapper">
<form class="panel" autocomplete="off" on:submit|preventDefault={save}>
<h6 class="m-b-base">Manage the allowed users sign-in/sign-up methods.</h6>
{#if isLoading}
<div class="loader" />
{:else}
<div class="accordions">
<EmailAuthAccordion
bind:this={emailAuthAccordion}
single
bind:config={authSettings.emailAuth}
/>
<AuthProviderAccordion
single
key="googleAuth"
title="Google"
icon="ri-google-line"
bind:config={authSettings.googleAuth}
/>
<AuthProviderAccordion
single
key="facebookAuth"
title="Facebook"
icon="ri-facebook-line"
bind:config={authSettings.facebookAuth}
/>
<AuthProviderAccordion
single
key="githubAuth"
title="GitHub"
icon="ri-github-line"
bind:config={authSettings.githubAuth}
/>
<AuthProviderAccordion
single
key="gitlabAuth"
title="GitLab"
icon="ri-gitlab-line"
showSelfHostedFields
bind:config={authSettings.gitlabAuth}
/>
</div>
<div class="flex m-t-base">
<div class="flex-fill" />
<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>
</main>
+242
View File
@@ -0,0 +1,242 @@
<script>
import { slide } from "svelte/transition";
import ApiClient from "@/utils/ApiClient";
import CommonHelper from "@/utils/CommonHelper";
import { addSuccessToast } from "@/stores/toasts";
import Field from "@/components/base/Field.svelte";
import ObjectSelect from "@/components/base/ObjectSelect.svelte";
import RedactedPasswordInput from "@/components/base/RedactedPasswordInput.svelte";
import SettingsSidebar from "@/components/settings/SettingsSidebar.svelte";
const tlsOptions = [
{ label: "Optional (StartTLS)", value: false },
{ label: "Always", value: true },
];
let formSettings = {};
let isLoading = false;
let isSaving = false;
let initialHash = "";
$: hasChanges = initialHash != JSON.stringify(formSettings);
CommonHelper.setDocumentTitle("Mail settings");
loadSettings();
async function loadSettings() {
isLoading = true;
try {
const settings = (await ApiClient.Settings.getAll()) || {};
init(settings);
} catch (err) {
ApiClient.errorResponseHandler(err);
}
isLoading = false;
}
async function save() {
if (isSaving || !hasChanges) {
return;
}
isSaving = true;
try {
const settings = await ApiClient.Settings.update(CommonHelper.filterRedactedProps(formSettings));
init(settings);
addSuccessToast("Successfully saved mail settings.");
} catch (err) {
ApiClient.errorResponseHandler(err);
}
isSaving = false;
}
function init(settings = {}) {
formSettings = {
meta: settings?.meta || {},
smtp: settings?.smtp || {},
};
initialHash = JSON.stringify(formSettings);
}
</script>
<SettingsSidebar />
<main class="page-wrapper">
<header class="page-header">
<nav class="breadcrumbs">
<div class="breadcrumb-item">Settings</div>
<div class="breadcrumb-item">Mail settings</div>
</nav>
</header>
<div class="wrapper">
<form class="panel" autocomplete="off" on:submit|preventDefault={() => save()}>
<div class="content txt-xl m-b-base">
<p>Configure common settings for sending emails.</p>
</div>
{#if isLoading}
<div class="loader" />
{:else}
<div class="grid">
<div class="col-lg-6">
<Field class="form-field required" name="meta.senderName" let:uniqueId>
<label for={uniqueId}>Sender name</label>
<input
type="text"
id={uniqueId}
required
bind:value={formSettings.meta.senderName}
/>
</Field>
</div>
<div class="col-lg-6">
<Field class="form-field required" name="meta.senderAddress" let:uniqueId>
<label for={uniqueId}>Sender address</label>
<input
type="email"
id={uniqueId}
required
bind:value={formSettings.meta.senderAddress}
/>
</Field>
</div>
<Field class="form-field required" name="meta.userVerificationUrl" let:uniqueId>
<label for={uniqueId}>User verification page url</label>
<input
type="text"
id={uniqueId}
required
bind:value={formSettings.meta.userVerificationUrl}
/>
<div class="help-block">
Used in the user verification email. Available placeholder parameters:
<code>%APP_URL%</code>, <code>%TOKEN%</code>.
</div>
</Field>
<Field class="form-field required" name="meta.userResetPasswordUrl" let:uniqueId>
<label for={uniqueId}>User reset password page url</label>
<input
type="text"
id={uniqueId}
required
bind:value={formSettings.meta.userResetPasswordUrl}
/>
<div class="help-block">
Used in the user password reset email. Available placeholder parameters:
<code>%APP_URL%</code>, <code>%TOKEN%</code>.
</div>
</Field>
<Field class="form-field required" name="meta.userConfirmEmailChangeUrl" let:uniqueId>
<label for={uniqueId}>User confirm email change page url</label>
<input
type="text"
id={uniqueId}
required
bind:value={formSettings.meta.userConfirmEmailChangeUrl}
/>
<div class="help-block">
Used in the user email change confirmation email. Available placeholder
parameters:
<code>%APP_URL%</code>, <code>%TOKEN%</code>.
</div>
</Field>
</div>
<hr />
<div class="content m-b-sm">
<p>
By default PocketBase uses the OS <code>sendmail</code> command for sending emails.
<br />
<strong class="txt-bold">
For better emails deliverability it is recommended to enable the SMTP settings
below.
</strong>
</p>
</div>
<Field class="form-field form-field-toggle" let:uniqueId>
<input type="checkbox" id={uniqueId} required bind:checked={formSettings.smtp.enabled} />
<label for={uniqueId}>Use SMTP mail server</label>
</Field>
{#if formSettings.smtp.enabled}
<div class="grid" transition:slide|local={{ duration: 150 }}>
<div class="col-lg-6">
<Field class="form-field required" name="smtp.host" let:uniqueId>
<label for={uniqueId}>SMTP server host</label>
<input
type="text"
id={uniqueId}
required
bind:value={formSettings.smtp.host}
/>
</Field>
</div>
<div class="col-lg-3">
<Field class="form-field required" name="smtp.port" let:uniqueId>
<label for={uniqueId}>Port</label>
<input
type="number"
id={uniqueId}
required
bind:value={formSettings.smtp.port}
/>
</Field>
</div>
<div class="col-lg-3">
<Field class="form-field required" name="smtp.tls" let:uniqueId>
<label for={uniqueId}>TLS Encryption</label>
<ObjectSelect
id={uniqueId}
items={tlsOptions}
bind:keyOfSelected={formSettings.smtp.tls}
/>
</Field>
</div>
<div class="col-lg-6">
<Field class="form-field" name="smtp.username" let:uniqueId>
<label for={uniqueId}>Username</label>
<input type="text" id={uniqueId} bind:value={formSettings.smtp.username} />
</Field>
</div>
<div class="col-lg-6">
<Field class="form-field" name="smtp.password" let:uniqueId>
<label for={uniqueId}>Password</label>
<RedactedPasswordInput
id={uniqueId}
bind:value={formSettings.smtp.password}
/>
</Field>
</div>
<!-- margin helper -->
<div class="col-lg-12" />
</div>
{/if}
<div class="flex">
<div class="flex-fill" />
<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>
</main>
@@ -0,0 +1,139 @@
<script>
import { slide } from "svelte/transition";
import ApiClient from "@/utils/ApiClient";
import CommonHelper from "@/utils/CommonHelper";
import { setErrors } from "@/stores/errors";
import { addSuccessToast } from "@/stores/toasts";
import Field from "@/components/base/Field.svelte";
import RedactedPasswordInput from "@/components/base/RedactedPasswordInput.svelte";
import SettingsSidebar from "@/components/settings/SettingsSidebar.svelte";
let s3 = {};
let isLoading = false;
let isSaving = false;
let initialHash = "";
$: hasChanges = initialHash != JSON.stringify(s3);
CommonHelper.setDocumentTitle("Files storage");
loadSettings();
async function loadSettings() {
isLoading = true;
try {
const settings = (await ApiClient.Settings.getAll()) || {};
init(settings);
} catch (err) {
ApiClient.errorResponseHandler(err);
}
isLoading = false;
}
async function save() {
if (isSaving || !hasChanges) {
return;
}
isSaving = true;
try {
const settings = await ApiClient.Settings.update(CommonHelper.filterRedactedProps({ s3 }));
init(settings);
setErrors({});
addSuccessToast("Successfully saved Files storage settings.");
} catch (err) {
ApiClient.errorResponseHandler(err);
}
isSaving = false;
}
function init(settings = {}) {
s3 = settings?.s3 || {};
initialHash = JSON.stringify(s3);
}
</script>
<SettingsSidebar />
<main class="page-wrapper">
<header class="page-header">
<nav class="breadcrumbs">
<div class="breadcrumb-item">Settings</div>
<div class="breadcrumb-item">Files storage</div>
</nav>
</header>
<div class="wrapper">
<form class="panel" autocomplete="off" on:submit|preventDefault={save}>
<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>
<input type="checkbox" id={uniqueId} required bind:checked={s3.enabled} />
<label for={uniqueId}>Use S3 storage</label>
</Field>
{#if s3.enabled}
<div class="grid" transition:slide|local={{ duration: 150 }}>
<div class="col-lg-12">
<Field class="form-field required" name="s3.endpoint" let:uniqueId>
<label for={uniqueId}>Endpoint</label>
<input type="text" id={uniqueId} required bind:value={s3.endpoint} />
</Field>
</div>
<div class="col-lg-6">
<Field class="form-field required" name="s3.bucket" let:uniqueId>
<label for={uniqueId}>Bucket</label>
<input type="text" id={uniqueId} required bind:value={s3.bucket} />
</Field>
</div>
<div class="col-lg-6">
<Field class="form-field required" name="s3.region" let:uniqueId>
<label for={uniqueId}>Region</label>
<input type="text" id={uniqueId} required bind:value={s3.region} />
</Field>
</div>
<div class="col-lg-6">
<Field class="form-field required" name="s3.accessKey" let:uniqueId>
<label for={uniqueId}>Access key</label>
<input type="text" id={uniqueId} required bind:value={s3.accessKey} />
</Field>
</div>
<div class="col-lg-6">
<Field class="form-field required" name="s3.secret" let:uniqueId>
<label for={uniqueId}>Secret</label>
<RedactedPasswordInput id={uniqueId} required bind:value={s3.secret} />
</Field>
</div>
<!-- margin helper -->
<div class="col-lg-12" />
</div>
{/if}
<div class="flex">
<div class="flex-fill" />
<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>
</main>
@@ -0,0 +1,136 @@
<script>
import ApiClient from "@/utils/ApiClient";
import CommonHelper from "@/utils/CommonHelper";
import { addSuccessToast } from "@/stores/toasts";
import Field from "@/components/base/Field.svelte";
import SettingsSidebar from "@/components/settings/SettingsSidebar.svelte";
const tokensList = [
{ key: "userAuthToken", label: "Users auth token" },
{ key: "userVerificationToken", label: "Users email verification token" },
{ key: "userPasswordResetToken", label: "Users password reset token" },
{ key: "userEmailChangeToken", label: "Users email change token" },
{ key: "adminAuthToken", label: "Admins auth token" },
{ key: "adminPasswordResetToken", label: "Admins password reset token" },
];
let tokenSettings = {};
let isLoading = false;
let isSaving = false;
let initialHash = "";
$: hasChanges = initialHash != JSON.stringify(tokenSettings);
CommonHelper.setDocumentTitle("Token options");
loadSettings();
async function loadSettings() {
isLoading = true;
try {
const result = (await ApiClient.Settings.getAll()) || {};
initSettings(result);
} catch (err) {
ApiClient.errorResponseHandler(err);
}
isLoading = false;
}
async function save() {
if (isSaving || !hasChanges) {
return;
}
isSaving = true;
try {
const result = await ApiClient.Settings.update(CommonHelper.filterRedactedProps(tokenSettings));
initSettings(result);
addSuccessToast("Successfully saved tokens options.");
} catch (err) {
ApiClient.errorResponseHandler(err);
}
isSaving = false;
}
function initSettings(data) {
data = data || {};
tokenSettings = {};
for (const listItem of tokensList) {
tokenSettings[listItem.key] = {
duration: data[listItem.key]?.duration || 0,
};
}
initialHash = JSON.stringify(tokenSettings);
}
</script>
<SettingsSidebar />
<main class="page-wrapper">
<header class="page-header">
<nav class="breadcrumbs">
<div class="breadcrumb-item">Settings</div>
<div class="breadcrumb-item">Token options</div>
</nav>
</header>
<div class="wrapper">
<form class="panel" autocomplete="off" on:submit|preventDefault={save}>
<div class="content m-b-sm txt-xl">
<p>Adjust common token options.</p>
</div>
{#if isLoading}
<div class="loader" />
{:else}
{#each tokensList as token (token.key)}
<Field class="form-field required" name="{token.key}.duration" let:uniqueId>
<label for={uniqueId}>{token.label} duration (in seconds)</label>
<input
type="number"
id={uniqueId}
required
bind:value={tokenSettings[token.key].duration}
/>
<div class="help-block">
<span
class="link-primary"
class:txt-success={tokenSettings[token.key].secret}
on:click={() => {
// toggle
if (tokenSettings[token.key].secret) {
delete tokenSettings[token.key].secret;
tokenSettings[token.key] = tokenSettings[token.key];
} else {
tokenSettings[token.key].secret = CommonHelper.randomString(50);
}
}}
>
Invalidate all previosly issued tokens
</span>
</div>
</Field>
{/each}
<div class="flex">
<div class="flex-fill" />
<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>
</main>
@@ -0,0 +1,61 @@
<script>
import { link } from "svelte-spa-router";
import active from "svelte-spa-router/active";
</script>
<aside class="page-sidebar settings-sidebar">
<div class="sidebar-content">
<div class="sidebar-title">System</div>
<a href="/settings" class="sidebar-list-item" use:active={{ path: "/settings" }} use:link>
<i class="ri-home-gear-line" />
<span class="txt">Application</span>
</a>
<a
href="/settings/mail"
class="sidebar-list-item"
use:active={{ path: "/settings/mail/?.*" }}
use:link
>
<i class="ri-send-plane-2-line" />
<span class="txt">Mail settings</span>
</a>
<a
href="/settings/storage"
class="sidebar-list-item"
use:active={{ path: "/settings/storage/?.*" }}
use:link
>
<i class="ri-archive-drawer-line" />
<span class="txt">Files storage</span>
</a>
<div class="sidebar-title">Authentication</div>
<a
href="/settings/auth-providers"
class="sidebar-list-item"
use:active={{ path: "/settings/auth-providers/?.*" }}
use:link
>
<i class="ri-lock-password-line" />
<span class="txt">Auth providers</span>
</a>
<a
href="/settings/tokens"
class="sidebar-list-item"
use:active={{ path: "/settings/tokens/?.*" }}
use:link
>
<i class="ri-key-line" />
<span class="txt">Token options</span>
</a>
<a
href="/settings/admins"
class="sidebar-list-item"
use:active={{ path: "/settings/admins/?.*" }}
use:link
>
<i class="ri-shield-user-line" />
<span class="txt">Admins</span>
</a>
</div>
</aside>