mirror of https://github.com/Budibase/budibase.git
48 changed files with 2438 additions and 732 deletions
@ -0,0 +1,61 @@ |
|||||
|
#!/usr/bin/env node
|
||||
|
|
||||
|
const os = require("os") |
||||
|
const exec = require("child_process").exec |
||||
|
const fs = require("fs") |
||||
|
const platform = os.platform() |
||||
|
|
||||
|
const windows = platform === "win32" |
||||
|
const mac = platform === "darwin" |
||||
|
const linux = platform === "linux" |
||||
|
|
||||
|
function execute(command) { |
||||
|
return new Promise(resolve => { |
||||
|
exec(command, (err, stdout) => resolve(linux ? !!stdout : true)) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
async function commandExistsUnix(command) { |
||||
|
const unixCmd = `command -v ${command} 2>/dev/null && { echo >&1 ${command}; exit 0; }` |
||||
|
return execute(command) |
||||
|
} |
||||
|
|
||||
|
async function commandExistsWindows(command) { |
||||
|
if (/[\x00-\x1f<>:"|?*]/.test(command)) { |
||||
|
return false |
||||
|
} |
||||
|
return execute(`where ${command}`) |
||||
|
} |
||||
|
|
||||
|
function commandExists(command) { |
||||
|
return windows ? commandExistsWindows(command) : commandExistsUnix(command) |
||||
|
} |
||||
|
|
||||
|
async function init() { |
||||
|
const docker = commandExists("docker") |
||||
|
const dockerCompose = commandExists("docker-compose") |
||||
|
if (docker && dockerCompose) { |
||||
|
console.log("Docker installed - continuing.") |
||||
|
return |
||||
|
} |
||||
|
if (mac) { |
||||
|
console.log( |
||||
|
"Please install docker by visiting: https://docs.docker.com/docker-for-mac/install/" |
||||
|
) |
||||
|
} else if (windows) { |
||||
|
console.log( |
||||
|
"Please install docker by visiting: https://docs.docker.com/docker-for-windows/install/" |
||||
|
) |
||||
|
} else if (linux) { |
||||
|
console.log("Beginning automated linux installation.") |
||||
|
await execute(`./hosting/scripts/linux/get-docker.sh`) |
||||
|
await execute(`./hosting/scripts/linux/get-docker-compose.sh`) |
||||
|
} else { |
||||
|
console.error( |
||||
|
"Platform unknown - please look online for information about installing docker for our OS." |
||||
|
) |
||||
|
} |
||||
|
console.log("Once installation complete please re-run the setup script.") |
||||
|
process.exit(-1) |
||||
|
} |
||||
|
init() |
||||
@ -1,25 +0,0 @@ |
|||||
<script> |
|
||||
import { onMount } from "svelte" |
|
||||
import AppCard from "./AppCard.svelte" |
|
||||
import { apps } from "stores/portal" |
|
||||
|
|
||||
onMount(apps.load) |
|
||||
</script> |
|
||||
|
|
||||
{#if $apps.length} |
|
||||
<div class="appList"> |
|
||||
{#each $apps as app} |
|
||||
<AppCard {...app} /> |
|
||||
{/each} |
|
||||
</div> |
|
||||
{:else} |
|
||||
<div>No apps found.</div> |
|
||||
{/if} |
|
||||
|
|
||||
<style> |
|
||||
.appList { |
|
||||
display: grid; |
|
||||
grid-gap: 50px; |
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); |
|
||||
} |
|
||||
</style> |
|
||||
@ -0,0 +1,80 @@ |
|||||
|
<script> |
||||
|
import { gradient } from "actions" |
||||
|
import { |
||||
|
Heading, |
||||
|
Button, |
||||
|
Icon, |
||||
|
ActionMenu, |
||||
|
MenuItem, |
||||
|
Link, |
||||
|
} from "@budibase/bbui" |
||||
|
import { url } from "@roxi/routify" |
||||
|
|
||||
|
export let app |
||||
|
export let openApp |
||||
|
export let exportApp |
||||
|
export let deleteApp |
||||
|
export let last |
||||
|
</script> |
||||
|
|
||||
|
<div class="title" class:last> |
||||
|
<div class="preview" use:gradient={{ seed: app.name }} /> |
||||
|
<Link href={$url(`../../app/${app._id}`)}> |
||||
|
<Heading size="XS"> |
||||
|
{app.name} |
||||
|
</Heading> |
||||
|
</Link> |
||||
|
</div> |
||||
|
<div class:last> |
||||
|
Edited {Math.round(Math.random() * 10 + 1)} months ago |
||||
|
</div> |
||||
|
<div class:last> |
||||
|
{#if Math.random() < 0.33} |
||||
|
<div class="status status--open" /> |
||||
|
Open |
||||
|
{:else if Math.random() < 0.33} |
||||
|
<div class="status status--locked-other" /> |
||||
|
Locked by Will Wheaton |
||||
|
{:else} |
||||
|
<div class="status status--locked-you" /> |
||||
|
Locked by you |
||||
|
{/if} |
||||
|
</div> |
||||
|
<div class:last> |
||||
|
<Button on:click={() => openApp(app)} size="S" secondary>Open</Button> |
||||
|
<ActionMenu align="right"> |
||||
|
<Icon hoverable slot="control" name="More" /> |
||||
|
<MenuItem on:click={() => exportApp(app)} icon="Download">Export</MenuItem> |
||||
|
<MenuItem on:click={() => deleteApp(app)} icon="Delete">Delete</MenuItem> |
||||
|
</ActionMenu> |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.preview { |
||||
|
height: 40px; |
||||
|
width: 40px; |
||||
|
border-radius: var(--border-radius-s); |
||||
|
} |
||||
|
.title :global(a) { |
||||
|
text-decoration: none; |
||||
|
} |
||||
|
.title :global(h1:hover) { |
||||
|
color: var(--spectrum-global-color-blue-600); |
||||
|
cursor: pointer; |
||||
|
transition: color 130ms ease; |
||||
|
} |
||||
|
.status { |
||||
|
height: 10px; |
||||
|
width: 10px; |
||||
|
border-radius: 50%; |
||||
|
} |
||||
|
.status--locked-you { |
||||
|
background-color: var(--spectrum-global-color-orange-600); |
||||
|
} |
||||
|
.status--locked-other { |
||||
|
background-color: var(--spectrum-global-color-red-600); |
||||
|
} |
||||
|
.status--open { |
||||
|
background-color: var(--spectrum-global-color-green-600); |
||||
|
} |
||||
|
</style> |
||||
@ -1,83 +0,0 @@ |
|||||
<script> |
|
||||
export let step, done, active |
|
||||
</script> |
|
||||
|
|
||||
<div class="container" class:active class:done> |
|
||||
<div class="circle" class:active class:done> |
|
||||
{#if done} |
|
||||
<svg |
|
||||
width="12" |
|
||||
height="10" |
|
||||
viewBox="0 0 12 10" |
|
||||
fill="none" |
|
||||
xmlns="http://www.w3.org/2000/svg" |
|
||||
> |
|
||||
<path |
|
||||
fill-rule="evenodd" |
|
||||
clip-rule="evenodd" |
|
||||
d="M10.1212 0.319527C10.327 0.115582 10.6047 0.000803464 10.8944 |
|
||||
4.20219e-06C11.1841 -0.00079506 11.4624 0.11245 11.6693 |
|
||||
0.315256C11.8762 0.518062 11.9949 0.794134 11.9998 1.08379C12.0048 |
|
||||
1.37344 11.8955 1.65339 11.6957 1.86313L5.82705 9.19893C5.72619 |
|
||||
9.30757 5.60445 9.39475 5.46913 9.45527C5.3338 9.51578 5.18766 9.54839 |
|
||||
5.03944 9.55113C4.89123 9.55388 4.74398 9.52671 4.60651 |
|
||||
9.47124C4.46903 9.41578 4.34416 9.33316 4.23934 9.22833L0.350925 |
|
||||
5.33845C0.242598 5.23751 0.155712 5.11578 0.0954499 4.98054C0.0351876 |
|
||||
4.84529 0.00278364 4.69929 0.00017159 4.55124C-0.00244046 4.4032 |
|
||||
0.024793 4.25615 0.0802466 4.11886C0.1357 3.98157 0.218238 3.85685 |
|
||||
0.322937 3.75215C0.427636 3.64746 0.55235 3.56492 0.68964 |
|
||||
3.50946C0.82693 3.45401 0.973983 3.42678 1.12203 3.42939C1.27007 3.432 |
|
||||
1.41607 3.46441 1.55132 3.52467C1.68657 3.58493 1.80829 3.67182 |
|
||||
1.90923 3.78014L4.98762 6.85706L10.0933 0.35187C10.1024 0.340482 |
|
||||
10.1122 0.329679 10.1227 0.319527H10.1212Z" |
|
||||
fill="var(--background)" |
|
||||
/> |
|
||||
</svg> |
|
||||
{:else}{step}{/if} |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<style> |
|
||||
.container::before { |
|
||||
content: ""; |
|
||||
position: absolute; |
|
||||
top: -30px; |
|
||||
width: 1px; |
|
||||
height: 30px; |
|
||||
background: var(--grey-5); |
|
||||
} |
|
||||
.container:first-child::before { |
|
||||
display: none; |
|
||||
} |
|
||||
.container { |
|
||||
position: relative; |
|
||||
height: 45px; |
|
||||
display: grid; |
|
||||
place-items: center; |
|
||||
} |
|
||||
.container.active { |
|
||||
box-shadow: inset 3px 0 0 0 var(--blue); |
|
||||
} |
|
||||
.circle.active { |
|
||||
background: var(--blue); |
|
||||
color: white; |
|
||||
border: none; |
|
||||
} |
|
||||
.circle.done { |
|
||||
background: var(--grey-5); |
|
||||
color: white; |
|
||||
border: none; |
|
||||
} |
|
||||
.circle { |
|
||||
color: var(--grey-5); |
|
||||
font-size: 14px; |
|
||||
font-weight: 500; |
|
||||
display: grid; |
|
||||
place-items: center; |
|
||||
width: 30px; |
|
||||
height: 30px; |
|
||||
border-radius: 50%; |
|
||||
border: 1px solid var(--grey-5); |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,121 +0,0 @@ |
|||||
<script> |
|
||||
import { Label, Heading, Input, notifications } from "@budibase/bbui" |
|
||||
|
|
||||
const BYTES_IN_MB = 1000000 |
|
||||
const FILE_SIZE_LIMIT = BYTES_IN_MB * 5 |
|
||||
|
|
||||
export let template |
|
||||
export let values |
|
||||
export let errors |
|
||||
export let touched |
|
||||
|
|
||||
let blurred = { appName: false } |
|
||||
let file |
|
||||
|
|
||||
function handleFile(evt) { |
|
||||
const fileArray = Array.from(evt.target.files) |
|
||||
if (fileArray.some(file => file.size >= FILE_SIZE_LIMIT)) { |
|
||||
notifications.error( |
|
||||
`Files cannot exceed ${ |
|
||||
FILE_SIZE_LIMIT / BYTES_IN_MB |
|
||||
}MB. Please try again with smaller files.` |
|
||||
) |
|
||||
return |
|
||||
} |
|
||||
file = evt.target.files[0] |
|
||||
template.file = file |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<div class="container"> |
|
||||
{#if template?.fromFile} |
|
||||
<Heading size="L">Import your Web App</Heading> |
|
||||
{:else} |
|
||||
<Heading size="L">Create your Web App</Heading> |
|
||||
{/if} |
|
||||
{#if template?.fromFile} |
|
||||
<div class="template"> |
|
||||
<Label extraSmall grey>Import File</Label> |
|
||||
<div class="dropzone"> |
|
||||
<input |
|
||||
id="file-upload" |
|
||||
accept=".txt" |
|
||||
type="file" |
|
||||
on:change={handleFile} |
|
||||
/> |
|
||||
<label for="file-upload" class:uploaded={file}> |
|
||||
{#if file}{file.name}{:else}Import{/if} |
|
||||
</label> |
|
||||
</div> |
|
||||
</div> |
|
||||
{:else if template} |
|
||||
<div class="template"> |
|
||||
<Label extraSmall grey>Selected Template</Label> |
|
||||
<Heading size="S">{template.name}</Heading> |
|
||||
</div> |
|
||||
{/if} |
|
||||
<Input |
|
||||
on:change={() => ($touched.applicationName = true)} |
|
||||
bind:value={$values.applicationName} |
|
||||
label="Web App Name" |
|
||||
placeholder="Enter name of your web application" |
|
||||
error={$touched.applicationName && $errors.applicationName} |
|
||||
/> |
|
||||
</div> |
|
||||
|
|
||||
<style> |
|
||||
.container { |
|
||||
display: grid; |
|
||||
grid-gap: var(--spacing-xl); |
|
||||
margin-top: var(--spacing-xl); |
|
||||
} |
|
||||
|
|
||||
.template :global(label) { |
|
||||
/* Fix layout due to LH 0 on heading */ |
|
||||
margin-bottom: 16px; |
|
||||
} |
|
||||
|
|
||||
.dropzone { |
|
||||
text-align: center; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
flex-direction: column; |
|
||||
border-radius: 10px; |
|
||||
transition: all 0.3s; |
|
||||
} |
|
||||
|
|
||||
.uploaded { |
|
||||
color: var(--blue); |
|
||||
} |
|
||||
|
|
||||
input[type="file"] { |
|
||||
display: none; |
|
||||
} |
|
||||
|
|
||||
label { |
|
||||
font-family: var(--font-sans); |
|
||||
cursor: pointer; |
|
||||
font-weight: 500; |
|
||||
box-sizing: border-box; |
|
||||
overflow: hidden; |
|
||||
border-radius: var(--border-radius-s); |
|
||||
color: var(--ink); |
|
||||
padding: var(--spacing-m) var(--spacing-l); |
|
||||
transition: all 0.2s ease 0s; |
|
||||
display: inline-flex; |
|
||||
text-rendering: optimizeLegibility; |
|
||||
min-width: auto; |
|
||||
outline: none; |
|
||||
font-feature-settings: "case" 1, "rlig" 1, "calt" 0; |
|
||||
-webkit-box-align: center; |
|
||||
user-select: none; |
|
||||
flex-shrink: 0; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
width: 100%; |
|
||||
background-color: var(--grey-2); |
|
||||
font-size: var(--font-size-xs); |
|
||||
line-height: normal; |
|
||||
border: var(--border-transparent); |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,29 +0,0 @@ |
|||||
<script> |
|
||||
import { Select, Heading } from "@budibase/bbui" |
|
||||
|
|
||||
export let values |
|
||||
export let errors |
|
||||
export let touched |
|
||||
</script> |
|
||||
|
|
||||
<div class="container"> |
|
||||
<Heading size="L">What's your role for this app?</Heading> |
|
||||
<Select |
|
||||
bind:value={$values.roleId} |
|
||||
label="Role" |
|
||||
options={[ |
|
||||
{ label: "Admin", value: "ADMIN" }, |
|
||||
{ label: "Power User", value: "POWER_USER" }, |
|
||||
]} |
|
||||
getOptionLabel={option => option.label} |
|
||||
getOptionValue={option => option.value} |
|
||||
error={$errors.roleId} |
|
||||
/> |
|
||||
</div> |
|
||||
|
|
||||
<style> |
|
||||
.container { |
|
||||
display: grid; |
|
||||
grid-gap: var(--spacing-xl); |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,2 +0,0 @@ |
|||||
export { default as Info } from "./Info.svelte" |
|
||||
export { default as User } from "./User.svelte" |
|
||||
@ -1,7 +0,0 @@ |
|||||
<script> |
|
||||
import { Page } from "@budibase/bbui" |
|
||||
</script> |
|
||||
|
|
||||
<Page wide> |
|
||||
<slot /> |
|
||||
</Page> |
|
||||
@ -0,0 +1,15 @@ |
|||||
|
<script> |
||||
|
import { Body, Menu, MenuItem, Detail, MenuSection, DetailSummary } from "@budibase/bbui" |
||||
|
|
||||
|
export let bindings |
||||
|
export let onBindingClick = () => {} |
||||
|
</script> |
||||
|
|
||||
|
<Menu> |
||||
|
{#each bindings as binding} |
||||
|
<MenuItem on:click={() => onBindingClick(binding)}> |
||||
|
<Detail size="M">{binding.name}</Detail> |
||||
|
<Body size="XS" noPadding>{binding.description}</Body> |
||||
|
</MenuItem> |
||||
|
{/each} |
||||
|
</Menu> |
||||
@ -0,0 +1,17 @@ |
|||||
|
<script> |
||||
|
import { url } from "@roxi/routify" |
||||
|
import { |
||||
|
Link, |
||||
|
} from "@budibase/bbui" |
||||
|
import { roles } from "stores/backend" |
||||
|
|
||||
|
export let value |
||||
|
</script> |
||||
|
|
||||
|
<Link quiet href={$url(`./${value}`)}><span>{value}</span></Link> |
||||
|
|
||||
|
<style> |
||||
|
span { |
||||
|
text-transform: capitalize; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,140 @@ |
|||||
|
<script> |
||||
|
import { |
||||
|
Menu, |
||||
|
MenuItem, |
||||
|
Button, |
||||
|
Detail, |
||||
|
Heading, |
||||
|
Divider, |
||||
|
Label, |
||||
|
Modal, |
||||
|
ModalContent, |
||||
|
notifications, |
||||
|
Layout, |
||||
|
Icon, |
||||
|
Body, |
||||
|
Page, |
||||
|
Select, |
||||
|
Tabs, |
||||
|
Tab, |
||||
|
MenuSection, |
||||
|
MenuSeparator, |
||||
|
} from "@budibase/bbui" |
||||
|
import { goto } from "@roxi/routify" |
||||
|
import { onMount } from "svelte" |
||||
|
import { fade } from "svelte/transition" |
||||
|
import { email } from "stores/portal" |
||||
|
import Editor from "components/integration/QueryEditor.svelte" |
||||
|
import TemplateBindings from "./TemplateBindings.svelte" |
||||
|
import api from "builderStore/api" |
||||
|
|
||||
|
const ConfigTypes = { |
||||
|
SMTP: "smtp", |
||||
|
} |
||||
|
|
||||
|
export let template |
||||
|
|
||||
|
let selected = "Edit" |
||||
|
let selectedBindingTab = "Template" |
||||
|
let htmlEditor |
||||
|
|
||||
|
$: selectedTemplate = $email.templates.find( |
||||
|
({ purpose }) => purpose === template |
||||
|
) |
||||
|
$: templateBindings = |
||||
|
$email.definitions?.bindings[selectedTemplate.purpose] || [] |
||||
|
|
||||
|
async function saveTemplate() { |
||||
|
try { |
||||
|
// Save your template config |
||||
|
await email.templates.save(selectedTemplate) |
||||
|
notifications.success(`Template saved.`) |
||||
|
} catch (err) { |
||||
|
notifications.error(`Failed to update template settings. ${err}`) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function setTemplateBinding(binding) { |
||||
|
htmlEditor.update((selectedTemplate.contents += `{{ ${binding.name} }}`)) |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<Page wide gap="L"> |
||||
|
<div class="backbutton" on:click={() => $goto("./")}> |
||||
|
<Icon name="BackAndroid" /> |
||||
|
<span>Back</span> |
||||
|
</div> |
||||
|
<header> |
||||
|
<Heading> |
||||
|
Email Template: {template} |
||||
|
</Heading> |
||||
|
<Button cta on:click={saveTemplate}>Save</Button> |
||||
|
</header> |
||||
|
<Tabs {selected}> |
||||
|
<Tab title="Edit"> |
||||
|
<div class="template-editor"> |
||||
|
<Editor |
||||
|
editorHeight={800} |
||||
|
bind:this={htmlEditor} |
||||
|
mode="handlebars" |
||||
|
on:change={e => { |
||||
|
selectedTemplate.contents = e.detail.value |
||||
|
}} |
||||
|
value={selectedTemplate.contents} |
||||
|
/> |
||||
|
<div class="bindings-editor"> |
||||
|
<Detail size="L">Bindings</Detail> |
||||
|
<Tabs selected={selectedBindingTab}> |
||||
|
<Tab title="Template"> |
||||
|
<TemplateBindings |
||||
|
title="Template Bindings" |
||||
|
bindings={templateBindings} |
||||
|
onBindingClick={setTemplateBinding} |
||||
|
/> |
||||
|
</Tab> |
||||
|
<Tab title="Common"> |
||||
|
<TemplateBindings |
||||
|
title="Common Bindings" |
||||
|
bindings={$email.definitions.bindings.common} |
||||
|
onBindingClick={setTemplateBinding} |
||||
|
/> |
||||
|
</Tab> |
||||
|
</Tabs> |
||||
|
</div> |
||||
|
</Tab> |
||||
|
<Tab title="Preview"> |
||||
|
<div class="preview" transition:fade> |
||||
|
{@html selectedTemplate.contents} |
||||
|
</div> |
||||
|
</Tab> |
||||
|
</Tabs> |
||||
|
</Page> |
||||
|
|
||||
|
<style> |
||||
|
.template-editor { |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr 20%; |
||||
|
grid-gap: var(--spacing-xl); |
||||
|
margin-top: var(--spacing-xl); |
||||
|
} |
||||
|
|
||||
|
header { |
||||
|
display: flex; |
||||
|
width: 100%; |
||||
|
justify-content: space-between; |
||||
|
} |
||||
|
|
||||
|
.preview { |
||||
|
background: white; |
||||
|
height: 800px; |
||||
|
padding: var(--spacing-xl); |
||||
|
} |
||||
|
|
||||
|
.backbutton { |
||||
|
display: flex; |
||||
|
gap: var(--spacing-m); |
||||
|
margin-bottom: var(--spacing-xl); |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
</style> |
||||
@ -0,0 +1,44 @@ |
|||||
|
import { writable } from "svelte/store" |
||||
|
import api from "builderStore/api" |
||||
|
|
||||
|
export function createEmailStore() { |
||||
|
const store = writable([]) |
||||
|
|
||||
|
return { |
||||
|
subscribe: store.subscribe, |
||||
|
templates: { |
||||
|
fetch: async () => { |
||||
|
// fetch the email template definitions
|
||||
|
const response = await api.get(`/api/admin/template/definitions`) |
||||
|
const definitions = await response.json() |
||||
|
|
||||
|
// fetch the email templates themselves
|
||||
|
const templatesResponse = await api.get(`/api/admin/template/email`) |
||||
|
const templates = await templatesResponse.json() |
||||
|
|
||||
|
store.set({ |
||||
|
definitions, |
||||
|
templates, |
||||
|
}) |
||||
|
}, |
||||
|
save: async template => { |
||||
|
// Save your template config
|
||||
|
const response = await api.post(`/api/admin/template`, template) |
||||
|
const json = await response.json() |
||||
|
if (response.status !== 200) throw new Error(json.message) |
||||
|
template._rev = json._rev |
||||
|
template._id = json._id |
||||
|
|
||||
|
store.update(state => { |
||||
|
const currentIdx = state.templates.findIndex( |
||||
|
template => template.purpose === json.purpose |
||||
|
) |
||||
|
state.templates.splice(currentIdx, 1, template) |
||||
|
return state |
||||
|
}) |
||||
|
}, |
||||
|
}, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export const email = createEmailStore() |
||||
@ -1,3 +1,4 @@ |
|||||
export { organisation } from "./organisation" |
export { organisation } from "./organisation" |
||||
export { admin } from "./admin" |
export { admin } from "./admin" |
||||
export { apps } from "./apps" |
export { apps } from "./apps" |
||||
|
export { email } from "./email" |
||||
|
|||||
File diff suppressed because it is too large
@ -0,0 +1,93 @@ |
|||||
|
const authPkg = require("@budibase/auth") |
||||
|
const { google } = require("@budibase/auth/src/middleware") |
||||
|
const { Configs } = require("../../constants") |
||||
|
const CouchDB = require("../../db") |
||||
|
const { clearCookie } = authPkg.utils |
||||
|
const { Cookies } = authPkg.constants |
||||
|
const { passport } = authPkg.auth |
||||
|
|
||||
|
const GLOBAL_DB = authPkg.StaticDatabases.GLOBAL.name |
||||
|
|
||||
|
exports.authenticate = async (ctx, next) => { |
||||
|
return passport.authenticate("local", async (err, user) => { |
||||
|
if (err) { |
||||
|
return ctx.throw(403, "Unauthorized") |
||||
|
} |
||||
|
|
||||
|
const expires = new Date() |
||||
|
expires.setDate(expires.getDate() + 1) |
||||
|
|
||||
|
if (!user) { |
||||
|
return ctx.throw(403, "Unauthorized") |
||||
|
} |
||||
|
|
||||
|
ctx.cookies.set(Cookies.Auth, user.token, { |
||||
|
expires, |
||||
|
path: "/", |
||||
|
httpOnly: false, |
||||
|
overwrite: true, |
||||
|
}) |
||||
|
|
||||
|
delete user.token |
||||
|
|
||||
|
ctx.body = { user } |
||||
|
})(ctx, next) |
||||
|
} |
||||
|
|
||||
|
exports.logout = async ctx => { |
||||
|
clearCookie(ctx, Cookies.Auth) |
||||
|
ctx.body = { message: "User logged out" } |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* The initial call that google authentication makes to take you to the google login screen. |
||||
|
* On a successful login, you will be redirected to the googleAuth callback route. |
||||
|
*/ |
||||
|
exports.googlePreAuth = async (ctx, next) => { |
||||
|
const db = new CouchDB(GLOBAL_DB) |
||||
|
const config = await authPkg.db.getScopedFullConfig(db, { |
||||
|
type: Configs.GOOGLE, |
||||
|
group: ctx.query.group, |
||||
|
}) |
||||
|
const strategy = await google.strategyFactory(config) |
||||
|
|
||||
|
return passport.authenticate(strategy, { |
||||
|
scope: ["profile", "email"], |
||||
|
})(ctx, next) |
||||
|
} |
||||
|
|
||||
|
exports.googleAuth = async (ctx, next) => { |
||||
|
const db = new CouchDB(GLOBAL_DB) |
||||
|
|
||||
|
const config = await authPkg.db.getScopedFullConfig(db, { |
||||
|
type: Configs.GOOGLE, |
||||
|
group: ctx.query.group, |
||||
|
}) |
||||
|
const strategy = await google.strategyFactory(config) |
||||
|
|
||||
|
return passport.authenticate( |
||||
|
strategy, |
||||
|
{ successRedirect: "/", failureRedirect: "/error" }, |
||||
|
async (err, user) => { |
||||
|
if (err) { |
||||
|
return ctx.throw(403, "Unauthorized") |
||||
|
} |
||||
|
|
||||
|
const expires = new Date() |
||||
|
expires.setDate(expires.getDate() + 1) |
||||
|
|
||||
|
if (!user) { |
||||
|
return ctx.throw(403, "Unauthorized") |
||||
|
} |
||||
|
|
||||
|
ctx.cookies.set(Cookies.Auth, user.token, { |
||||
|
expires, |
||||
|
path: "/", |
||||
|
httpOnly: false, |
||||
|
overwrite: true, |
||||
|
}) |
||||
|
|
||||
|
ctx.redirect("/") |
||||
|
} |
||||
|
)(ctx, next) |
||||
|
} |
||||
Loading…
Reference in new issue