diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index 9b3a95d7f7..5524cb5991 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -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 diff --git a/packages/builder/src/components/feedback/NPSFeedbackForm.svelte b/packages/builder/src/components/feedback/NPSFeedbackForm.svelte index 4c5bb46c63..6a6e52ec74 100644 --- a/packages/builder/src/components/feedback/NPSFeedbackForm.svelte +++ b/packages/builder/src/components/feedback/NPSFeedbackForm.svelte @@ -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") } diff --git a/packages/builder/src/components/start/ChooseIconModal.svelte b/packages/builder/src/components/start/ChooseIconModal.svelte index 4efb679a51..b2f68c6ce7 100644 --- a/packages/builder/src/components/start/ChooseIconModal.svelte +++ b/packages/builder/src/components/start/ChooseIconModal.svelte @@ -1,5 +1,12 @@ diff --git a/packages/builder/src/components/start/CreateAppModal.svelte b/packages/builder/src/components/start/CreateAppModal.svelte index ebb24e9e2f..97e0fd5715 100644 --- a/packages/builder/src/components/start/CreateAppModal.svelte +++ b/packages/builder/src/components/start/CreateAppModal.svelte @@ -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") + } } diff --git a/packages/builder/src/components/start/UpdateAppModal.svelte b/packages/builder/src/components/start/UpdateAppModal.svelte index 432b13c7c3..508a0723b1 100644 --- a/packages/builder/src/components/start/UpdateAppModal.svelte +++ b/packages/builder/src/components/start/UpdateAppModal.svelte @@ -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") } } diff --git a/packages/builder/src/pages/builder/_layout.svelte b/packages/builder/src/pages/builder/_layout.svelte index 31f1d18d56..10779b2491 100644 --- a/packages/builder/src/pages/builder/_layout.svelte +++ b/packages/builder/src/pages/builder/_layout.svelte @@ -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 }) diff --git a/packages/builder/src/pages/builder/admin/index.svelte b/packages/builder/src/pages/builder/admin/index.svelte index d42197a130..99731b8285 100644 --- a/packages/builder/src/pages/builder/admin/index.svelte +++ b/packages/builder/src/pages/builder/admin/index.svelte @@ -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") + } } }) diff --git a/packages/builder/src/pages/builder/apps/index.svelte b/packages/builder/src/pages/builder/apps/index.svelte index aafc28cd92..b4655d8f04 100644 --- a/packages/builder/src/pages/builder/apps/index.svelte +++ b/packages/builder/src/pages/builder/apps/index.svelte @@ -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 + } + } {#if $auth.user && loaded} @@ -79,7 +92,7 @@ Open developer mode {/if} - Log out + Log out diff --git a/packages/builder/src/pages/builder/auth/_components/OIDCButton.svelte b/packages/builder/src/pages/builder/auth/_components/OIDCButton.svelte index bae68b6548..27f5bde186 100644 --- a/packages/builder/src/pages/builder/auth/_components/OIDCButton.svelte +++ b/packages/builder/src/pages/builder/auth/_components/OIDCButton.svelte @@ -1,5 +1,5 @@ diff --git a/packages/builder/src/pages/builder/auth/index.svelte b/packages/builder/src/pages/builder/auth/index.svelte index a2a02e65c1..72b3a8c7cf 100644 --- a/packages/builder/src/pages/builder/auth/index.svelte +++ b/packages/builder/src/pages/builder/auth/index.svelte @@ -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 }) diff --git a/packages/builder/src/pages/builder/auth/login.svelte b/packages/builder/src/pages/builder/auth/login.svelte index 7a13164c51..d9151b4342 100644 --- a/packages/builder/src/pages/builder/auth/login.svelte +++ b/packages/builder/src/pages/builder/auth/login.svelte @@ -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 }) diff --git a/packages/builder/src/pages/builder/auth/org.svelte b/packages/builder/src/pages/builder/auth/org.svelte index 5a484b6c93..8fd94463d9 100644 --- a/packages/builder/src/pages/builder/auth/org.svelte +++ b/packages/builder/src/pages/builder/auth/org.svelte @@ -1,5 +1,13 @@ diff --git a/packages/builder/src/pages/builder/invite/index.svelte b/packages/builder/src/pages/builder/invite/index.svelte index ddf888ad73..c4745d8737 100644 --- a/packages/builder/src/pages/builder/invite/index.svelte +++ b/packages/builder/src/pages/builder/invite/index.svelte @@ -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") } } diff --git a/packages/builder/src/pages/builder/portal/_layout.svelte b/packages/builder/src/pages/builder/portal/_layout.svelte index 8fca18d29d..f4679647ff 100644 --- a/packages/builder/src/pages/builder/portal/_layout.svelte +++ b/packages/builder/src/pages/builder/portal/_layout.svelte @@ -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 @@ $goto("../apps")}> Close developer mode - Log out + Log out diff --git a/packages/builder/src/pages/builder/portal/apps/index.svelte b/packages/builder/src/pages/builder/portal/apps/index.svelte index ec111e046e..e71dc325fb 100644 --- a/packages/builder/src/pages/builder/portal/apps/index.svelte +++ b/packages/builder/src/pages/builder/portal/apps/index.svelte @@ -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 }) diff --git a/packages/builder/src/pages/builder/portal/manage/auth/index.svelte b/packages/builder/src/pages/builder/portal/manage/auth/index.svelte index bdbf884c99..b001f02fe9 100644 --- a/packages/builder/src/pages/builder/portal/manage/auth/index.svelte +++ b/packages/builder/src/pages/builder/portal/manage/auth/index.svelte @@ -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 diff --git a/packages/builder/src/pages/builder/portal/manage/email/[template].svelte b/packages/builder/src/pages/builder/portal/manage/email/[template].svelte index cc00f3d798..33ecca2a10 100644 --- a/packages/builder/src/pages/builder/portal/manage/email/[template].svelte +++ b/packages/builder/src/pages/builder/portal/manage/email/[template].svelte @@ -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") } } diff --git a/packages/builder/src/pages/builder/portal/manage/email/_layout.svelte b/packages/builder/src/pages/builder/portal/manage/email/_layout.svelte index 410a7d4ff2..e371c2daae 100644 --- a/packages/builder/src/pages/builder/portal/manage/email/_layout.svelte +++ b/packages/builder/src/pages/builder/portal/manage/email/_layout.svelte @@ -1,7 +1,15 @@ diff --git a/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte b/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte index 549d0e4334..a8cb340465 100644 --- a/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte +++ b/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte @@ -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 } diff --git a/packages/builder/src/pages/builder/portal/manage/users/_components/AddUserModal.svelte b/packages/builder/src/pages/builder/portal/manage/users/_components/AddUserModal.svelte index 25a69af1c8..0255784a7b 100644 --- a/packages/builder/src/pages/builder/portal/manage/users/_components/AddUserModal.svelte +++ b/packages/builder/src/pages/builder/portal/manage/users/_components/AddUserModal.svelte @@ -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") } } diff --git a/packages/builder/src/pages/builder/portal/manage/users/_components/BasicOnboardingModal.svelte b/packages/builder/src/pages/builder/portal/manage/users/_components/BasicOnboardingModal.svelte index ff958d542b..29e2d56ed0 100644 --- a/packages/builder/src/pages/builder/portal/manage/users/_components/BasicOnboardingModal.svelte +++ b/packages/builder/src/pages/builder/portal/manage/users/_components/BasicOnboardingModal.svelte @@ -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") } } diff --git a/packages/builder/src/pages/builder/portal/manage/users/_components/ForceResetPasswordModal.svelte b/packages/builder/src/pages/builder/portal/manage/users/_components/ForceResetPasswordModal.svelte index 6468498df8..a380f0aa65 100644 --- a/packages/builder/src/pages/builder/portal/manage/users/_components/ForceResetPasswordModal.svelte +++ b/packages/builder/src/pages/builder/portal/manage/users/_components/ForceResetPasswordModal.svelte @@ -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") } } diff --git a/packages/builder/src/pages/builder/portal/manage/users/_components/UpdateRolesModal.svelte b/packages/builder/src/pages/builder/portal/manage/users/_components/UpdateRolesModal.svelte index afa4c84f0e..5a60bfdff8 100644 --- a/packages/builder/src/pages/builder/portal/manage/users/_components/UpdateRolesModal.svelte +++ b/packages/builder/src/pages/builder/portal/manage/users/_components/UpdateRolesModal.svelte @@ -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") } } diff --git a/packages/builder/src/pages/builder/portal/manage/users/index.svelte b/packages/builder/src/pages/builder/portal/manage/users/index.svelte index 124115a486..61192063cc 100644 --- a/packages/builder/src/pages/builder/portal/manage/users/index.svelte +++ b/packages/builder/src/pages/builder/portal/manage/users/index.svelte @@ -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") + } + }) diff --git a/packages/builder/src/pages/builder/portal/settings/organisation.svelte b/packages/builder/src/pages/builder/portal/settings/organisation.svelte index c9086b2b45..7094a0af01 100644 --- a/packages/builder/src/pages/builder/portal/settings/organisation.svelte +++ b/packages/builder/src/pages/builder/portal/settings/organisation.svelte @@ -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 } diff --git a/packages/builder/src/stores/portal/admin.js b/packages/builder/src/stores/portal/admin.js index d97105fd46..dc68c43cc5 100644 --- a/packages/builder/src/stores/portal/admin.js +++ b/packages/builder/src/stores/portal/admin.js @@ -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 }) } diff --git a/packages/builder/src/stores/portal/apps.js b/packages/builder/src/stores/portal/apps.js index de944c057d..b8fb8c5670 100644 --- a/packages/builder/src/stores/portal/apps.js +++ b/packages/builder/src/stores/portal/apps.js @@ -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 { diff --git a/packages/builder/src/stores/portal/auth.js b/packages/builder/src/stores/portal/auth.js index bdd4d95915..3895b57e82 100644 --- a/packages/builder/src/stores/portal/auth.js +++ b/packages/builder/src/stores/portal/auth.js @@ -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, + }) }, } } diff --git a/packages/builder/src/stores/portal/email.js b/packages/builder/src/stores/portal/email.js index 5bef63701f..2e222d34c4 100644 --- a/packages/builder/src/stores/portal/email.js +++ b/packages/builder/src/stores/portal/email.js @@ -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 + }) }, }, } diff --git a/packages/builder/src/stores/portal/oidc.js b/packages/builder/src/stores/portal/oidc.js index 751988bff6..3a4b954753 100644 --- a/packages/builder/src/stores/portal/oidc.js +++ b/packages/builder/src/stores/portal/oidc.js @@ -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, + }, } } diff --git a/packages/builder/src/stores/portal/organisation.js b/packages/builder/src/stores/portal/organisation.js index 21a110c54a..9709578fa2 100644 --- a/packages/builder/src/stores/portal/organisation.js +++ b/packages/builder/src/stores/portal/organisation.js @@ -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 { diff --git a/packages/builder/src/stores/portal/templates.js b/packages/builder/src/stores/portal/templates.js index b82ecd70e2..904e9cfa8e 100644 --- a/packages/builder/src/stores/portal/templates.js +++ b/packages/builder/src/stores/portal/templates.js @@ -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) + }, } } diff --git a/packages/builder/src/stores/portal/users.js b/packages/builder/src/stores/portal/users.js index 9a3df120e0..cebf03d4c0 100644 --- a/packages/builder/src/stores/portal/users.js +++ b/packages/builder/src/stores/portal/users.js @@ -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 { diff --git a/packages/frontend-core/src/api/app.js b/packages/frontend-core/src/api/app.js index 543dcd58ee..1b0735be05 100644 --- a/packages/frontend-core/src/api/app.js +++ b/packages/frontend-core/src/api/app.js @@ -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", + }) + }, }) diff --git a/packages/frontend-core/src/api/auth.js b/packages/frontend-core/src/api/auth.js index 5f280b50da..9289d71239 100644 --- a/packages/frontend-core/src/api/auth.js +++ b/packages/frontend-core/src/api/auth.js @@ -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, + }, }) }, }) diff --git a/packages/frontend-core/src/api/configs.js b/packages/frontend-core/src/api/configs.js new file mode 100644 index 0000000000..9e320f7499 --- /dev/null +++ b/packages/frontend-core/src/api/configs.js @@ -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", + }) + }, +}) diff --git a/packages/frontend-core/src/api/index.js b/packages/frontend-core/src/api/index.js index 9511a946b4..973a1fbc2f 100644 --- a/packages/frontend-core/src/api/index.js +++ b/packages/frontend-core/src/api/index.js @@ -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), } diff --git a/packages/frontend-core/src/api/other.js b/packages/frontend-core/src/api/other.js new file mode 100644 index 0000000000..b3481f1e27 --- /dev/null +++ b/packages/frontend-core/src/api/other.js @@ -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", + }) + }, +}) diff --git a/packages/frontend-core/src/api/templates.js b/packages/frontend-core/src/api/templates.js index 7cd1d78d7b..720110fe5d 100644 --- a/packages/frontend-core/src/api/templates.js +++ b/packages/frontend-core/src/api/templates.js @@ -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", + }) + }, }) diff --git a/packages/frontend-core/src/api/user.js b/packages/frontend-core/src/api/user.js new file mode 100644 index 0000000000..d1fe7f7251 --- /dev/null +++ b/packages/frontend-core/src/api/user.js @@ -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, + }, + }) + }, +})