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

View File

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

View File

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

View File

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

View File

@ -9,154 +9,86 @@
Avatar,
Page,
Icon,
notifications,
Body,
} from "@budibase/bbui"
import api, { del } from "builderStore/api"
import analytics from "analytics"
import { onMount } from "svelte"
import { apps, organisation } from "stores/portal"
import { auth } from "stores/backend"
import download from "downloadjs"
import { goto } from "@roxi/routify"
import { AppStatus } from "constants"
import { gradient } from "actions"
let layout = "grid"
let template
let appToDelete
let creationModal
let deletionModal
let creatingApp = 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 () => {
checkKeys()
await apps.load(AppStatus.DEV)
loaded = true
})
$: console.log($auth.user)
</script>
<div class="container">
<Page>
<div class="content">
<Layout noPadding>
<img src={$organisation.logoUrl} />
<div class="info-title">
<Layout noPadding gap="XS">
<Heading size="L">Hey {$auth.user.email}</Heading>
<Body noPadding>
Welcome to the {$organisation.company} portal. Below you'll find the
list of apps that you have access to, as well as company news and the
employee handbook.
</Body>
</Layout>
<ActionMenu align="right">
<div slot="control" class="avatar">
<Avatar size="M" name="John Doe" />
<Icon size="XL" name="ChevronDown" />
</div>
<MenuItem icon="UserEdit" on:click={auth.logout}>
Update user information
</MenuItem>
<MenuItem icon="LockClosed" on:click={auth.logout}>
Update password
</MenuItem>
<MenuItem icon="UserDeveloper" on:click={() => $goto("../portal")}>
Open developer mode
</MenuItem>
<MenuItem icon="LogOut" on:click={auth.logout}>Log out</MenuItem>
</ActionMenu>
</div>
<Divider />
<div class="apps-title">
<Heading>Apps</Heading>
<Select placeholder="Filter by groups" />
</div>
<div class="group">
<Layout gap="S" noPadding>
<div class="group-title">
<Body weight="500" noPadding size="XS">GROUP</Body>
{#if $auth.user?.builder?.global}
<Icon name="Settings" hoverable />
{/if}
</div>
{#each $apps as app, idx (app.appId)}
<div class="app" on:click={() => $goto(`../app/${app.appId}`)}>
<div class="preview" use:gradient={{ seed: app.name }} />
<div class="app-info">
<Heading size="XS">{app.name}</Heading>
<Body noPadding size="S">
Edited {Math.round(Math.random() * 10 + 1)} months ago
</Body>
</div>
<Icon name="ChevronRight" />
{#if loaded}
<div class="container">
<Page>
<div class="content">
<Layout noPadding>
<img src={$organisation.logoUrl} />
<div class="info-title">
<Layout noPadding gap="XS">
<Heading size="L">Hey {$auth.user.email}</Heading>
<Body noPadding>
Welcome to the {$organisation.company} portal. Below you'll find
the list of apps that you have access to, as well as company news
and the employee handbook.
</Body>
</Layout>
<ActionMenu align="right">
<div slot="control" class="avatar">
<Avatar size="M" name="John Doe" />
<Icon size="XL" name="ChevronDown" />
</div>
{/each}
</Layout>
</div>
</Layout>
</div>
</Page>
</div>
<MenuItem icon="UserEdit">Update user information</MenuItem>
<MenuItem icon="LockClosed">Update password</MenuItem>
<MenuItem
icon="UserDeveloper"
on:click={() => $goto("../portal")}
>
Open developer mode
</MenuItem>
<MenuItem icon="LogOut" on:click={auth.logout}>Log out</MenuItem>
</ActionMenu>
</div>
<Divider />
<div class="apps-title">
<Heading>Apps</Heading>
<Select placeholder="Filter by groups" />
</div>
<div class="group">
<Layout gap="S" noPadding>
<div class="group-title">
<Body weight="500" noPadding size="XS">GROUP</Body>
{#if $auth.user?.builder?.global}
<Icon name="Settings" hoverable />
{/if}
</div>
{#each $apps as app, idx (app.appId)}
<a class="app" target="_blank" href={`/${app.appId}`}>
<div class="preview" use:gradient={{ seed: app.name }} />
<div class="app-info">
<Heading size="XS">{app.name}</Heading>
<Body noPadding size="S">
Edited {Math.round(Math.random() * 10 + 1)} months ago
</Body>
</div>
<Icon name="ChevronRight" />
</a>
{/each}
</Layout>
</div>
</Layout>
</div>
</Page>
</div>
{/if}
<style>
.container {
@ -208,6 +140,7 @@
border-radius: var(--border-radius-s);
align-items: center;
grid-gap: var(--spacing-xl);
color: inherit;
}
.app:hover {
cursor: pointer;

View File

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

View File

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

View File

@ -1,5 +1,5 @@
<script>
import { isActive, goto } from "@roxi/routify"
import { isActive, redirect, goto } from "@roxi/routify"
import {
Icon,
Avatar,
@ -15,12 +15,12 @@
import { organisation } from "stores/portal"
import { auth } from "stores/backend"
import BuilderSettingsModal from "components/start/BuilderSettingsModal.svelte"
import { onMount } from "svelte"
let oldSettingsModal
let loaded = false
organisation.init()
let menu = [
const menu = [
{ title: "Apps", href: "/builder/portal/apps" },
{ title: "Drafts", href: "/builder/portal/drafts" },
{ title: "Users", href: "/builder/portal/manage/users", heading: "Manage" },
@ -35,54 +35,66 @@
{ title: "Theming", href: "/builder/portal/theming" },
{ 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>
<div class="container">
<div class="nav">
<Layout paddingX="L" paddingY="L">
<div class="branding">
<div class="name" on:click={() => $goto("./apps")}>
<img
src={$organisation?.logoUrl || "https://i.imgur.com/ZKyklgF.png"}
alt="Logotype"
/>
<span>{$organisation?.company || "Budibase"}</span>
{#if loaded}
<div class="container">
<div class="nav">
<Layout paddingX="L" paddingY="L">
<div class="branding">
<div class="name" on:click={() => $goto("./apps")}>
<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="onboarding">
<ConfigChecklist />
<div class="menu">
<Navigation>
{#each menu as { title, href, heading }}
<Item selected={$isActive(href)} {href} {heading}>{title}</Item>
{/each}
</Navigation>
</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" />
<ActionMenu align="right">
<div slot="control" class="avatar">
<Avatar size="M" name="John Doe" />
<Icon size="XL" name="ChevronDown" />
</div>
<MenuItem icon="Settings" on:click={oldSettingsModal.show}>
Old settings
</MenuItem>
<MenuItem icon="LogOut" on:click={auth.logout}>Log out</MenuItem>
</ActionMenu>
</Layout>
</div>
<div class="content">
<slot />
<div class="main">
<div class="toolbar">
<Search placeholder="Global search" />
<ActionMenu align="right">
<div slot="control" class="avatar">
<Avatar size="M" name="John Doe" />
<Icon size="XL" name="ChevronDown" />
</div>
<MenuItem icon="Settings" on:click={oldSettingsModal.show}>
Old settings
</MenuItem>
<MenuItem icon="LogOut" on:click={auth.logout}>Log out</MenuItem>
</ActionMenu>
</div>
<div class="content">
<slot />
</div>
</div>
</div>
</div>
<Modal bind:this={oldSettingsModal} width="30%">
<BuilderSettingsModal />
</Modal>
<Modal bind:this={oldSettingsModal} width="30%">
<BuilderSettingsModal />
</Modal>
{/if}
<style>
.container {

View File

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

View File

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