Update remaining builder stores to use new core API and handle errors properly

This commit is contained in:
Andrew Kingston 2022-01-24 14:32:27 +00:00
parent db30c91a0b
commit e0cb60d011
42 changed files with 740 additions and 573 deletions

View File

@ -109,9 +109,11 @@ export const getFrontendStore = () => {
theme: {
save: async theme => {
const appId = get(store).appId
const metadata = { appId, theme }
try {
await API.saveAppMetadata(metadata)
await API.saveAppMetadata({
appId,
metadata: { theme },
})
store.update(state => {
state.theme = theme
return state
@ -124,9 +126,11 @@ export const getFrontendStore = () => {
customTheme: {
save: async customTheme => {
const appId = get(store).appId
const metadata = { appId, customTheme }
try {
await API.saveAppMetadata(metadata)
await API.saveAppMetadata({
appId,
metadata: { customTheme },
})
store.update(state => {
state.customTheme = customTheme
return state

View File

@ -13,6 +13,7 @@
Detail,
Divider,
Layout,
notifications,
} from "@budibase/bbui"
import { auth } from "stores/portal"
@ -45,20 +46,28 @@
improvements,
comment,
})
auth.updateSelf({
flags: {
feedbackSubmitted: true,
},
})
try {
auth.updateSelf({
flags: {
feedbackSubmitted: true,
},
})
} catch (error) {
notifications.error("Error updating user")
}
dispatch("complete")
}
function cancelFeedback() {
auth.updateSelf({
flags: {
feedbackSubmitted: true,
},
})
try {
auth.updateSelf({
flags: {
feedbackSubmitted: true,
},
})
} catch (error) {
notifications.error("Error updating user")
}
dispatch("complete")
}
</script>

View File

@ -1,5 +1,12 @@
<script>
import { ModalContent, Modal, Icon, ColorPicker, Label } from "@budibase/bbui"
import {
ModalContent,
Modal,
Icon,
ColorPicker,
Label,
notifications,
} from "@budibase/bbui"
import { apps } from "stores/portal"
export let app
@ -51,12 +58,16 @@
}
const save = async () => {
await apps.update(app.instance._id, {
icon: {
name: selectedIcon,
color: selectedColor,
},
})
try {
await apps.update(app.instance._id, {
icon: {
name: selectedIcon,
color: selectedColor,
},
})
} catch (error) {
notifications.error("Error updating app")
}
}
</script>

View File

@ -118,15 +118,18 @@
await auth.setInitInfo({})
$goto(`/builder/app/${createdApp.instance._id}`)
} catch (error) {
console.error(error)
notifications.error(error)
notifications.error("Error creating app")
submitting = false
}
}
async function onCancel() {
template = null
await auth.setInitInfo({})
try {
await auth.setInitInfo({})
} catch (error) {
notifications.error("Error setting init info")
}
}
</script>

View File

@ -80,8 +80,7 @@
await apps.update(app.instance._id, { name: $values.name.trim() })
hide()
} catch (error) {
console.error(error)
notifications.error(error)
notifications.error("Error updating app")
}
}

View File

@ -3,6 +3,7 @@
import { admin, auth } from "stores/portal"
import { onMount } from "svelte"
import { CookieUtils, Constants } from "@budibase/frontend-core"
import { notifications } from "@budibase/bbui"
let loaded = false
@ -41,9 +42,12 @@
if (user.tenantId !== urlTenantId) {
// user should not be here - play it safe and log them out
await auth.logout()
await auth.setOrganisation(null)
return
try {
await auth.logout()
await auth.setOrganisation(null)
} catch (error) {
// Swallow error and do nothing
}
}
} else {
// no user - set the org according to the url
@ -52,17 +56,18 @@
}
onMount(async () => {
if ($params["?template"]) {
await auth.setInitInfo({ init_template: $params["?template"] })
try {
if ($params["?template"]) {
await auth.setInitInfo({ init_template: $params["?template"] })
}
await auth.checkAuth()
await admin.init()
if (useAccountPortal && multiTenancyEnabled) {
await validateTenantId()
}
} catch (error) {
notifications.error("Error initialising builder")
}
await auth.checkAuth()
await admin.init()
if (useAccountPortal && multiTenancyEnabled) {
await validateTenantId()
}
loaded = true
})

View File

@ -31,17 +31,21 @@
adminUser.tenantId = tenantId
// Save the admin user
await API.createAdminUser(adminUser)
notifications.success(`Admin user created`)
notifications.success("Admin user created")
await admin.init()
$goto("../portal")
} catch (err) {
notifications.error(`Failed to create admin user: ${err}`)
} catch (error) {
notifications.error("Failed to create admin user")
}
}
onMount(async () => {
if (!cloud) {
await admin.checkImportComplete()
try {
await admin.checkImportComplete()
} catch (error) {
notifications.error("Error checking import status")
}
}
})
</script>

View File

@ -10,6 +10,7 @@
Icon,
Body,
Modal,
notifications,
} from "@budibase/bbui"
import { onMount } from "svelte"
import { apps, organisation, auth, admin } from "stores/portal"
@ -26,8 +27,12 @@
let changePasswordModal
onMount(async () => {
await organisation.init()
await apps.load()
try {
await organisation.init()
await apps.load()
} catch (error) {
notifications.error("Error loading apps")
}
loaded = true
})
@ -44,6 +49,14 @@
function getUrl(app) {
return !isCloud ? `/app/${encodeURIComponent(app.name)}` : `/${app.prodId}`
}
const logout = async () => {
try {
await auth.logout()
} catch (error) {
// Swallow error and do nothing
}
}
</script>
{#if $auth.user && loaded}
@ -79,7 +92,7 @@
Open developer mode
</MenuItem>
{/if}
<MenuItem icon="LogOut" on:click={auth.logout}>Log out</MenuItem>
<MenuItem icon="LogOut" on:click={logout}>Log out</MenuItem>
</ActionMenu>
</div>
<Layout noPadding gap="XS">

View File

@ -1,5 +1,5 @@
<script>
import { ActionButton } from "@budibase/bbui"
import { ActionButton, notifications } from "@budibase/bbui"
import OidcLogo from "assets/oidc-logo.png"
import Auth0Logo from "assets/auth0-logo.png"
import MicrosoftLogo from "assets/microsoft-logo.png"
@ -20,7 +20,11 @@
}
onMount(async () => {
await oidc.init()
try {
await oidc.init()
} catch (error) {
notifications.error("Error getting OIDC config")
}
})
$: src = !$oidc.logo

View File

@ -25,7 +25,11 @@
}
onMount(async () => {
await organisation.init()
try {
await organisation.init()
} catch (error) {
notifications.error("Error getting org config")
}
})
</script>

View File

@ -2,6 +2,7 @@
import { redirect } from "@roxi/routify"
import { auth, admin } from "stores/portal"
import { onMount } from "svelte"
import { notifications } from "@budibase/bbui"
$: tenantSet = $auth.tenantSet
$: multiTenancyEnabled = $admin.multiTenancy
@ -17,8 +18,12 @@
}
onMount(async () => {
await admin.init()
await auth.checkQueryString()
try {
await admin.init()
await auth.checkQueryString()
} catch (error) {
notifications.error("Error getting checklist")
}
loaded = true
})
</script>

View File

@ -31,7 +31,6 @@
username,
password,
})
if ($auth?.user?.forceResetPassword) {
$goto("./reset")
} else {
@ -39,8 +38,7 @@
$goto("../portal")
}
} catch (err) {
console.error(err)
notifications.error(err.message ? err.message : "Invalid Credentials")
notifications.error(err.message ? err.message : "Invalid credentials")
}
}
@ -49,7 +47,11 @@
}
onMount(async () => {
await organisation.init()
try {
await organisation.init()
} catch (error) {
notifications.error("Error getting org config")
}
loaded = true
})
</script>

View File

@ -1,5 +1,13 @@
<script>
import { Body, Button, Divider, Heading, Input, Layout } from "@budibase/bbui"
import {
Body,
Button,
Divider,
Heading,
Input,
Layout,
notifications,
} from "@budibase/bbui"
import { goto } from "@roxi/routify"
import { auth, admin } from "stores/portal"
import Logo from "assets/bb-emblem.svg"
@ -13,13 +21,17 @@
$: useAccountPortal = cloud && !$admin.disableAccountPortal
async function setOrg() {
if (tenantId == null || tenantId === "") {
tenantId = "default"
try {
if (tenantId == null || tenantId === "") {
tenantId = "default"
}
await auth.setOrg(tenantId)
// re-init now org selected
await admin.init()
$goto("../")
} catch (error) {
notifications.error("Error setting organisation")
}
await auth.setOrg(tenantId)
// re-init now org selected
await admin.init()
$goto("../")
}
function handleKeydown(evt) {

View File

@ -31,7 +31,11 @@
}
onMount(async () => {
await organisation.init()
try {
await organisation.init()
} catch (error) {
notifications.error("Error getting org config")
}
})
</script>

View File

@ -10,14 +10,11 @@
async function acceptInvite() {
try {
const res = await users.acceptInvite(inviteCode, password)
if (!res) {
throw new Error(res.message)
}
notifications.success(`User created.`)
await users.acceptInvite(inviteCode, password)
notifications.success("Invitation accepted successfully")
$goto("../auth/login")
} catch (err) {
notifications.error(err)
} catch (error) {
notifications.error("Error accepting invitation")
}
}
</script>

View File

@ -10,6 +10,7 @@
MenuItem,
Modal,
clickOutside,
notifications,
} from "@budibase/bbui"
import ConfigChecklist from "components/common/ConfigChecklist.svelte"
import { organisation, auth } from "stores/portal"
@ -78,6 +79,14 @@
return menu
}
const logout = async () => {
try {
await auth.logout()
} catch (error) {
// Swallow error and do nothing
}
}
const showMobileMenu = () => (mobileMenuVisible = true)
const hideMobileMenu = () => (mobileMenuVisible = false)
@ -87,7 +96,11 @@
if (!$auth.user?.builder?.global) {
$redirect("../")
} else {
await organisation.init()
try {
await organisation.init()
} catch (error) {
notifications.error("Error getting org config")
}
loaded = true
}
}
@ -158,7 +171,7 @@
<MenuItem icon="UserDeveloper" on:click={() => $goto("../apps")}>
Close developer mode
</MenuItem>
<MenuItem icon="LogOut" on:click={auth.logout}>Log out</MenuItem>
<MenuItem icon="LogOut" on:click={logout}>Log out</MenuItem>
</ActionMenu>
</div>
</div>

View File

@ -139,7 +139,6 @@
$goto(`/builder/app/${createdApp.instance._id}`)
} catch (error) {
notifications.error("Error creating app")
console.error(error)
}
}
@ -248,17 +247,23 @@
}
onMount(async () => {
await apps.load()
await templates.load()
if ($templates?.length === 0) {
notifications.error("There was a problem loading quick start templates.")
}
// if the portal is loaded from an external URL with a template param
const initInfo = await auth.getInitInfo()
if (initInfo?.init_template) {
creatingFromTemplate = true
createAppFromTemplateUrl(initInfo.init_template)
return
try {
await apps.load()
await templates.load()
if ($templates?.length === 0) {
notifications.error(
"There was a problem loading quick start templates."
)
}
// If the portal is loaded from an external URL with a template param
const initInfo = await auth.getInitInfo()
if (initInfo?.init_template) {
creatingFromTemplate = true
createAppFromTemplateUrl(initInfo.init_template)
return
}
} catch (error) {
notifications.error("Error loading apps and templates")
}
loaded = true
})

View File

@ -207,15 +207,18 @@
notifications.success(`Settings saved`)
analytics.captureEvent(Events.SSO.SAVED)
})
.catch(error => {
notifications.error(`Failed to update auth settings`)
console.error(error.message)
.catch(() => {
notifications.error("Failed to update auth settings")
})
}
}
onMount(async () => {
await organisation.init()
try {
await organisation.init()
} catch (error) {
notifications.error("Error getting org config")
}
// Fetch Google config
let googleDoc

View File

@ -36,9 +36,9 @@
try {
// Save your template config
await email.templates.save(selectedTemplate)
notifications.success(`Template saved.`)
} catch (err) {
notifications.error(`Failed to update template settings. ${err}`)
notifications.success("Template saved")
} catch (error) {
notifications.error("Failed to update template settings")
}
}

View File

@ -1,7 +1,15 @@
<script>
import { onMount } from "svelte"
import { email } from "stores/portal"
import { notifications } from "@budibase/bbui"
email.templates.fetch()
onMount(async () => {
try {
await email.templates.fetch()
} catch (error) {
notifications.error("Error fetching email templates")
}
})
</script>
<slot />

View File

@ -64,31 +64,43 @@
const apps = fetchData(`/api/global/roles`)
async function deleteUser() {
const res = await users.delete(userId)
if (res.status === 200) {
try {
await users.delete(userId)
notifications.success(`User ${$userFetch?.data?.email} deleted.`)
$goto("./")
} else {
notifications.error(res?.message ? res.message : "Failed to delete user.")
} catch (error) {
notifications.error("Error deleting user")
}
}
let toggleDisabled = false
async function updateUserFirstName(evt) {
await users.save({ ...$userFetch?.data, firstName: evt.target.value })
await userFetch.refresh()
try {
await users.save({ ...$userFetch?.data, firstName: evt.target.value })
await userFetch.refresh()
} catch (error) {
notifications.error("Error updating user")
}
}
async function updateUserLastName(evt) {
await users.save({ ...$userFetch?.data, lastName: evt.target.value })
await userFetch.refresh()
try {
await users.save({ ...$userFetch?.data, lastName: evt.target.value })
await userFetch.refresh()
} catch (error) {
notifications.error("Error updating user")
}
}
async function toggleFlag(flagName, detail) {
toggleDisabled = true
await users.save({ ...$userFetch?.data, [flagName]: { global: detail } })
await userFetch.refresh()
try {
await users.save({ ...$userFetch?.data, [flagName]: { global: detail } })
await userFetch.refresh()
} catch (error) {
notifications.error("Error updating user")
}
toggleDisabled = false
}

View File

@ -21,12 +21,12 @@
const [email, error, touched] = createValidationStore("", emailValidator)
async function createUserFlow() {
const res = await users.invite({ email: $email, builder, admin })
if (res.status) {
notifications.error(res.message)
} else {
try {
const res = await users.invite({ email: $email, builder, admin })
notifications.success(res.message)
analytics.captureEvent(Events.USER.INVITE, { type: selected })
} catch (error) {
notifications.error("Error inviting user")
}
}
</script>

View File

@ -16,17 +16,17 @@
admin = false
async function createUser() {
const res = await users.create({
email: $email,
password,
builder,
admin,
forceResetPassword: true,
})
if (res.status) {
notifications.error(res.message)
} else {
try {
await users.create({
email: $email,
password,
builder,
admin,
forceResetPassword: true,
})
notifications.success("Successfully created user")
} catch (error) {
notifications.error("Error creating user")
}
}
</script>

View File

@ -10,16 +10,16 @@
const password = Math.random().toString(36).substr(2, 20)
async function resetPassword() {
const res = await users.save({
...user,
password,
forceResetPassword: true,
})
if (res.status) {
notifications.error(res.message)
} else {
notifications.success("Password reset.")
try {
await users.save({
...user,
password,
forceResetPassword: true,
})
notifications.success("Password reset successfully")
dispatch("update")
} catch (error) {
notifications.error("Error resetting password")
}
}
</script>

View File

@ -18,33 +18,31 @@
let selectedRole = user?.roles?.[app?._id]
async function updateUserRoles() {
let res
if (selectedRole === NO_ACCESS) {
// remove the user role
const filteredRoles = { ...user.roles }
delete filteredRoles[app?._id]
res = await users.save({
...user,
roles: {
...filteredRoles,
},
})
} else {
// add the user role
res = await users.save({
...user,
roles: {
...user.roles,
[app._id]: selectedRole,
},
})
}
if (res.status === 400) {
notifications.error("Failed to update role")
} else {
try {
if (selectedRole === NO_ACCESS) {
// Remove the user role
const filteredRoles = { ...user.roles }
delete filteredRoles[app?._id]
await users.save({
...user,
roles: {
...filteredRoles,
},
})
} else {
// Add the user role
await users.save({
...user,
roles: {
...user.roles,
[app._id]: selectedRole,
},
})
}
notifications.success("Role updated")
dispatch("update")
} catch (error) {
notifications.error("Failed to update role")
}
}
</script>

View File

@ -11,13 +11,13 @@
Label,
Layout,
Modal,
notifications,
} from "@budibase/bbui"
import TagsRenderer from "./_components/TagsTableRenderer.svelte"
import AddUserModal from "./_components/AddUserModal.svelte"
import BasicOnboardingModal from "./_components/BasicOnboardingModal.svelte"
import { users } from "stores/portal"
users.init()
import { onMount } from "svelte"
const schema = {
email: {},
@ -47,6 +47,14 @@
createUserModal.hide()
basicOnboardingModal.show()
}
onMount(async () => {
try {
await users.init()
} catch (error) {
notifications.error("Error getting user list")
}
})
</script>
<Layout noPadding>

View File

@ -44,24 +44,28 @@
async function saveConfig() {
loading = true
// Upload logo if required
if ($values.logo && !$values.logo.url) {
await uploadLogo($values.logo)
await organisation.init()
}
try {
// Upload logo if required
if ($values.logo && !$values.logo.url) {
await uploadLogo($values.logo)
await organisation.init()
}
const config = {
company: $values.company ?? "",
platformUrl: $values.platformUrl ?? "",
}
const config = {
company: $values.company ?? "",
platformUrl: $values.platformUrl ?? "",
}
// Remove logo if required
if (!$values.logo) {
config.logoUrl = ""
}
// Remove logo if required
if (!$values.logo) {
config.logoUrl = ""
}
// Update settings
await organisation.save(config)
// Update settings
await organisation.save(config)
} catch (error) {
notifications.error("Error saving org config")
}
loading = false
}
</script>

View File

@ -23,65 +23,37 @@ export function createAdminStore() {
const admin = writable(DEFAULT_CONFIG)
async function init() {
try {
const tenantId = get(auth).tenantId
const checklist = await API.getChecklist(tenantId)
const totalSteps = Object.keys(checklist).length
const completedSteps = Object.values(checklist).filter(
x => x?.checked
).length
await getEnvironment()
admin.update(store => {
store.loaded = true
store.checklist = checklist
store.onboardingProgress = (completedSteps / totalSteps) * 100
return store
})
} catch (error) {
admin.update(store => {
store.checklist = null
return store
})
}
const tenantId = get(auth).tenantId
const checklist = await API.getChecklist(tenantId)
const totalSteps = Object.keys(checklist).length
const completedSteps = Object.values(checklist).filter(
x => x?.checked
).length
await getEnvironment()
admin.update(store => {
store.loaded = true
store.checklist = checklist
store.onboardingProgress = (completedSteps / totalSteps) * 100
return store
})
}
async function checkImportComplete() {
try {
const result = await API.checkImportComplete()
admin.update(store => {
store.importComplete = result ? result.imported : false
return store
})
} catch (error) {
admin.update(store => {
store.importComplete = false
return store
})
}
const result = await API.checkImportComplete()
admin.update(store => {
store.importComplete = result ? result.imported : false
return store
})
}
async function getEnvironment() {
let multiTenancyEnabled = false
let cloud = false
let disableAccountPortal = false
let accountPortalUrl = ""
let isDev = false
try {
const environment = await API.getEnvironment()
multiTenancyEnabled = environment.multiTenancy
cloud = environment.cloud
disableAccountPortal = environment.disableAccountPortal
accountPortalUrl = environment.accountPortalUrl
isDev = environment.isDev
} catch (err) {
// Just let it stay disabled
}
const environment = await API.getEnvironment()
admin.update(store => {
store.multiTenancy = multiTenancyEnabled
store.cloud = cloud
store.disableAccountPortal = disableAccountPortal
store.accountPortalUrl = accountPortalUrl
store.isDev = isDev
store.multiTenancy = environment.multiTenancy
store.cloud = environment.cloud
store.disableAccountPortal = environment.disableAccountPortal
store.accountPortalUrl = environment.accountPortalUrl
store.isDev = environment.isDev
return store
})
}

View File

@ -1,7 +1,6 @@
import { writable } from "svelte/store"
import { get } from "builderStore/api"
import { AppStatus } from "../../constants"
import api from "../../builderStore/api"
import { API } from "api"
const extractAppId = id => {
const split = id?.split("_") || []
@ -12,77 +11,67 @@ export function createAppStore() {
const store = writable([])
async function load() {
try {
const res = await get(`/api/applications?status=all`)
const json = await res.json()
if (res.ok && Array.isArray(json)) {
// Merge apps into one sensible list
let appMap = {}
let devApps = json.filter(app => app.status === AppStatus.DEV)
let deployedApps = json.filter(app => app.status === AppStatus.DEPLOYED)
const json = await API.getApps()
if (Array.isArray(json)) {
// Merge apps into one sensible list
let appMap = {}
let devApps = json.filter(app => app.status === AppStatus.DEV)
let deployedApps = json.filter(app => app.status === AppStatus.DEPLOYED)
// First append all dev app version
devApps.forEach(app => {
const id = extractAppId(app.appId)
appMap[id] = {
...app,
devId: app.appId,
devRev: app._rev,
}
})
// First append all dev app version
devApps.forEach(app => {
const id = extractAppId(app.appId)
appMap[id] = {
...app,
devId: app.appId,
devRev: app._rev,
}
})
// Then merge with all prod app versions
deployedApps.forEach(app => {
const id = extractAppId(app.appId)
// Then merge with all prod app versions
deployedApps.forEach(app => {
const id = extractAppId(app.appId)
// Skip any deployed apps which don't have a dev counterpart
if (!appMap[id]) {
return
}
// Skip any deployed apps which don't have a dev counterpart
if (!appMap[id]) {
return
}
appMap[id] = {
...appMap[id],
...app,
prodId: app.appId,
prodRev: app._rev,
}
})
appMap[id] = {
...appMap[id],
...app,
prodId: app.appId,
prodRev: app._rev,
}
})
// Transform into an array and clean up
const apps = Object.values(appMap)
apps.forEach(app => {
app.appId = extractAppId(app.devId)
delete app._id
delete app._rev
})
store.set(apps)
} else {
store.set([])
}
return json
} catch (error) {
// Transform into an array and clean up
const apps = Object.values(appMap)
apps.forEach(app => {
app.appId = extractAppId(app.devId)
delete app._id
delete app._rev
})
store.set(apps)
} else {
store.set([])
}
}
async function update(appId, value) {
console.log({ value })
const response = await api.put(`/api/applications/${appId}`, { ...value })
if (response.status === 200) {
store.update(state => {
const updatedAppIndex = state.findIndex(
app => app.instance._id === appId
)
if (updatedAppIndex !== -1) {
let updatedApp = state[updatedAppIndex]
updatedApp = { ...updatedApp, ...value }
state.apps = state.splice(updatedAppIndex, 1, updatedApp)
}
return state
})
} else {
throw new Error("Error updating name")
}
await API.saveAppMetadata({
appId,
metadata: value,
})
store.update(state => {
const updatedAppIndex = state.findIndex(app => app.instance._id === appId)
if (updatedAppIndex !== -1) {
let updatedApp = state[updatedAppIndex]
updatedApp = { ...updatedApp, ...value }
state.apps = state.splice(updatedAppIndex, 1, updatedApp)
}
return state
})
}
return {

View File

@ -1,5 +1,5 @@
import { derived, writable, get } from "svelte/store"
import api from "../../builderStore/api"
import { API } from "api"
import { admin } from "stores/portal"
import analytics from "analytics"
@ -83,7 +83,7 @@ export function createAuthStore() {
}
async function setInitInfo(info) {
await api.post(`/api/global/auth/init`, info)
await API.setInitInfo(info)
auth.update(store => {
store.initInfo = info
return store
@ -99,13 +99,12 @@ export function createAuthStore() {
}
async function getInitInfo() {
const response = await api.get(`/api/global/auth/init`)
const json = response.json()
const info = await API.getInitInfo()
auth.update(store => {
store.initInfo = json
store.initInfo = info
return store
})
return json
return info
}
return {
@ -124,77 +123,43 @@ export function createAuthStore() {
await setOrganisation(tenantId)
},
checkAuth: async () => {
const response = await api.get("/api/global/users/self")
if (response.status !== 200) {
setUser(null)
} else {
const json = await response.json()
setUser(json)
}
const user = await API.fetchBuilderSelf()
setUser(user)
},
login: async creds => {
const tenantId = get(store).tenantId
const response = await api.post(
`/api/global/auth/${tenantId}/login`,
creds
)
const json = await response.json()
if (response.status === 200) {
setUser(json.user)
} else {
throw new Error(json.message ? json.message : "Invalid credentials")
}
return json
const response = await API.logIn({
username: creds.username,
password: creds.password,
tenantId,
})
setUser(response.user)
},
logout: async () => {
const response = await api.post(`/api/global/auth/logout`)
if (response.status !== 200) {
throw "Unable to create logout"
}
await response.json()
await API.logOut()
await setInitInfo({})
setUser(null)
setPostLogout()
},
updateSelf: async fields => {
const newUser = { ...get(auth).user, ...fields }
const response = await api.post("/api/global/users/self", newUser)
if (response.status === 200) {
setUser(newUser)
} else {
throw "Unable to update user details"
}
await API.updateSelf(newUser)
setUser(newUser)
},
forgotPassword: async email => {
const tenantId = get(store).tenantId
const response = await api.post(`/api/global/auth/${tenantId}/reset`, {
await API.requestForgotPassword({
tenantId,
email,
})
if (response.status !== 200) {
throw "Unable to send email with reset link"
}
await response.json()
},
resetPassword: async (password, code) => {
resetPassword: async (password, resetCode) => {
const tenantId = get(store).tenantId
const response = await api.post(
`/api/global/auth/${tenantId}/reset/update`,
{
password,
resetCode: code,
}
)
if (response.status !== 200) {
throw "Unable to reset password"
}
await response.json()
},
createUser: async user => {
const response = await api.post(`/api/global/users`, user)
if (response.status !== 200) {
throw "Unable to create user"
}
await response.json()
await API.resetPassword({
tenantId,
password,
resetCode,
})
},
}
}

View File

@ -1,6 +1,5 @@
import { writable } from "svelte/store"
import { API } from "api"
import { notifications } from "@budibase/bbui"
export function createEmailStore() {
const store = writable({})
@ -9,35 +8,26 @@ export function createEmailStore() {
subscribe: store.subscribe,
templates: {
fetch: async () => {
try {
// fetch the email template definitions and templates
const definitions = await API.getEmailTemplateDefinitions()
const templates = await API.getEmailTemplates()
store.set({
definitions,
templates,
})
} catch (error) {
notifications.error("Error fetching email templates")
store.set({})
}
// Fetch the email template definitions and templates
const definitions = await API.getEmailTemplateDefinitions()
const templates = await API.getEmailTemplates()
store.set({
definitions,
templates,
})
},
save: async template => {
try {
// Save your template config
const savedTemplate = await API.saveEmailTemplate(template)
template._rev = savedTemplate._rev
template._id = savedTemplate._id
store.update(state => {
const currentIdx = state.templates.findIndex(
template => template.purpose === savedTemplate.purpose
)
state.templates.splice(currentIdx, 1, template)
return state
})
} catch (error) {
notifications.error("Error saving email template")
}
// Save your template config
const savedTemplate = await API.saveEmailTemplate(template)
template._rev = savedTemplate._rev
template._id = savedTemplate._id
store.update(state => {
const currentIdx = state.templates.findIndex(
template => template.purpose === savedTemplate.purpose
)
state.templates.splice(currentIdx, 1, template)
return state
})
},
},
}

View File

@ -11,10 +11,11 @@ const OIDC_CONFIG = {
export function createOidcStore() {
const store = writable(OIDC_CONFIG)
const { set, subscribe } = store
async function init() {
const tenantId = get(auth).tenantId
try {
return {
subscribe,
set,
init: async () => {
const tenantId = get(auth).tenantId
const config = await API.getOIDCConfig(tenantId)
if (Object.keys(config || {}).length) {
// Just use the first config for now.
@ -23,15 +24,7 @@ export function createOidcStore() {
} else {
set(OIDC_CONFIG)
}
} catch (error) {
set(OIDC_CONFIG)
}
}
return {
subscribe,
set,
init,
},
}
}

View File

@ -1,5 +1,5 @@
import { writable, get } from "svelte/store"
import api from "builderStore/api"
import { API } from "api"
import { auth } from "stores/portal"
const DEFAULT_CONFIG = {
@ -19,35 +19,23 @@ export function createOrganisationStore() {
async function init() {
const tenantId = get(auth).tenantId
const res = await api.get(`/api/global/configs/public?tenantId=${tenantId}`)
const json = await res.json()
if (json.status === 400) {
set(DEFAULT_CONFIG)
} else {
set({ ...DEFAULT_CONFIG, ...json.config, _rev: json._rev })
}
const tenant = await API.getTenantConfig(tenantId)
set({ ...DEFAULT_CONFIG, ...tenant.config, _rev: tenant._rev })
}
async function save(config) {
// delete non-persisted fields
// Delete non-persisted fields
const storeConfig = get(store)
delete storeConfig.oidc
delete storeConfig.google
delete storeConfig.oidcCallbackUrl
delete storeConfig.googleCallbackUrl
const res = await api.post("/api/global/configs", {
await API.saveConfig({
type: "settings",
config: { ...get(store), ...config },
_rev: get(store)._rev,
})
const json = await res.json()
if (json.status) {
return json
}
await init()
return { status: 200 }
}
return {

View File

@ -1,18 +1,15 @@
import { writable } from "svelte/store"
import api from "builderStore/api"
import { API } from "api"
export function templatesStore() {
const { subscribe, set } = writable([])
async function load() {
const response = await api.get("/api/templates?type=app")
const json = await response.json()
set(json)
}
return {
subscribe,
load,
load: async () => {
const templates = await API.getAppTemplates()
set(templates)
},
}
}

View File

@ -1,38 +1,28 @@
import { writable } from "svelte/store"
import api, { post } from "builderStore/api"
import { API } from "api"
import { update } from "lodash"
export function createUsersStore() {
const { subscribe, set } = writable([])
async function init() {
const response = await api.get(`/api/global/users`)
const json = await response.json()
set(json)
const users = await API.getUsers()
set(users)
}
async function invite({ email, builder, admin }) {
const body = { email, userInfo: {} }
if (admin) {
body.userInfo.admin = {
global: true,
}
}
if (builder) {
body.userInfo.builder = {
global: true,
}
}
const response = await api.post(`/api/global/users/invite`, body)
return await response.json()
await API.inviteUser({
email,
builder,
admin,
})
}
async function acceptInvite(inviteCode, password) {
const response = await api.post("/api/global/users/invite/accept", {
await API.acceptInvite({
inviteCode,
password,
})
return await response.json()
}
async function create({
@ -56,29 +46,17 @@ export function createUsersStore() {
if (admin) {
body.admin = { global: true }
}
const response = await api.post("/api/global/users", body)
await API.saveUser(body)
await init()
return await response.json()
}
async function del(id) {
const response = await api.delete(`/api/global/users/${id}`)
await API.deleteUser(id)
update(users => users.filter(user => user._id !== id))
const json = await response.json()
return {
...json,
status: response.status,
}
}
async function save(data) {
try {
const res = await post(`/api/global/users`, data)
return await res.json()
} catch (error) {
console.log(error)
return error
}
await API.saveUser(data)
}
return {

View File

@ -10,14 +10,12 @@ export const buildAppEndpoints = API => ({
/**
* Saves and patches metadata about an app.
* @param appId the ID of the app to update
* @param metadata the app metadata to save
*/
saveAppMetadata: async metadata => {
if (!metadata?.appId) {
throw API.error("App metadata must have an appId set")
}
saveAppMetadata: async ({ appId, metadata }) => {
return await API.put({
url: `/api/applications/${metadata.appId}`,
url: `/api/applications/${appId}`,
body: metadata,
})
},
@ -132,4 +130,13 @@ export const buildAppEndpoints = API => ({
url: `/api/applications/${appId}/sync`,
})
},
/**
* Gets a list of apps.
*/
getApps: async () => {
return await API.get({
url: "/api/applications?status=all",
})
},
})

View File

@ -1,18 +1,15 @@
export const buildAuthEndpoints = API => ({
/**
* Performs a log in request.
* Performs a login request.
* @param tenantId the ID of the tenant to log in to
* @param username the username (email)
* @param password the password
*/
logIn: async ({ email, password }) => {
if (!email) {
return API.error("Please enter your email")
}
if (!password) {
return API.error("Please enter your password")
}
logIn: async ({ tenantId, username, password }) => {
return await API.post({
url: "/api/global/auth",
url: `/api/global/auth/${tenantId}/login`,
body: {
username: email,
username,
password,
},
})
@ -28,137 +25,52 @@ export const buildAuthEndpoints = API => ({
},
/**
* Fetches the currently logged in user object
* Sets initialisation info.
* @param info the info to set
*/
fetchSelf: async () => {
return await API.get({
url: "/api/self",
})
},
/**
* Creates a user for an app.
* @param user the user to create
*/
createAppUser: async user => {
setInitInfo: async info => {
return await API.post({
url: "/api/users/metadata",
body: user,
url: "/api/global/auth/init",
body: info,
})
},
/**
* Updates the current user metadata.
* @param metadata the metadata to save
* Gets the initialisation info.
*/
updateOwnMetadata: async metadata => {
getInitInfo: async () => {
return await API.get({
url: "/api/global/auth/init",
})
},
/**
* Sends a password reset email.
* @param tenantId the ID of the tenant the user is in
* @param email the email address of the user
*/
requestForgotPassword: async ({ tenantId, email }) => {
return await API.post({
url: "/api/users/metadata/self",
body: metadata,
url: `/api/global/auth/${tenantId}/reset`,
body: {
email,
},
})
},
/**
* Creates an admin user.
* @param adminUser the admin user to create
* Resets a user's password.
* @param tenantId the ID of the tenant the user is in
* @param password the new password to set
* @param resetCode the reset code to authenticate the request
*/
createAdminUser: async adminUser => {
resetPassword: async ({ tenantId, password, resetCode }) => {
return await API.post({
url: "/api/global/users/init",
body: adminUser,
})
},
/**
* Saves a global config.
* @param config the config to save
*/
saveConfig: async config => {
return await API.post({
url: "/api/global/configs",
body: config,
})
},
/**
* Gets a global config of a certain type.
* @param type the type to fetch
*/
getConfig: async type => {
return await API.get({
url: `/api/global/configs/${type}`,
})
},
/**
* Gets the OIDC config for a certain tenant.
* @param tenantId the tenant ID to get the config for
*/
getOIDCConfig: async tenantId => {
return await API.get({
url: `/api/global/configs/public/oidc?tenantId=${tenantId}`,
})
},
/**
* Gets the checklist for a specific tenant.
* @param tenantId the tenant ID to get the checklist for
*/
getChecklist: async tenantId => {
return await API.get({
url: `/api/global/configs/checklist?tenantId=${tenantId}`,
})
},
/**
* TODO: find out what this is
*/
checkImportComplete: async () => {
return await API.get({
url: "/api/cloud/import/complete",
})
},
/**
* Gets the current environment details.
*/
getEnvironment: async () => {
return await API.get({
url: "/api/system/environment",
})
},
/**
* Updates the company logo for the environment.
* @param data the logo form data
*/
uploadLogo: async data => {
return await API.post({
url: "/api/global/configs/upload/settings/logoUrl",
body: data,
json: false,
})
},
/**
* Uploads a logo for an OIDC provider.
* @param name the name of the OIDC provider
* @param data the logo form data to upload
*/
uploadOIDCLogo: async ({ name, data }) => {
return await API.post({
url: `/api/global/configs/upload/logos_oidc/${name}`,
body: data,
json: false,
})
},
/**
* Gets the list of OIDC logos.
*/
getOIDCLogos: async () => {
return await API.get({
url: "/api/global/configs/logos_oidc",
url: `/api/global/auth/${tenantId}/reset/update`,
body: {
password,
resetCode,
},
})
},
})

View File

@ -0,0 +1,86 @@
export const buildConfigEndpoints = API => ({
/**
* Saves a global config.
* @param config the config to save
*/
saveConfig: async config => {
return await API.post({
url: "/api/global/configs",
body: config,
})
},
/**
* Gets a global config of a certain type.
* @param type the type to fetch
*/
getConfig: async type => {
return await API.get({
url: `/api/global/configs/${type}`,
})
},
/**
* Gets the config for a certain tenant.
* @param tenantId the tenant ID to get the config for
*/
getTenantConfig: async tenantId => {
return await API.get({
url: `/api/global/configs/public?tenantId=${tenantId}`,
})
},
/**
* Gets the OIDC config for a certain tenant.
* @param tenantId the tenant ID to get the config for
*/
getOIDCConfig: async tenantId => {
return await API.get({
url: `/api/global/configs/public/oidc?tenantId=${tenantId}`,
})
},
/**
* Gets the checklist for a specific tenant.
* @param tenantId the tenant ID to get the checklist for
*/
getChecklist: async tenantId => {
return await API.get({
url: `/api/global/configs/checklist?tenantId=${tenantId}`,
})
},
/**
* Updates the company logo for the environment.
* @param data the logo form data
*/
uploadLogo: async data => {
return await API.post({
url: "/api/global/configs/upload/settings/logoUrl",
body: data,
json: false,
})
},
/**
* Uploads a logo for an OIDC provider.
* @param name the name of the OIDC provider
* @param data the logo form data to upload
*/
uploadOIDCLogo: async ({ name, data }) => {
return await API.post({
url: `/api/global/configs/upload/logos_oidc/${name}`,
body: data,
json: false,
})
},
/**
* Gets the list of OIDC logos.
*/
getOIDCLogos: async () => {
return await API.get({
url: "/api/global/configs/logos_oidc",
})
},
})

View File

@ -4,9 +4,11 @@ import { buildAppEndpoints } from "./app"
import { buildAttachmentEndpoints } from "./attachments"
import { buildAuthEndpoints } from "./auth"
import { buildAutomationEndpoints } from "./automations"
import { buildConfigEndpoints } from "./configs"
import { buildDatasourceEndpoints } from "./datasources"
import { buildFlagEndpoints } from "./flags"
import { buildHostingEndpoints } from "./hosting"
import { buildOtherEndpoints } from "./other"
import { buildPermissionsEndpoints } from "./permissions"
import { buildQueryEndpoints } from "./queries"
import { buildRelationshipEndpoints } from "./relationships"
@ -16,6 +18,7 @@ import { buildRowEndpoints } from "./rows"
import { buildScreenEndpoints } from "./screens"
import { buildTableEndpoints } from "./tables"
import { buildTemplateEndpoints } from "./templates"
import { buildUserEndpoints } from "./user"
import { buildViewEndpoints } from "./views"
const defaultAPIClientConfig = {
@ -189,9 +192,11 @@ export const createAPIClient = config => {
...buildAttachmentEndpoints(API),
...buildAuthEndpoints(API),
...buildAutomationEndpoints(API),
...buildConfigEndpoints(API),
...buildDatasourceEndpoints(API),
...buildFlagEndpoints(API),
...buildHostingEndpoints(API),
...buildOtherEndpoints(API),
...buildPermissionsEndpoints(API),
...buildQueryEndpoints(API),
...buildRelationshipEndpoints(API),
@ -201,6 +206,7 @@ export const createAPIClient = config => {
...buildScreenEndpoints(API),
...buildTableEndpoints(API),
...buildTemplateEndpoints(API),
...buildUserEndpoints(API),
...buildViewEndpoints(API),
}

View File

@ -0,0 +1,19 @@
export const buildOtherEndpoints = API => ({
/**
* TODO: find out what this is
*/
checkImportComplete: async () => {
return await API.get({
url: "/api/cloud/import/complete",
})
},
/**
* Gets the current environment details.
*/
getEnvironment: async () => {
return await API.get({
url: "/api/system/environment",
})
},
})

View File

@ -25,4 +25,13 @@ export const buildTemplateEndpoints = API => ({
},
})
},
/**
* Gets a list of app templates.
*/
getAppTemplates: async () => {
return await API.get({
url: "/api/templates?type=app",
})
},
})

View File

@ -0,0 +1,129 @@
export const buildUserEndpoints = API => ({
/**
* Fetches the currently logged-in user object.
* Used in client apps.
*/
fetchSelf: async () => {
return await API.get({
url: "/api/self",
})
},
/**
* Fetches the currently logged-in user object.
* Used in the builder.
*/
fetchBuilderSelf: async () => {
return await API.get({
url: "/api/global/users/self",
})
},
/**
* Gets a list of users in the current tenant.
*/
getUsers: async () => {
return await API.get({
url: "/api/global/users",
})
},
/**
* Creates a user for an app.
* @param user the user to create
*/
createAppUser: async user => {
return await API.post({
url: "/api/users/metadata",
body: user,
})
},
/**
* Updates the current user metadata.
* @param metadata the metadata to save
*/
updateOwnMetadata: async metadata => {
return await API.post({
url: "/api/users/metadata/self",
body: metadata,
})
},
/**
* Creates an admin user.
* @param adminUser the admin user to create
*/
createAdminUser: async adminUser => {
return await API.post({
url: "/api/global/users/init",
body: adminUser,
})
},
/**
* Updates the current logged-in user.
* @param user the new user object to save
*/
updateSelf: async user => {
return await API.post({
url: "/api/global/users/self",
body: user,
})
},
/**
* Creates or updates a user in the current tenant.
* @param user the new user to create
*/
saveUser: async user => {
return await API.post({
url: "/api/global/users",
body: user,
})
},
/**
* Deletes a user from the curernt tenant.
* @param userId the ID of the user to delete
*/
deleteUser: async userId => {
return await API.delete({
url: `/api/global/users/${userId}`,
})
},
/**
* Invites a user to the current tenant.
* @param email the email address to send the invitation to
* @param builder whether the user should be a global builder
* @param admin whether the user should be a global admin
*/
inviteUser: async ({ email, builder, admin }) => {
return await API.post({
url: "/api/global/users/invite",
body: {
email,
userInfo: {
admin: admin ? { global: true } : undefined,
builder: builder ? { global: true } : undefined,
},
},
})
},
/**
* Accepts an invitation to join the platform and creates a user.
* @param inviteCode the invite code sent in the email
* @param password the password for the newly created user
*/
acceptInvitation: async ({ inviteCode, password }) => {
return await API.post({
url: "/api/global/users/invite/accept",
body: {
inviteCode,
password,
},
})
},
})