SMTP and template management E2E

This commit is contained in:
Martin McKeaveney 2021-05-06 18:02:44 +01:00
parent 52d87c8267
commit 7588030780
5 changed files with 277 additions and 81 deletions

View File

@ -12,55 +12,47 @@
let modal let modal
</script> </script>
{#if $auth} <div class="root">
{#if $auth.user} <div class="ui-nav">
<div class="root"> <div class="home-logo">
<div class="ui-nav"> <img src={Logo} alt="Budibase icon" />
<div class="home-logo"> </div>
<img src={Logo} alt="Budibase icon" /> <div class="nav-section">
</div> <div class="nav-top">
<div class="nav-section"> <Navigation>
<div class="nav-top"> <Item href="/builder/" icon="Apps" selected>Apps</Item>
<Navigation> <Item external href="https://portal.budi.live/" icon="Servers">
<Item href="/builder/" icon="Apps" selected>Apps</Item> Hosting
<Item external href="https://portal.budi.live/" icon="Servers"> </Item>
Hosting <Item external href="https://docs.budibase.com/" icon="Book">
</Item> Documentation
<Item external href="https://docs.budibase.com/" icon="Book"> </Item>
Documentation <Item
</Item> external
<Item href="https://github.com/Budibase/budibase/discussions"
external icon="PeopleGroup"
href="https://github.com/Budibase/budibase/discussions" >
icon="PeopleGroup" Community
> </Item>
Community <Item
</Item> external
<Item href="https://github.com/Budibase/budibase/issues/new/choose"
external icon="Bug"
href="https://github.com/Budibase/budibase/issues/new/choose" >
icon="Bug" Raise an issue
> </Item>
Raise an issue </Navigation>
</Item>
</Navigation>
</div>
<div class="nav-bottom">
<BuilderSettingsButton />
<LogoutButton />
</div>
</div>
</div> </div>
<div class="main"> <div class="nav-bottom">
<slot /> <BuilderSettingsButton />
<LogoutButton />
</div> </div>
</div> </div>
{:else} </div>
<section class="login"> <div class="main">
<LoginForm /> <slot />
</section> </div>
{/if} </div>
{/if}
<style> <style>
.root { .root {

View File

@ -14,8 +14,10 @@
SideNavigation as Navigation, SideNavigation as Navigation,
SideNavigationItem as Item, SideNavigationItem as Item,
} from "@budibase/bbui" } from "@budibase/bbui"
import LoginForm from "components/login/LoginForm.svelte"
import api from "builderStore/api" import api from "builderStore/api"
import ConfigChecklist from "components/common/ConfigChecklist.svelte" import ConfigChecklist from "components/common/ConfigChecklist.svelte"
import { auth } from "stores/backend"
import { organisation, admin } from "stores/portal" import { organisation, admin } from "stores/portal"
organisation.init() organisation.init()
@ -47,43 +49,52 @@
] ]
</script> </script>
<div class="container"> {#if $auth}
<div class="nav"> {#if $auth.user}
<Layout paddingX="L" paddingY="L"> <div class="container">
<div class="branding"> <div class="nav">
<div class="name"> <Layout paddingX="L" paddingY="L">
<img <div class="branding">
src={$organisation?.logoUrl || "https://i.imgur.com/ZKyklgF.png"} <div class="name">
alt="Logotype" <img
/> src={$organisation?.logoUrl ||
<span>{$organisation?.company || "Budibase"}</span> "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>
<div class="onboarding"> <div class="content">
<ConfigChecklist /> <slot />
</div> </div>
</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>
<div> {:else}
<slot /> <section class="login">
</div> <LoginForm />
</div> </section>
</div> {/if}
{/if}
<style> <style>
.container { .container {

View File

@ -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>

View File

@ -1,5 +1,5 @@
const { generateTemplateID, StaticDatabases } = require("@budibase/auth").db const { generateTemplateID, StaticDatabases } = require("@budibase/auth").db
const { CouchDB } = require("../../../db") const CouchDB = require("../../../db")
const { const {
TemplateMetadata, TemplateMetadata,
TemplateBindings, TemplateBindings,

View File

@ -17,7 +17,7 @@ function smtpValidation() {
auth: Joi.object({ auth: Joi.object({
type: Joi.string().valid("login", "oauth2", null), type: Joi.string().valid("login", "oauth2", null),
user: Joi.string().required(), user: Joi.string().required(),
pass: Joi.string().valid("", null), pass: Joi.string().allow("", null),
}).optional(), }).optional(),
}).unknown(true) }).unknown(true)
} }