Merge pull request from Budibase/ts-portal-auth-store

Convert portal auth store to typescript
This commit is contained in:
Andrew Kingston 2024-12-17 10:49:07 +00:00 committed by GitHub
commit 5b835c51bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 176 additions and 176 deletions
packages
builder/src
pages/builder
stores/portal
types/src/documents/global

View File

@ -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

View File

@ -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 {

View File

@ -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) {

View File

@ -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,

View File

@ -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()

View File

@ -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()

View File

@ -70,6 +70,8 @@ export interface User extends Document {
appFavourites?: string[]
ssoId?: string
appSort?: string
budibaseAccess?: boolean
accountPortalAccess?: boolean
}
export interface UserBindings extends Document {