Merge pull request #15181 from Budibase/ts-portal-auth-store
Convert portal auth store to typescript
This commit is contained in:
commit
5b835c51bf
packages
builder/src
types/src/documents/global
|
@ -36,10 +36,7 @@
|
|||
await API.createAdminUser(adminUser)
|
||||
notifications.success("Admin user created")
|
||||
await admin.init()
|
||||
await auth.login({
|
||||
username: formData?.email.trim(),
|
||||
password: formData?.password,
|
||||
})
|
||||
await auth.login(formData?.email.trim(), formData?.password)
|
||||
$goto("../portal")
|
||||
} catch (error) {
|
||||
submitted = false
|
||||
|
|
|
@ -35,10 +35,7 @@
|
|||
return
|
||||
}
|
||||
try {
|
||||
await auth.login({
|
||||
username: formData?.username.trim(),
|
||||
password: formData?.password,
|
||||
})
|
||||
await auth.login(formData?.username.trim(), formData?.password)
|
||||
if ($auth?.user?.forceResetPassword) {
|
||||
$goto("./reset")
|
||||
} else {
|
||||
|
|
|
@ -66,10 +66,7 @@
|
|||
|
||||
async function login() {
|
||||
try {
|
||||
await auth.login({
|
||||
username: formData.email.trim(),
|
||||
password: formData.password.trim(),
|
||||
})
|
||||
await auth.login(formData.email.trim(), formData.password.trim())
|
||||
notifications.success("Logged in successfully")
|
||||
$goto("../portal")
|
||||
} catch (err) {
|
||||
|
|
|
@ -4,7 +4,7 @@ import { AppStatus } from "constants"
|
|||
import { API } from "api"
|
||||
import { auth } from "./auth"
|
||||
import BudiStore from "../BudiStore"
|
||||
import { App, UpdateAppRequest, User } from "@budibase/types"
|
||||
import { App, UpdateAppRequest } from "@budibase/types"
|
||||
|
||||
interface AppIdentifierMetadata {
|
||||
devId?: string
|
||||
|
@ -174,7 +174,7 @@ export class AppsStore extends BudiStore<PortalAppsStore> {
|
|||
export const appsStore = new AppsStore()
|
||||
|
||||
export const sortBy = derived([appsStore, auth], ([$store, $auth]) => {
|
||||
return $store.sortBy || ($auth.user as User | null)?.appSort || "name"
|
||||
return $store.sortBy || $auth.user?.appSort || "name"
|
||||
})
|
||||
|
||||
// Centralise any logic that enriches the apps list
|
||||
|
@ -182,7 +182,7 @@ export const enrichedApps = derived(
|
|||
[appsStore, auth, sortBy],
|
||||
([$store, $auth, $sortBy]) => {
|
||||
const enrichedApps: EnrichedApp[] = $store.apps.map(app => {
|
||||
const user = $auth.user as User | null
|
||||
const user = $auth.user
|
||||
return {
|
||||
...app,
|
||||
deployed: app.status === AppStatus.DEPLOYED,
|
||||
|
|
|
@ -1,161 +0,0 @@
|
|||
import { derived, writable, get } from "svelte/store"
|
||||
import { API } from "api"
|
||||
import { admin } from "stores/portal"
|
||||
import analytics from "analytics"
|
||||
|
||||
export function createAuthStore() {
|
||||
const auth = writable({
|
||||
user: null,
|
||||
accountPortalAccess: false,
|
||||
tenantId: "default",
|
||||
tenantSet: false,
|
||||
loaded: false,
|
||||
postLogout: false,
|
||||
})
|
||||
const store = derived(auth, $store => {
|
||||
return {
|
||||
user: $store.user,
|
||||
accountPortalAccess: $store.accountPortalAccess,
|
||||
tenantId: $store.tenantId,
|
||||
tenantSet: $store.tenantSet,
|
||||
loaded: $store.loaded,
|
||||
postLogout: $store.postLogout,
|
||||
isSSO: !!$store.user?.provider,
|
||||
}
|
||||
})
|
||||
|
||||
function setUser(user) {
|
||||
auth.update(store => {
|
||||
store.loaded = true
|
||||
store.user = user
|
||||
store.accountPortalAccess = user?.accountPortalAccess
|
||||
if (user) {
|
||||
store.tenantId = user.tenantId || "default"
|
||||
store.tenantSet = true
|
||||
}
|
||||
return store
|
||||
})
|
||||
|
||||
if (user) {
|
||||
analytics
|
||||
.activate()
|
||||
.then(() => {
|
||||
analytics.identify(user._id)
|
||||
})
|
||||
.catch(() => {
|
||||
// This request may fail due to browser extensions blocking requests
|
||||
// containing the word analytics, so we don't want to spam users with
|
||||
// an error here.
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function setOrganisation(tenantId) {
|
||||
const prevId = get(store).tenantId
|
||||
auth.update(store => {
|
||||
store.tenantId = tenantId
|
||||
store.tenantSet = !!tenantId
|
||||
return store
|
||||
})
|
||||
if (prevId !== tenantId) {
|
||||
// re-init admin after setting org
|
||||
await admin.init()
|
||||
}
|
||||
}
|
||||
|
||||
async function setInitInfo(info) {
|
||||
await API.setInitInfo(info)
|
||||
auth.update(store => {
|
||||
store.initInfo = info
|
||||
return store
|
||||
})
|
||||
return info
|
||||
}
|
||||
|
||||
function setPostLogout() {
|
||||
auth.update(store => {
|
||||
store.postLogout = true
|
||||
return store
|
||||
})
|
||||
}
|
||||
|
||||
async function getInitInfo() {
|
||||
const info = await API.getInitInfo()
|
||||
auth.update(store => {
|
||||
store.initInfo = info
|
||||
return store
|
||||
})
|
||||
return info
|
||||
}
|
||||
|
||||
const actions = {
|
||||
checkQueryString: async () => {
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
if (urlParams.has("tenantId")) {
|
||||
const tenantId = urlParams.get("tenantId")
|
||||
await setOrganisation(tenantId)
|
||||
}
|
||||
},
|
||||
setOrg: async tenantId => {
|
||||
await setOrganisation(tenantId)
|
||||
},
|
||||
getSelf: async () => {
|
||||
// We need to catch this locally as we never want this to fail, even
|
||||
// though normally we never want to swallow API errors at the store level.
|
||||
// We're either logged in or we aren't.
|
||||
// We also need to always update the loaded flag.
|
||||
try {
|
||||
const user = await API.fetchBuilderSelf()
|
||||
setUser(user)
|
||||
} catch (error) {
|
||||
setUser(null)
|
||||
}
|
||||
},
|
||||
login: async creds => {
|
||||
const tenantId = get(store).tenantId
|
||||
await API.logIn(tenantId, creds.username, creds.password)
|
||||
await actions.getSelf()
|
||||
},
|
||||
logout: async () => {
|
||||
await API.logOut()
|
||||
setPostLogout()
|
||||
setUser(null)
|
||||
await setInitInfo({})
|
||||
},
|
||||
updateSelf: async fields => {
|
||||
await API.updateSelf({ ...fields })
|
||||
// Refetch to enrich after update.
|
||||
try {
|
||||
const user = await API.fetchBuilderSelf()
|
||||
setUser(user)
|
||||
} catch (error) {
|
||||
setUser(null)
|
||||
}
|
||||
},
|
||||
forgotPassword: async email => {
|
||||
const tenantId = get(store).tenantId
|
||||
await API.requestForgotPassword(tenantId, email)
|
||||
},
|
||||
resetPassword: async (password, resetCode) => {
|
||||
const tenantId = get(store).tenantId
|
||||
await API.resetPassword(tenantId, password, resetCode)
|
||||
},
|
||||
generateAPIKey: async () => {
|
||||
return API.generateAPIKey()
|
||||
},
|
||||
fetchAPIKey: async () => {
|
||||
const info = await API.fetchDeveloperInfo()
|
||||
return info?.apiKey
|
||||
},
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe: store.subscribe,
|
||||
setOrganisation,
|
||||
getInitInfo,
|
||||
setInitInfo,
|
||||
...actions,
|
||||
}
|
||||
}
|
||||
|
||||
export const auth = createAuthStore()
|
|
@ -0,0 +1,168 @@
|
|||
import { get } from "svelte/store"
|
||||
import { API } from "api"
|
||||
import { admin } from "stores/portal"
|
||||
import analytics from "analytics"
|
||||
import BudiStore from "stores/BudiStore"
|
||||
import {
|
||||
isSSOUser,
|
||||
SetInitInfoRequest,
|
||||
UpdateSelfRequest,
|
||||
User,
|
||||
} from "@budibase/types"
|
||||
|
||||
interface PortalAuthStore {
|
||||
user?: User
|
||||
initInfo?: Record<string, any>
|
||||
accountPortalAccess: boolean
|
||||
loaded: boolean
|
||||
isSSO: boolean
|
||||
tenantId: string
|
||||
tenantSet: boolean
|
||||
postLogout: boolean
|
||||
}
|
||||
|
||||
class AuthStore extends BudiStore<PortalAuthStore> {
|
||||
constructor() {
|
||||
super({
|
||||
accountPortalAccess: false,
|
||||
tenantId: "default",
|
||||
tenantSet: false,
|
||||
loaded: false,
|
||||
postLogout: false,
|
||||
isSSO: false,
|
||||
})
|
||||
}
|
||||
|
||||
setUser(user?: User) {
|
||||
this.set({
|
||||
loaded: true,
|
||||
user: user,
|
||||
accountPortalAccess: !!user?.accountPortalAccess,
|
||||
tenantId: user?.tenantId || "default",
|
||||
tenantSet: !!user,
|
||||
isSSO: user != null && isSSOUser(user),
|
||||
postLogout: false,
|
||||
})
|
||||
|
||||
if (user) {
|
||||
analytics
|
||||
.activate()
|
||||
.then(() => {
|
||||
analytics.identify(user._id)
|
||||
})
|
||||
.catch(() => {
|
||||
// This request may fail due to browser extensions blocking requests
|
||||
// containing the word analytics, so we don't want to spam users with
|
||||
// an error here.
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async setOrganisation(tenantId: string) {
|
||||
const prevId = get(this.store).tenantId
|
||||
auth.update(store => {
|
||||
store.tenantId = tenantId
|
||||
store.tenantSet = !!tenantId
|
||||
return store
|
||||
})
|
||||
if (prevId !== tenantId) {
|
||||
// re-init admin after setting org
|
||||
await admin.init()
|
||||
}
|
||||
}
|
||||
|
||||
async setInitInfo(info: SetInitInfoRequest) {
|
||||
await API.setInitInfo(info)
|
||||
auth.update(store => {
|
||||
store.initInfo = info
|
||||
return store
|
||||
})
|
||||
return info
|
||||
}
|
||||
|
||||
setPostLogout() {
|
||||
auth.update(store => {
|
||||
store.postLogout = true
|
||||
return store
|
||||
})
|
||||
}
|
||||
|
||||
async getInitInfo() {
|
||||
const info = await API.getInitInfo()
|
||||
auth.update(store => {
|
||||
store.initInfo = info
|
||||
return store
|
||||
})
|
||||
return info
|
||||
}
|
||||
|
||||
async checkQueryString() {
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
const tenantId = urlParams.get("tenantId")
|
||||
if (tenantId) {
|
||||
await this.setOrganisation(tenantId)
|
||||
}
|
||||
}
|
||||
|
||||
async setOrg(tenantId: string) {
|
||||
await this.setOrganisation(tenantId)
|
||||
}
|
||||
|
||||
async getSelf() {
|
||||
// We need to catch this locally as we never want this to fail, even
|
||||
// though normally we never want to swallow API errors at the store level.
|
||||
// We're either logged in or we aren't.
|
||||
// We also need to always update the loaded flag.
|
||||
try {
|
||||
const user = await API.fetchBuilderSelf()
|
||||
this.setUser(user)
|
||||
} catch (error) {
|
||||
this.setUser()
|
||||
}
|
||||
}
|
||||
|
||||
async login(username: string, password: string) {
|
||||
const tenantId = get(this.store).tenantId
|
||||
await API.logIn(tenantId, username, password)
|
||||
await this.getSelf()
|
||||
}
|
||||
|
||||
async logout() {
|
||||
await API.logOut()
|
||||
this.setPostLogout()
|
||||
this.setUser()
|
||||
await this.setInitInfo({})
|
||||
}
|
||||
|
||||
async updateSelf(fields: UpdateSelfRequest) {
|
||||
await API.updateSelf(fields)
|
||||
// Refetch to enrich after update.
|
||||
try {
|
||||
const user = await API.fetchBuilderSelf()
|
||||
this.setUser(user)
|
||||
} catch (error) {
|
||||
this.setUser()
|
||||
}
|
||||
}
|
||||
|
||||
async forgotPassword(email: string) {
|
||||
const tenantId = get(this.store).tenantId
|
||||
await API.requestForgotPassword(tenantId, email)
|
||||
}
|
||||
|
||||
async resetPassword(password: string, resetCode: string) {
|
||||
const tenantId = get(this.store).tenantId
|
||||
await API.resetPassword(tenantId, password, resetCode)
|
||||
}
|
||||
|
||||
async generateAPIKey() {
|
||||
return API.generateAPIKey()
|
||||
}
|
||||
|
||||
async fetchAPIKey() {
|
||||
const info = await API.fetchDeveloperInfo()
|
||||
return info?.apiKey
|
||||
}
|
||||
}
|
||||
|
||||
export const auth = new AuthStore()
|
|
@ -70,6 +70,8 @@ export interface User extends Document {
|
|||
appFavourites?: string[]
|
||||
ssoId?: string
|
||||
appSort?: string
|
||||
budibaseAccess?: boolean
|
||||
accountPortalAccess?: boolean
|
||||
}
|
||||
|
||||
export interface UserBindings extends Document {
|
||||
|
|
Loading…
Reference in New Issue