SMTP and template management E2E
This commit is contained in:
parent
52d87c8267
commit
7588030780
|
@ -12,55 +12,47 @@
|
|||
let modal
|
||||
</script>
|
||||
|
||||
{#if $auth}
|
||||
{#if $auth.user}
|
||||
<div class="root">
|
||||
<div class="ui-nav">
|
||||
<div class="home-logo">
|
||||
<img src={Logo} alt="Budibase icon" />
|
||||
</div>
|
||||
<div class="nav-section">
|
||||
<div class="nav-top">
|
||||
<Navigation>
|
||||
<Item href="/builder/" icon="Apps" selected>Apps</Item>
|
||||
<Item external href="https://portal.budi.live/" icon="Servers">
|
||||
Hosting
|
||||
</Item>
|
||||
<Item external href="https://docs.budibase.com/" icon="Book">
|
||||
Documentation
|
||||
</Item>
|
||||
<Item
|
||||
external
|
||||
href="https://github.com/Budibase/budibase/discussions"
|
||||
icon="PeopleGroup"
|
||||
>
|
||||
Community
|
||||
</Item>
|
||||
<Item
|
||||
external
|
||||
href="https://github.com/Budibase/budibase/issues/new/choose"
|
||||
icon="Bug"
|
||||
>
|
||||
Raise an issue
|
||||
</Item>
|
||||
</Navigation>
|
||||
</div>
|
||||
<div class="nav-bottom">
|
||||
<BuilderSettingsButton />
|
||||
<LogoutButton />
|
||||
</div>
|
||||
</div>
|
||||
<div class="root">
|
||||
<div class="ui-nav">
|
||||
<div class="home-logo">
|
||||
<img src={Logo} alt="Budibase icon" />
|
||||
</div>
|
||||
<div class="nav-section">
|
||||
<div class="nav-top">
|
||||
<Navigation>
|
||||
<Item href="/builder/" icon="Apps" selected>Apps</Item>
|
||||
<Item external href="https://portal.budi.live/" icon="Servers">
|
||||
Hosting
|
||||
</Item>
|
||||
<Item external href="https://docs.budibase.com/" icon="Book">
|
||||
Documentation
|
||||
</Item>
|
||||
<Item
|
||||
external
|
||||
href="https://github.com/Budibase/budibase/discussions"
|
||||
icon="PeopleGroup"
|
||||
>
|
||||
Community
|
||||
</Item>
|
||||
<Item
|
||||
external
|
||||
href="https://github.com/Budibase/budibase/issues/new/choose"
|
||||
icon="Bug"
|
||||
>
|
||||
Raise an issue
|
||||
</Item>
|
||||
</Navigation>
|
||||
</div>
|
||||
<div class="main">
|
||||
<slot />
|
||||
<div class="nav-bottom">
|
||||
<BuilderSettingsButton />
|
||||
<LogoutButton />
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<section class="login">
|
||||
<LoginForm />
|
||||
</section>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
<div class="main">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
|
|
|
@ -14,8 +14,10 @@
|
|||
SideNavigation as Navigation,
|
||||
SideNavigationItem as Item,
|
||||
} from "@budibase/bbui"
|
||||
import LoginForm from "components/login/LoginForm.svelte"
|
||||
import api from "builderStore/api"
|
||||
import ConfigChecklist from "components/common/ConfigChecklist.svelte"
|
||||
import { auth } from "stores/backend"
|
||||
import { organisation, admin } from "stores/portal"
|
||||
|
||||
organisation.init()
|
||||
|
@ -47,43 +49,52 @@
|
|||
]
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<div class="nav">
|
||||
<Layout paddingX="L" paddingY="L">
|
||||
<div class="branding">
|
||||
<div class="name">
|
||||
<img
|
||||
src={$organisation?.logoUrl || "https://i.imgur.com/ZKyklgF.png"}
|
||||
alt="Logotype"
|
||||
/>
|
||||
<span>{$organisation?.company || "Budibase"}</span>
|
||||
{#if $auth}
|
||||
{#if $auth.user}
|
||||
<div class="container">
|
||||
<div class="nav">
|
||||
<Layout paddingX="L" paddingY="L">
|
||||
<div class="branding">
|
||||
<div class="name">
|
||||
<img
|
||||
src={$organisation?.logoUrl ||
|
||||
"https://i.imgur.com/ZKyklgF.png"}
|
||||
alt="Logotype"
|
||||
/>
|
||||
<span>{$organisation?.company || "Budibase"}</span>
|
||||
</div>
|
||||
<div class="onboarding">
|
||||
<ConfigChecklist />
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu">
|
||||
<Navigation>
|
||||
{#each menu as { title, href, heading }}
|
||||
<Item selected={$isActive(href)} {href} {heading}>{title}</Item>
|
||||
{/each}
|
||||
</Navigation>
|
||||
</div>
|
||||
</Layout>
|
||||
</div>
|
||||
<div class="main">
|
||||
<div class="toolbar">
|
||||
<Search placeholder="Global search" />
|
||||
<div class="avatar">
|
||||
<Avatar size="M" name="John Doe" />
|
||||
<Icon size="XL" name="ChevronDown" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="onboarding">
|
||||
<ConfigChecklist />
|
||||
<div class="content">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu">
|
||||
<Navigation>
|
||||
{#each menu as { title, href, heading }}
|
||||
<Item selected={$isActive(href)} {href} {heading}>{title}</Item>
|
||||
{/each}
|
||||
</Navigation>
|
||||
</div>
|
||||
</Layout>
|
||||
</div>
|
||||
<div class="main">
|
||||
<div class="toolbar">
|
||||
<Search placeholder="Global search" />
|
||||
<div class="avatar">
|
||||
<Avatar size="M" name="John Doe" />
|
||||
<Icon size="XL" name="ChevronDown" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<section class="login">
|
||||
<LoginForm />
|
||||
</section>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.container {
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
<script>
|
||||
import {
|
||||
Button,
|
||||
Heading,
|
||||
Divider,
|
||||
Label,
|
||||
notifications,
|
||||
Layout,
|
||||
Input,
|
||||
TextArea,
|
||||
Body,
|
||||
Page,
|
||||
Select,
|
||||
} from "@budibase/bbui"
|
||||
import { onMount } from "svelte"
|
||||
import api from "builderStore/api"
|
||||
|
||||
const ConfigTypes = {
|
||||
SMTP: "smtp",
|
||||
}
|
||||
|
||||
let smtpConfig
|
||||
let templateIdx = 0
|
||||
let templateDefinition
|
||||
let templates = []
|
||||
|
||||
$: templateTypes = templates.map((template, idx) => ({
|
||||
label: template.purpose,
|
||||
value: idx,
|
||||
}))
|
||||
|
||||
$: selectedTemplate = templates[templateIdx]
|
||||
|
||||
async function saveSmtp() {
|
||||
try {
|
||||
// Save your SMTP config
|
||||
const response = await api.post(`/api/admin/configs`, smtpConfig)
|
||||
const json = await response.json()
|
||||
if (response.status !== 200) throw new Error(json.message)
|
||||
smtpConfig._rev = json._rev
|
||||
smtpConfig._id = json._id
|
||||
|
||||
notifications.success(`Settings saved.`)
|
||||
} catch (err) {
|
||||
notifications.error(`Failed to save email settings. ${err}`)
|
||||
}
|
||||
}
|
||||
|
||||
async function saveTemplate() {
|
||||
try {
|
||||
// Save your SMTP config
|
||||
const response = await api.post(`/api/admin/template`, selectedTemplate)
|
||||
const json = await response.json()
|
||||
if (response.status !== 200) throw new Error(json.message)
|
||||
selectedTemplate._rev = json._rev
|
||||
selectedTemplate._id = json._id
|
||||
|
||||
notifications.success(`Template saved.`)
|
||||
} catch (err) {
|
||||
notifications.error(`Failed to update template settings. ${err}`)
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchSmtp() {
|
||||
// fetch the configs for smtp
|
||||
const smtpResponse = await api.get(`/api/admin/configs/${ConfigTypes.SMTP}`)
|
||||
const smtpDoc = await smtpResponse.json()
|
||||
|
||||
if (!smtpDoc._id) {
|
||||
smtpConfig = {
|
||||
type: ConfigTypes.SMTP,
|
||||
config: {
|
||||
auth: {
|
||||
type: "login",
|
||||
},
|
||||
},
|
||||
}
|
||||
} else {
|
||||
smtpConfig = smtpDoc
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchTemplates() {
|
||||
// fetch the email template definitions
|
||||
const templatesResponse = await api.get(`/api/admin/template/definitions`)
|
||||
const templateDefDoc = await templatesResponse.json()
|
||||
|
||||
// fetch the email templates themselves
|
||||
const emailTemplatesResponse = await api.get(`/api/admin/template/email`)
|
||||
const emailTemplates = await emailTemplatesResponse.json()
|
||||
|
||||
templateDefinition = templateDefDoc
|
||||
templates = emailTemplates
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
await fetchSmtp()
|
||||
await fetchTemplates()
|
||||
})
|
||||
</script>
|
||||
|
||||
<Page>
|
||||
<header>
|
||||
<Heading size="M">Email</Heading>
|
||||
<Body size="S">
|
||||
Sending email is not required, but highly recommended for processes such
|
||||
as password recovery. To setup automated auth emails, simply add the
|
||||
values below and click activate.
|
||||
</Body>
|
||||
</header>
|
||||
<Divider />
|
||||
{#if smtpConfig}
|
||||
<div class="config-form">
|
||||
<Heading size="S">SMTP</Heading>
|
||||
<Body size="S">
|
||||
To allow your app to benefit from automated auth emails, add your SMTP
|
||||
details below.
|
||||
</Body>
|
||||
<Layout gap="S">
|
||||
<Heading size="S">
|
||||
<span />
|
||||
</Heading>
|
||||
<div class="form-row">
|
||||
<Label>Host</Label>
|
||||
<Input bind:value={smtpConfig.config.host} />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<Label>Port</Label>
|
||||
<Input type="number" bind:value={smtpConfig.config.port} />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<Label>User</Label>
|
||||
<Input bind:value={smtpConfig.config.auth.user} />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<Label>Password</Label>
|
||||
<Input type="password" bind:value={smtpConfig.config.auth.pass} />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<Label>From email address</Label>
|
||||
<Input type="email" bind:value={smtpConfig.config.from} />
|
||||
</div>
|
||||
<Button cta on:click={saveSmtp}>Save</Button>
|
||||
</Layout>
|
||||
</div>
|
||||
<Divider />
|
||||
|
||||
<div class="config-form">
|
||||
<Heading size="S">Templates</Heading>
|
||||
<Body size="S">
|
||||
Budibase comes out of the box with ready-made email templates to help
|
||||
with user onboarding. Please refrain from changing the links.
|
||||
</Body>
|
||||
<div class="template-controls">
|
||||
<Select bind:value={templateIdx} options={templateTypes} />
|
||||
<Button cta on:click={saveTemplate}>Save</Button>
|
||||
</div>
|
||||
{#if selectedTemplate}
|
||||
<TextArea bind:value={selectedTemplate.contents} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</Page>
|
||||
|
||||
<style>
|
||||
.config-form {
|
||||
margin-top: 42px;
|
||||
margin-bottom: 42px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 20% 1fr;
|
||||
grid-gap: var(--spacing-l);
|
||||
}
|
||||
|
||||
span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-s);
|
||||
}
|
||||
|
||||
header {
|
||||
margin-bottom: 42px;
|
||||
}
|
||||
|
||||
.template-controls {
|
||||
display: grid;
|
||||
grid-template-columns: 80% 1fr;
|
||||
grid-gap: var(--spacing-xl);
|
||||
margin-bottom: var(--spacing-xl);
|
||||
}
|
||||
</style>
|
|
@ -1,5 +1,5 @@
|
|||
const { generateTemplateID, StaticDatabases } = require("@budibase/auth").db
|
||||
const { CouchDB } = require("../../../db")
|
||||
const CouchDB = require("../../../db")
|
||||
const {
|
||||
TemplateMetadata,
|
||||
TemplateBindings,
|
||||
|
|
|
@ -17,7 +17,7 @@ function smtpValidation() {
|
|||
auth: Joi.object({
|
||||
type: Joi.string().valid("login", "oauth2", null),
|
||||
user: Joi.string().required(),
|
||||
pass: Joi.string().valid("", null),
|
||||
pass: Joi.string().allow("", null),
|
||||
}).optional(),
|
||||
}).unknown(true)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue