Improve routing based on auth and roles, and use redirects rather than pushing new routes

This commit is contained in:
Andrew Kingston 2021-05-18 14:39:26 +01:00
parent 473c9ebfa6
commit 78ba798be2
10 changed files with 155 additions and 200 deletions

View File

@ -25,11 +25,11 @@
<Layout noPadding gap="XS" alignContent="start"> <Layout noPadding gap="XS" alignContent="start">
<div class="preview" use:gradient={{ seed: app.name }} /> <div class="preview" use:gradient={{ seed: app.name }} />
<div class="title"> <div class="title">
<Link on:click={() => openApp(app)}> <div class="name" on:click={() => openApp(app)}>
<Heading size="XS"> <Heading size="XS">
{app.name} {app.name}
</Heading> </Heading>
</Link> </div>
<ActionMenu align="right"> <ActionMenu align="right">
<Icon slot="control" name="More" hoverable /> <Icon slot="control" name="More" hoverable />
<MenuItem on:click={() => exportApp(app)} icon="Download"> <MenuItem on:click={() => exportApp(app)} icon="Download">
@ -76,7 +76,7 @@
align-items: center; align-items: center;
} }
.title :global(a) { .name {
text-decoration: none; text-decoration: none;
flex: 1 1 auto; flex: 1 1 auto;
width: 0; width: 0;

View File

@ -1,13 +1,6 @@
<script> <script>
import { gradient } from "actions" import { gradient } from "actions"
import { import { Heading, Button, Icon, ActionMenu, MenuItem } from "@budibase/bbui"
Heading,
Button,
Icon,
ActionMenu,
MenuItem,
Link,
} from "@budibase/bbui"
import { auth } from "stores/backend" import { auth } from "stores/backend"
export let app export let app
@ -21,11 +14,11 @@
<div class="title" class:last> <div class="title" class:last>
<div class="preview" use:gradient={{ seed: app.name }} /> <div class="preview" use:gradient={{ seed: app.name }} />
<Link on:click={() => openApp(app)}> <div class="name" on:click={() => openApp(app)}>
<Heading size="XS"> <Heading size="XS">
{app.name} {app.name}
</Heading> </Heading>
</Link> </div>
</div> </div>
<div class:last> <div class:last>
Edited {Math.round(Math.random() * 10 + 1)} months ago Edited {Math.round(Math.random() * 10 + 1)} months ago
@ -66,7 +59,7 @@
width: 40px; width: 40px;
border-radius: var(--border-radius-s); border-radius: var(--border-radius-s);
} }
.title :global(a) { .name {
text-decoration: none; text-decoration: none;
} }
.title :global(h1:hover) { .title :global(h1:hover) {

View File

@ -1,6 +1,6 @@
<script> <script>
import { onMount } from "svelte" import { onMount } from "svelte"
import { isActive, goto } from "@roxi/routify" import { isActive, goto, redirect } from "@roxi/routify"
import { auth } from "stores/backend" import { auth } from "stores/backend"
import { admin } from "stores/portal" import { admin } from "stores/portal"
@ -16,14 +16,14 @@
// Force creation of an admin user if one doesn't exist // Force creation of an admin user if one doesn't exist
$: { $: {
if (loaded && !hasAdminUser) { if (loaded && !hasAdminUser) {
$goto("./admin") $redirect("./admin")
} }
} }
// Redirect to log in at any time if the user isn't authenticated // Redirect to log in at any time if the user isn't authenticated
$: { $: {
if (loaded && hasAdminUser && !$auth.user && !$isActive("./auth")) { if (loaded && hasAdminUser && !$auth.user && !$isActive("./auth")) {
$goto("./auth/login") $redirect("./auth/login")
} }
} }
</script> </script>

View File

@ -0,0 +1,7 @@
<script>
import { auth } from "stores/backend"
</script>
{#if $auth.user}
<slot />
{/if}

View File

@ -9,93 +9,25 @@
Avatar, Avatar,
Page, Page,
Icon, Icon,
notifications,
Body, Body,
} from "@budibase/bbui" } from "@budibase/bbui"
import api, { del } from "builderStore/api"
import analytics from "analytics"
import { onMount } from "svelte" import { onMount } from "svelte"
import { apps, organisation } from "stores/portal" import { apps, organisation } from "stores/portal"
import { auth } from "stores/backend" import { auth } from "stores/backend"
import download from "downloadjs"
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import { AppStatus } from "constants" import { AppStatus } from "constants"
import { gradient } from "actions" import { gradient } from "actions"
let layout = "grid"
let template
let appToDelete
let creationModal
let deletionModal
let creatingApp = false
let loaded = false let loaded = false
const checkKeys = async () => {
const response = await api.get(`/api/keys/`)
const keys = await response.json()
if (keys.userId) {
analytics.identify(keys.userId)
}
}
const initiateAppCreation = () => {
creationModal.show()
creatingApp = true
}
const initiateAppImport = () => {
template = { fromFile: true }
creationModal.show()
creatingApp = true
}
const stopAppCreation = () => {
template = null
creatingApp = false
}
const openApp = app => {
$goto(`../../app/${app._id}`)
}
const exportApp = app => {
try {
download(
`/api/backups/export?appId=${app._id}&appname=${encodeURIComponent(
app.name
)}`
)
notifications.success("App export complete")
} catch (err) {
console.error(err)
notifications.error("App export failed")
}
}
const deleteApp = app => {
appToDelete = app
deletionModal.show()
}
const confirmDeleteApp = async () => {
if (!appToDelete) {
return
}
await del(`/api/applications/${appToDelete?._id}`)
await apps.load()
appToDelete = null
}
onMount(async () => { onMount(async () => {
checkKeys()
await apps.load(AppStatus.DEV) await apps.load(AppStatus.DEV)
loaded = true loaded = true
}) })
$: console.log($auth.user)
</script> </script>
<div class="container"> {#if loaded}
<div class="container">
<Page> <Page>
<div class="content"> <div class="content">
<Layout noPadding> <Layout noPadding>
@ -104,9 +36,9 @@
<Layout noPadding gap="XS"> <Layout noPadding gap="XS">
<Heading size="L">Hey {$auth.user.email}</Heading> <Heading size="L">Hey {$auth.user.email}</Heading>
<Body noPadding> <Body noPadding>
Welcome to the {$organisation.company} portal. Below you'll find the Welcome to the {$organisation.company} portal. Below you'll find
list of apps that you have access to, as well as company news and the the list of apps that you have access to, as well as company news
employee handbook. and the employee handbook.
</Body> </Body>
</Layout> </Layout>
<ActionMenu align="right"> <ActionMenu align="right">
@ -114,13 +46,12 @@
<Avatar size="M" name="John Doe" /> <Avatar size="M" name="John Doe" />
<Icon size="XL" name="ChevronDown" /> <Icon size="XL" name="ChevronDown" />
</div> </div>
<MenuItem icon="UserEdit" on:click={auth.logout}> <MenuItem icon="UserEdit">Update user information</MenuItem>
Update user information <MenuItem icon="LockClosed">Update password</MenuItem>
</MenuItem> <MenuItem
<MenuItem icon="LockClosed" on:click={auth.logout}> icon="UserDeveloper"
Update password on:click={() => $goto("../portal")}
</MenuItem> >
<MenuItem icon="UserDeveloper" on:click={() => $goto("../portal")}>
Open developer mode Open developer mode
</MenuItem> </MenuItem>
<MenuItem icon="LogOut" on:click={auth.logout}>Log out</MenuItem> <MenuItem icon="LogOut" on:click={auth.logout}>Log out</MenuItem>
@ -140,7 +71,7 @@
{/if} {/if}
</div> </div>
{#each $apps as app, idx (app.appId)} {#each $apps as app, idx (app.appId)}
<div class="app" on:click={() => $goto(`../app/${app.appId}`)}> <a class="app" target="_blank" href={`/${app.appId}`}>
<div class="preview" use:gradient={{ seed: app.name }} /> <div class="preview" use:gradient={{ seed: app.name }} />
<div class="app-info"> <div class="app-info">
<Heading size="XS">{app.name}</Heading> <Heading size="XS">{app.name}</Heading>
@ -149,14 +80,15 @@
</Body> </Body>
</div> </div>
<Icon name="ChevronRight" /> <Icon name="ChevronRight" />
</div> </a>
{/each} {/each}
</Layout> </Layout>
</div> </div>
</Layout> </Layout>
</div> </div>
</Page> </Page>
</div> </div>
{/if}
<style> <style>
.container { .container {
@ -208,6 +140,7 @@
border-radius: var(--border-radius-s); border-radius: var(--border-radius-s);
align-items: center; align-items: center;
grid-gap: var(--spacing-xl); grid-gap: var(--spacing-xl);
color: inherit;
} }
.app:hover { .app:hover {
cursor: pointer; cursor: pointer;

View File

@ -1,4 +1,4 @@
<script> <script>
import { goto } from "@roxi/routify" import { redirect } from "@roxi/routify"
$goto("./login") $redirect("./login")
</script> </script>

View File

@ -1,4 +1,14 @@
<script> <script>
import { goto } from "@roxi/routify" import { redirect } from "@roxi/routify"
$goto("./portal") import { auth } from "stores/backend"
$: {
if (!$auth.user) {
$redirect("./auth/login")
} else if ($auth.user.builder?.global) {
$redirect("./portal")
} else {
$redirect("./apps")
}
}
</script> </script>

View File

@ -1,5 +1,5 @@
<script> <script>
import { isActive, goto } from "@roxi/routify" import { isActive, redirect, goto } from "@roxi/routify"
import { import {
Icon, Icon,
Avatar, Avatar,
@ -15,12 +15,12 @@
import { organisation } from "stores/portal" import { organisation } from "stores/portal"
import { auth } from "stores/backend" import { auth } from "stores/backend"
import BuilderSettingsModal from "components/start/BuilderSettingsModal.svelte" import BuilderSettingsModal from "components/start/BuilderSettingsModal.svelte"
import { onMount } from "svelte"
let oldSettingsModal let oldSettingsModal
let loaded = false
organisation.init() const menu = [
let menu = [
{ title: "Apps", href: "/builder/portal/apps" }, { title: "Apps", href: "/builder/portal/apps" },
{ title: "Drafts", href: "/builder/portal/drafts" }, { title: "Drafts", href: "/builder/portal/drafts" },
{ title: "Users", href: "/builder/portal/manage/users", heading: "Manage" }, { title: "Users", href: "/builder/portal/manage/users", heading: "Manage" },
@ -35,9 +35,20 @@
{ title: "Theming", href: "/builder/portal/theming" }, { title: "Theming", href: "/builder/portal/theming" },
{ title: "Account", href: "/builder/portal/account" }, { title: "Account", href: "/builder/portal/account" },
] ]
onMount(async () => {
// Prevent non-builders from accessing the portal
if (!$auth.user?.builder?.global) {
$redirect("../")
} else {
await organisation.init()
loaded = true
}
})
</script> </script>
<div class="container"> {#if loaded}
<div class="container">
<div class="nav"> <div class="nav">
<Layout paddingX="L" paddingY="L"> <Layout paddingX="L" paddingY="L">
<div class="branding"> <div class="branding">
@ -79,10 +90,11 @@
<slot /> <slot />
</div> </div>
</div> </div>
</div> </div>
<Modal bind:this={oldSettingsModal} width="30%"> <Modal bind:this={oldSettingsModal} width="30%">
<BuilderSettingsModal /> <BuilderSettingsModal />
</Modal> </Modal>
{/if}
<style> <style>
.container { .container {

View File

@ -1,4 +1,4 @@
<script> <script>
import { goto } from "@roxi/routify" import { redirect } from "@roxi/routify"
$goto("./apps") $redirect("./apps")
</script> </script>

View File

@ -1,4 +1,4 @@
<script> <script>
import { goto } from "@roxi/routify" import { redirect } from "@roxi/routify"
$goto("./builder") $redirect("./builder")
</script> </script>