From 6dc0ebcac6bc0e0cd1bb2bd2b0b83338ba0d29e2 Mon Sep 17 00:00:00 2001 From: MaysWind Date: Sun, 5 Jan 2025 21:39:39 +0800 Subject: [PATCH] code refactor --- src/DesktopApp.vue | 5 +- src/MobileApp.vue | 5 +- src/desktop-main.js | 3 - src/lib/services.ts | 17 +- src/lib/userstate.ts | 265 +++++++++--------- src/mobile-main.js | 3 - src/router/desktop.js | 14 +- src/router/mobile.js | 14 +- src/stores/index.js | 47 ++-- src/stores/token.js | 4 +- src/stores/transaction.js | 14 +- src/stores/twoFactorAuth.js | 4 +- src/stores/user.js | 12 +- src/views/desktop/HomePage.vue | 3 +- src/views/desktop/SignupPage.vue | 3 +- src/views/desktop/UnlockPage.vue | 16 +- src/views/desktop/VerifyEmailPage.vue | 5 +- .../app/settings/tabs/AppLockSettingTab.vue | 26 +- src/views/mobile/ApplicationLockPage.vue | 26 +- src/views/mobile/HomePage.vue | 3 +- src/views/mobile/SignupPage.vue | 3 +- src/views/mobile/UnlockPage.vue | 16 +- 22 files changed, 279 insertions(+), 229 deletions(-) diff --git a/src/DesktopApp.vue b/src/DesktopApp.vue index 9057dba0..656459f7 100644 --- a/src/DesktopApp.vue +++ b/src/DesktopApp.vue @@ -30,6 +30,7 @@ import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts'; import { ThemeType } from '@/core/theme.ts'; import { isProduction } from '@/lib/version.ts'; import { initMapProvider } from '@/lib/map/index.ts'; +import { isUserLogined, isUserUnlocked } from '@/lib/userstate.ts'; import { getSystemTheme, setExpenseAndIncomeAmountColor } from '@/lib/ui/common.ts'; export default { @@ -79,8 +80,8 @@ export default { setExpenseAndIncomeAmountColor(self.userStore.currentUserExpenseAmountColor, self.userStore.currentUserIncomeAmountColor); - if (self.$user.isUserLogined()) { - if (!self.settingsStore.appSettings.applicationLock || self.$user.isUserUnlocked()) { + if (isUserLogined()) { + if (!self.settingsStore.appSettings.applicationLock || isUserUnlocked()) { // refresh token if user is logined self.tokensStore.refreshTokenAndRevokeOldToken().then(response => { if (response.user) { diff --git a/src/MobileApp.vue b/src/MobileApp.vue index 01fdd938..f27e6a92 100644 --- a/src/MobileApp.vue +++ b/src/MobileApp.vue @@ -20,6 +20,7 @@ import { ThemeType } from '@/core/theme.ts'; import { isProduction } from '@/lib/version.ts'; import { getTheme, isEnableAnimate } from '@/lib/settings.ts'; import { initMapProvider } from '@/lib/map/index.ts'; +import { isUserLogined, isUserUnlocked } from '@/lib/userstate.ts'; import { setExpenseAndIncomeAmountColor } from '@/lib/ui/common.ts'; import { isModalShowing, setAppFontSize } from '@/lib/ui/mobile.js'; @@ -135,8 +136,8 @@ export default { setExpenseAndIncomeAmountColor(self.userStore.currentUserExpenseAmountColor, self.userStore.currentUserIncomeAmountColor); - if (self.$user.isUserLogined()) { - if (!self.settingsStore.appSettings.applicationLock || self.$user.isUserUnlocked()) { + if (isUserLogined()) { + if (!self.settingsStore.appSettings.applicationLock || isUserUnlocked()) { // refresh token if user is logined self.tokensStore.refreshTokenAndRevokeOldToken().then(response => { if (response.user) { diff --git a/src/desktop-main.js b/src/desktop-main.js index 4fc2e04a..b4e76b2a 100644 --- a/src/desktop-main.js +++ b/src/desktop-main.js @@ -71,7 +71,6 @@ import draggable from 'vuedraggable'; import router from '@/router/desktop.js'; import { getVersion, getBuildTime } from '@/lib/version.ts'; -import userstate from '@/lib/userstate.ts'; import { getI18nOptions, translateIf, @@ -477,6 +476,4 @@ app.config.globalProperties.$locale = i18nFunctions(i18n.global); app.config.globalProperties.$tIf = (text, isTranslate) => translateIf(text, isTranslate, i18n.global.t); app.config.globalProperties.$tError = (message) => translateError(message, i18n.global.t); -app.config.globalProperties.$user = userstate; - app.mount('#app'); diff --git a/src/lib/services.ts b/src/lib/services.ts index c52cf620..4dddc0d1 100644 --- a/src/lib/services.ts +++ b/src/lib/services.ts @@ -109,7 +109,10 @@ import type { UserProfileUpdateResponse } from '@/models/user.ts'; -import userState from './userstate.ts'; +import { + getCurrentToken, + clearCurrentTokenAndUserInfo +} from './userstate.ts'; import { isDefined, @@ -145,7 +148,7 @@ const blockedRequests: ((token: string | undefined) => void)[] = []; axios.defaults.baseURL = BASE_API_URL_PATH; axios.defaults.timeout = DEFAULT_API_TIMEOUT; axios.interceptors.request.use((config: ApiRequestConfig) => { - const token = userState.getToken(); + const token = getCurrentToken(); if (token && !config.noAuth) { config.headers.Authorization = `Bearer ${token}`; @@ -184,7 +187,7 @@ axios.interceptors.response.use(response => { || errorCode === 202006 // current token does not require two-factor authorization || errorCode === 202012 // token is empty ) { - userState.clearTokenAndUserInfo(false); + clearCurrentTokenAndUserInfo(false); location.reload(); return Promise.reject({ processed: true }); } @@ -521,7 +524,7 @@ export default { return `${BASE_QRCODE_PATH}/${qrCodeName}.png`; }, generateMapProxyTileImageUrl: (mapProvider: string, language: string): string => { - const token = userState.getToken(); + const token = getCurrentToken(); let url = `${BASE_PROXY_URL_PATH}/map/tile/{z}/{x}/{y}.png?provider=${mapProvider}&token=${token}`; if (language) { @@ -531,7 +534,7 @@ export default { return url; }, generateMapProxyAnnotationImageUrl: (mapProvider: string, language: string): string => { - const token = userState.getToken(); + const token = getCurrentToken(); let url = `${BASE_PROXY_URL_PATH}/map/annotation/{z}/{x}/{y}.png?provider=${mapProvider}&token=${token}`; if (language) { @@ -564,7 +567,7 @@ export default { } const params = []; - params.push('token=' + userState.getToken()); + params.push('token=' + getCurrentToken()); if (disableBrowserCache) { if (isBoolean(disableBrowserCache)) { @@ -586,7 +589,7 @@ export default { } const params = []; - params.push('token=' + userState.getToken()); + params.push('token=' + getCurrentToken()); if (disableBrowserCache) { if (isBoolean(disableBrowserCache)) { diff --git a/src/lib/userstate.ts b/src/lib/userstate.ts index 977da8d2..e99af77e 100644 --- a/src/lib/userstate.ts +++ b/src/lib/userstate.ts @@ -34,80 +34,11 @@ function getDecryptedToken(encryptedToken: string, appLockState: ApplicationLock return bytes.toString(CryptoJS.enc.Utf8); } -function getToken(): string | null { - if (isEnableApplicationLock()) { - const usedEncryptedToken = sessionStorage.getItem(encryptedTokenSessionStorageKey); - const currentEncryptedToken = localStorage.getItem(tokenLocalStorageKey); - - if (!usedEncryptedToken || !currentEncryptedToken) { - return null; - } - - if (usedEncryptedToken === currentEncryptedToken) { - return sessionStorage.getItem(tokenSessionStorageKey); - } - - // re-decrypt token - logger.warn(`encrypted token in local storage does not equal to the one in session storage, need to re-decrypt`); - - const appLockState = getUserAppLockState(); - const token = getDecryptedToken(currentEncryptedToken, appLockState); - - sessionStorage.setItem(encryptedTokenSessionStorageKey, currentEncryptedToken); - sessionStorage.setItem(tokenSessionStorageKey, token); - - return token; - } else { - return localStorage.getItem(tokenLocalStorageKey); - } -} - -function getUserInfo(): UserBasicInfo | null { - const data = localStorage.getItem(userInfoLocalStorageKey); - - if (!data) { - return null; - } - - return JSON.parse(data) as UserBasicInfo; -} - -function getUserTransactionDraft(): unknown | null { - let data = localStorage.getItem(transactionDraftLocalStorageKey); - - if (!data) { - return null; - } - - if (isEnableApplicationLock()) { - const appLockState = getUserAppLockState(); - data = getDecryptedToken(data, appLockState); - } - - return JSON.parse(data); -} - -function getUserAppLockState(): ApplicationLockState { - const data = sessionStorage.getItem(appLockStateSessionStorageKey); - - if (!data) { - throw new Error('No app lock state in session storage'); - } - - const appLockState = JSON.parse(data); - - if (!appLockState || !appLockState.username || !appLockState.secret) { - throw new Error('App lock state is invalid'); - } - - return appLockState as ApplicationLockState; -} - -function isUserLogined(): boolean { +export function isUserLogined(): boolean { return !!localStorage.getItem(tokenLocalStorageKey); } -function isUserUnlocked(): boolean { +export function isUserUnlocked(): boolean { if (!isUserLogined()) { return false; } @@ -119,31 +50,27 @@ function isUserUnlocked(): boolean { return !!sessionStorage.getItem(appLockStateSessionStorageKey) && !!sessionStorage.getItem(tokenSessionStorageKey); } -function getWebAuthnCredentialId(): string | undefined { - const webauthnConfigData = localStorage.getItem(webauthnConfigLocalStorageKey); +export function hasUserAppLockState(): boolean { + return !!getUserAppLockState(); +} - if (!webauthnConfigData) { - return undefined; +export function getUserAppLockState(): ApplicationLockState | null { + const data = sessionStorage.getItem(appLockStateSessionStorageKey); + + if (!data) { + return null; } - const webauthnConfig = JSON.parse(webauthnConfigData) as WebAuthnConfig; + const appLockState = JSON.parse(data); - return webauthnConfig.credentialId; + if (!appLockState || !appLockState.username || !appLockState.secret) { + return null; + } + + return appLockState as ApplicationLockState; } -function saveWebAuthnConfig(credentialId: string): void { - const webAuthnConfig: WebAuthnConfig = { - credentialId: credentialId - }; - - localStorage.setItem(webauthnConfigLocalStorageKey, JSON.stringify(webAuthnConfig)); -} - -function clearWebAuthnConfig(): void { - localStorage.removeItem(webauthnConfigLocalStorageKey); -} - -function unlockTokenByWebAuthn(credentialId: string, userName: string, userSecret: string): void { +export function unlockTokenByWebAuthn(credentialId: string, userName: string, userSecret: string): void { const webauthnConfigData = localStorage.getItem(webauthnConfigLocalStorageKey); if (!webauthnConfigData) { @@ -173,7 +100,7 @@ function unlockTokenByWebAuthn(credentialId: string, userName: string, userSecre sessionStorage.setItem(tokenSessionStorageKey, token); } -function unlockTokenByPinCode(userName: string, pinCode: string): void { +export function unlockTokenByPinCode(userName: string, pinCode: string): void { const encryptedToken = localStorage.getItem(tokenLocalStorageKey); if (!encryptedToken) { @@ -191,7 +118,7 @@ function unlockTokenByPinCode(userName: string, pinCode: string): void { sessionStorage.setItem(tokenSessionStorageKey, token); } -function encryptToken(userName: string, pinCode: string): void { +export function encryptToken(userName: string, pinCode: string): void { const token = localStorage.getItem(tokenLocalStorageKey); if (!token) { @@ -210,7 +137,7 @@ function encryptToken(userName: string, pinCode: string): void { localStorage.setItem(tokenLocalStorageKey, encryptedToken); } -function decryptToken(): void { +export function decryptToken(): void { const token = sessionStorage.getItem(tokenSessionStorageKey); if (!token) { @@ -223,18 +150,55 @@ function decryptToken(): void { sessionStorage.removeItem(appLockStateSessionStorageKey); } -function isCorrectPinCode(pinCode: string): boolean { +export function isCorrectPinCode(pinCode: string): boolean { const secret = getAppLockSecret(pinCode); const appLockState = getUserAppLockState(); + if (!appLockState) { + return false; + } + return appLockState && secret === appLockState.secret; } -function updateToken(token: string): void { +export function getCurrentToken(): string | null { + if (isEnableApplicationLock()) { + const usedEncryptedToken = sessionStorage.getItem(encryptedTokenSessionStorageKey); + const currentEncryptedToken = localStorage.getItem(tokenLocalStorageKey); + + if (!usedEncryptedToken || !currentEncryptedToken) { + return null; + } + + if (usedEncryptedToken === currentEncryptedToken) { + return sessionStorage.getItem(tokenSessionStorageKey); + } + + // re-decrypt token + logger.warn(`encrypted token in local storage does not equal to the one in session storage, need to re-decrypt`); + + const appLockState = getUserAppLockState(); + + if (!appLockState) { + return null; + } + + const token = getDecryptedToken(currentEncryptedToken, appLockState); + + sessionStorage.setItem(encryptedTokenSessionStorageKey, currentEncryptedToken); + sessionStorage.setItem(tokenSessionStorageKey, token); + + return token; + } else { + return localStorage.getItem(tokenLocalStorageKey); + } +} + +export function updateCurrentToken(token: string): void { if (isString(token)) { - if (isEnableApplicationLock()) { + if (isEnableApplicationLock() && hasUserAppLockState()) { const appLockState = getUserAppLockState(); - const encryptedToken = getEncryptedToken(token, appLockState); + const encryptedToken = getEncryptedToken(token, appLockState as ApplicationLockState); sessionStorage.setItem(encryptedTokenSessionStorageKey, encryptedToken); sessionStorage.setItem(tokenSessionStorageKey, token); @@ -245,13 +209,75 @@ function updateToken(token: string): void { } } -function updateUserInfo(user: UserBasicInfo): void { +export function hasWebAuthnConfig(): boolean { + return !!getWebAuthnCredentialId(); +} + +export function getWebAuthnCredentialId(): string | undefined { + const webauthnConfigData = localStorage.getItem(webauthnConfigLocalStorageKey); + + if (!webauthnConfigData) { + return undefined; + } + + const webauthnConfig = JSON.parse(webauthnConfigData) as WebAuthnConfig; + + return webauthnConfig.credentialId; +} + +export function saveWebAuthnConfig(credentialId: string): void { + const webAuthnConfig: WebAuthnConfig = { + credentialId: credentialId + }; + + localStorage.setItem(webauthnConfigLocalStorageKey, JSON.stringify(webAuthnConfig)); +} + +export function clearWebAuthnConfig(): void { + localStorage.removeItem(webauthnConfigLocalStorageKey); +} + +export function getCurrentUserInfo(): UserBasicInfo | null { + const data = localStorage.getItem(userInfoLocalStorageKey); + + if (!data) { + return null; + } + + return JSON.parse(data) as UserBasicInfo; +} + +export function updateCurrentUserInfo(user: UserBasicInfo): void { if (isObject(user)) { localStorage.setItem(userInfoLocalStorageKey, JSON.stringify(user)); } } -function updateUserTransactionDraft(transaction: unknown): void { +export function clearCurrentUserInfo(): void { + localStorage.removeItem(userInfoLocalStorageKey); +} + +export function getUserTransactionDraft(): unknown | null { + let data = localStorage.getItem(transactionDraftLocalStorageKey); + + if (!data) { + return null; + } + + if (isEnableApplicationLock()) { + const appLockState = getUserAppLockState(); + + if (!appLockState) { + return null; + } + + data = getDecryptedToken(data, appLockState); + } + + return JSON.parse(data); +} + +export function updateUserTransactionDraft(transaction: unknown): void { if (!isObject(transaction)) { return; } @@ -260,27 +286,28 @@ function updateUserTransactionDraft(transaction: unknown): void { if (isEnableApplicationLock()) { const appLockState = getUserAppLockState(); + + if (!appLockState) { + return; + } + data = getEncryptedToken(data, appLockState); } localStorage.setItem(transactionDraftLocalStorageKey, data); } -function clearUserInfo(): void { - localStorage.removeItem(userInfoLocalStorageKey); -} - -function clearUserTransactionDraft(): void { +export function clearUserTransactionDraft(): void { localStorage.removeItem(transactionDraftLocalStorageKey); } -function clearSessionToken(): void { +export function clearCurrentSessionToken(): void { sessionStorage.removeItem(tokenSessionStorageKey); sessionStorage.removeItem(encryptedTokenSessionStorageKey); sessionStorage.removeItem(appLockStateSessionStorageKey); } -function clearTokenAndUserInfo(clearAppLockState: boolean): void { +export function clearCurrentTokenAndUserInfo(clearAppLockState: boolean): void { if (clearAppLockState) { sessionStorage.removeItem(appLockStateSessionStorageKey); } @@ -289,29 +316,5 @@ function clearTokenAndUserInfo(clearAppLockState: boolean): void { sessionStorage.removeItem(encryptedTokenSessionStorageKey); localStorage.removeItem(tokenLocalStorageKey); clearUserTransactionDraft(); - clearUserInfo(); + clearCurrentUserInfo(); } - -export default { - getToken, - getUserInfo, - getUserTransactionDraft, - getUserAppLockState, - isUserLogined, - isUserUnlocked, - getWebAuthnCredentialId, - saveWebAuthnConfig, - clearWebAuthnConfig, - unlockTokenByWebAuthn, - unlockTokenByPinCode, - encryptToken, - decryptToken, - isCorrectPinCode, - updateToken, - updateUserTransactionDraft, - updateUserInfo, - clearUserInfo, - clearUserTransactionDraft, - clearSessionToken, - clearTokenAndUserInfo -}; diff --git a/src/mobile-main.js b/src/mobile-main.js index 50a9cd76..ae84ceb1 100644 --- a/src/mobile-main.js +++ b/src/mobile-main.js @@ -80,7 +80,6 @@ import VueDatePicker from '@vuepic/vue-datepicker'; import '@vuepic/vue-datepicker/dist/main.css'; import { getVersion, getBuildTime } from '@/lib/version.ts'; -import userstate from '@/lib/userstate.ts'; import { getI18nOptions, translateIf, @@ -216,6 +215,4 @@ app.config.globalProperties.$showLoading = showLoading; app.config.globalProperties.$hideLoading = hideLoading; app.config.globalProperties.$routeBackOnError = routeBackOnError; -app.config.globalProperties.$user = userstate; - app.mount('#app'); diff --git a/src/router/desktop.js b/src/router/desktop.js index 5b6c0c91..e2bb7502 100644 --- a/src/router/desktop.js +++ b/src/router/desktop.js @@ -1,7 +1,7 @@ import { createRouter, createWebHashHistory } from 'vue-router'; import { TemplateType } from '@/core/template.ts'; -import userState from '@/lib/userstate.ts'; +import { isUserLogined, isUserUnlocked } from '@/lib/userstate.ts'; import MainLayout from '@/views/desktop/MainLayout.vue'; import LoginPage from '@/views/desktop/LoginPage.vue'; @@ -32,14 +32,14 @@ import ExchangeRatesPage from '@/views/desktop/ExchangeRatesPage.vue'; import AboutPage from '@/views/desktop/AboutPage.vue'; function checkLogin() { - if (!userState.isUserLogined()) { + if (!isUserLogined()) { return { path: '/login', replace: true }; } - if (!userState.isUserUnlocked()) { + if (!isUserUnlocked()) { return { path: '/unlock', replace: true @@ -48,14 +48,14 @@ function checkLogin() { } function checkLocked() { - if (!userState.isUserLogined()) { + if (!isUserLogined()) { return { path: '/login', replace: true }; } - if (userState.isUserUnlocked()) { + if (isUserUnlocked()) { return { path: '/', replace: true @@ -64,14 +64,14 @@ function checkLocked() { } function checkNotLogin() { - if (userState.isUserLogined() && !userState.isUserUnlocked()) { + if (isUserLogined() && !isUserUnlocked()) { return { path: '/unlock', replace: true }; } - if (userState.isUserLogined()) { + if (isUserLogined()) { return { path: '/', replace: true diff --git a/src/router/mobile.js b/src/router/mobile.js index e1e984dc..62c062d8 100644 --- a/src/router/mobile.js +++ b/src/router/mobile.js @@ -1,4 +1,4 @@ -import userState from '@/lib/userstate.ts'; +import { isUserLogined, isUserUnlocked } from '@/lib/userstate.ts'; import HomePage from '@/views/mobile/HomePage.vue'; import LoginPage from '@/views/mobile/LoginPage.vue'; @@ -49,7 +49,7 @@ function asyncResolve(component) { } function checkLogin({ router, resolve, reject }) { - if (!userState.isUserLogined()) { + if (!isUserLogined()) { reject(); router.navigate('/login', { clearPreviousHistory: true, @@ -58,7 +58,7 @@ function checkLogin({ router, resolve, reject }) { return; } - if (!userState.isUserUnlocked()) { + if (!isUserUnlocked()) { reject(); router.navigate('/unlock', { clearPreviousHistory: true, @@ -71,7 +71,7 @@ function checkLogin({ router, resolve, reject }) { } function checkLocked({ router, resolve, reject }) { - if (!userState.isUserLogined()) { + if (!isUserLogined()) { reject(); router.navigate('/login', { clearPreviousHistory: true, @@ -80,7 +80,7 @@ function checkLocked({ router, resolve, reject }) { return; } - if (userState.isUserUnlocked()) { + if (isUserUnlocked()) { reject(); router.navigate('/', { clearPreviousHistory: true, @@ -93,7 +93,7 @@ function checkLocked({ router, resolve, reject }) { } function checkNotLogin({ router, resolve, reject }) { - if (userState.isUserLogined() && !userState.isUserUnlocked()) { + if (isUserLogined() && !isUserUnlocked()) { reject(); router.navigate('/unlock', { clearPreviousHistory: true, @@ -102,7 +102,7 @@ function checkNotLogin({ router, resolve, reject }) { return; } - if (userState.isUserLogined()) { + if (isUserLogined()) { reject(); router.navigate('/', { clearPreviousHistory: true, diff --git a/src/stores/index.js b/src/stores/index.js index 1ca50a27..f8118561 100644 --- a/src/stores/index.js +++ b/src/stores/index.js @@ -11,7 +11,14 @@ import { useOverviewStore } from './overview.js'; import { useStatisticsStore } from './statistics.js'; import { useExchangeRatesStore } from './exchangeRates.js'; -import userState from '@/lib/userstate.ts'; +import { + hasUserAppLockState, + getUserAppLockState, + updateCurrentToken, + clearWebAuthnConfig, + clearCurrentSessionToken, + clearCurrentTokenAndUserInfo +} from '@/lib/userstate.ts'; import services from '@/lib/services.ts'; import logger from '@/lib/logger.ts'; import { isObject, isString } from '@/lib/common.ts'; @@ -75,18 +82,18 @@ export const useRootStore = defineStore('root', { return; } - if (settingsStore.appSettings.applicationLock || userState.getUserAppLockState()) { - const appLockState = userState.getUserAppLockState(); + if (settingsStore.appSettings.applicationLock || hasUserAppLockState()) { + const appLockState = getUserAppLockState(); if (!appLockState || appLockState.username !== data.result.user.username) { - userState.clearTokenAndUserInfo(true); + clearCurrentTokenAndUserInfo(true); settingsStore.setEnableApplicationLock(false); settingsStore.setEnableApplicationLockWebAuthn(false); - userState.clearWebAuthnConfig(); + clearWebAuthnConfig(); } } - userState.updateToken(data.result.token); + updateCurrentToken(data.result.token); if (data.result.user && isObject(data.result.user)) { const userStore = useUserStore(); @@ -136,18 +143,18 @@ export const useRootStore = defineStore('root', { return; } - if (settingsStore.appSettings.applicationLock || userState.getUserAppLockState()) { - const appLockState = userState.getUserAppLockState(); + if (settingsStore.appSettings.applicationLock || hasUserAppLockState()) { + const appLockState = getUserAppLockState(); if (!appLockState || appLockState.username !== data.result.user.username) { - userState.clearTokenAndUserInfo(true); + clearCurrentTokenAndUserInfo(true); settingsStore.setEnableApplicationLock(false); settingsStore.setEnableApplicationLockWebAuthn(false); - userState.clearWebAuthnConfig(); + clearWebAuthnConfig(); } } - userState.updateToken(data.result.token); + updateCurrentToken(data.result.token); if (data.result.user && isObject(data.result.user)) { const userStore = useUserStore(); @@ -192,11 +199,11 @@ export const useRootStore = defineStore('root', { if (settingsStore.appSettings.applicationLock) { settingsStore.setEnableApplicationLock(false); settingsStore.setEnableApplicationLockWebAuthn(false); - userState.clearWebAuthnConfig(); + clearWebAuthnConfig(); } if (data.result.token && isString(data.result.token)) { - userState.updateToken(data.result.token); + updateCurrentToken(data.result.token); } if (data.result.user && isObject(data.result.user)) { @@ -219,7 +226,7 @@ export const useRootStore = defineStore('root', { }); }, lock() { - userState.clearSessionToken(); + clearCurrentSessionToken(); this.resetAllStates(false); }, logout() { @@ -234,8 +241,8 @@ export const useRootStore = defineStore('root', { return; } - userState.clearTokenAndUserInfo(true); - userState.clearWebAuthnConfig(); + clearCurrentTokenAndUserInfo(true); + clearWebAuthnConfig(); self.resetAllStates(true); resolve(data.result); @@ -253,8 +260,8 @@ export const useRootStore = defineStore('root', { }); }, forceLogout() { - userState.clearTokenAndUserInfo(true); - userState.clearWebAuthnConfig(); + clearCurrentTokenAndUserInfo(true); + clearWebAuthnConfig(); this.resetAllStates(true); }, verifyEmail({ token, requestNewToken }) { @@ -271,7 +278,7 @@ export const useRootStore = defineStore('root', { } if (data.result.newToken && isString(data.result.newToken)) { - userState.updateToken(data.result.newToken); + updateCurrentToken(data.result.newToken); } if (data.result.user && isObject(data.result.user)) { @@ -405,7 +412,7 @@ export const useRootStore = defineStore('root', { } if (data.result.newToken && isString(data.result.newToken)) { - userState.updateToken(data.result.newToken); + updateCurrentToken(data.result.newToken); } if (data.result.user && isObject(data.result.user)) { diff --git a/src/stores/token.js b/src/stores/token.js index dbe14eed..b81e29eb 100644 --- a/src/stores/token.js +++ b/src/stores/token.js @@ -2,7 +2,7 @@ import { defineStore } from 'pinia'; import { useUserStore } from './user.js'; -import userState from '@/lib/userstate.ts'; +import { updateCurrentToken } from '@/lib/userstate.ts'; import services from '@/lib/services.ts'; import logger from '@/lib/logger.ts'; import { isObject } from '@/lib/common.ts'; @@ -46,7 +46,7 @@ export const useTokensStore = defineStore('tokens', { } if (data && data.success && data.result && data.result.newToken) { - userState.updateToken(data.result.newToken); + updateCurrentToken(data.result.newToken); if (data.result.oldTokenId) { self.revokeToken({ diff --git a/src/stores/transaction.js b/src/stores/transaction.js index 0d231dfe..6911f2e7 100644 --- a/src/stores/transaction.js +++ b/src/stores/transaction.js @@ -12,7 +12,11 @@ import { DateRange } from '@/core/datetime.ts'; import { CategoryType } from '@/core/category.ts'; import { TransactionType, TransactionTagFilterType } from '@/core/transaction.ts'; import { TRANSACTION_MIN_AMOUNT, TRANSACTION_MAX_AMOUNT } from '@/consts/transaction.ts'; -import userState from '@/lib/userstate.ts'; +import { + getUserTransactionDraft, + updateUserTransactionDraft, + clearUserTransactionDraft +} from '@/lib/userstate.ts'; import services from '@/lib/services.ts'; import logger from '@/lib/logger.ts'; import { @@ -366,7 +370,7 @@ function buildTransactionDraft(transaction) { export const useTransactionsStore = defineStore('transactions', { state: () => ({ - transactionDraft: userState.getUserTransactionDraft(), + transactionDraft: getUserTransactionDraft(), transactionsFilter: { dateType: DateRange.All.type, maxTime: 0, @@ -502,7 +506,7 @@ export const useTransactionsStore = defineStore('transactions', { const settingsStore = useSettingsStore(); if (settingsStore.appSettings.autoSaveTransactionDraft === 'enabled' || settingsStore.appSettings.autoSaveTransactionDraft === 'confirmation') { - this.transactionDraft = userState.getUserTransactionDraft(); + this.transactionDraft = getUserTransactionDraft(); } else { this.transactionDraft = null; } @@ -590,11 +594,11 @@ export const useTransactionsStore = defineStore('transactions', { this.transactionDraft = buildTransactionDraft(transaction); } - userState.updateUserTransactionDraft(this.transactionDraft); + updateUserTransactionDraft(this.transactionDraft); }, clearTransactionDraft() { this.transactionDraft = null; - userState.clearUserTransactionDraft(); + clearUserTransactionDraft(); }, generateNewTransactionModel(type) { const settingsStore = useSettingsStore(); diff --git a/src/stores/twoFactorAuth.js b/src/stores/twoFactorAuth.js index 3561a6bb..b96d656a 100644 --- a/src/stores/twoFactorAuth.js +++ b/src/stores/twoFactorAuth.js @@ -1,6 +1,6 @@ import { defineStore } from 'pinia'; -import userState from '@/lib/userstate.ts'; +import { updateCurrentToken } from '@/lib/userstate.ts'; import services from '@/lib/services.ts'; import logger from '@/lib/logger.ts'; import { isBoolean } from '@/lib/common.ts'; @@ -69,7 +69,7 @@ export const useTwoFactorAuthStore = defineStore('twoFactorAuth', { } if (data.result.token) { - userState.updateToken(data.result.token); + updateCurrentToken(data.result.token); } resolve(data.result); diff --git a/src/stores/user.js b/src/stores/user.js index e9026842..db3a88db 100644 --- a/src/stores/user.js +++ b/src/stores/user.js @@ -2,7 +2,11 @@ import { defineStore } from 'pinia'; import { useSettingsStore } from './setting.ts'; -import userState from '@/lib/userstate.ts'; +import { + getCurrentUserInfo, + updateCurrentUserInfo, + clearCurrentUserInfo +} from '@/lib/userstate.ts'; import services from '@/lib/services.ts'; import logger from '@/lib/logger.ts'; import { @@ -12,7 +16,7 @@ import { export const useUserStore = defineStore('user', { state: () => ({ - currentUserBasicInfo: userState.getUserInfo() + currentUserBasicInfo: getCurrentUserInfo() }), getters: { currentUserNickname(state) { @@ -99,11 +103,11 @@ export const useUserStore = defineStore('user', { }, storeUserBasicInfo(userInfo) { this.currentUserBasicInfo = userInfo; - userState.updateUserInfo(userInfo); + updateCurrentUserInfo(userInfo); }, resetUserBasicInfo() { this.currentUserBasicInfo = null; - userState.clearUserInfo(); + clearCurrentUserInfo(); }, getCurrentUserProfile() { return new Promise((resolve, reject) => { diff --git a/src/views/desktop/HomePage.vue b/src/views/desktop/HomePage.vue index 4d5861d4..ccb7152a 100644 --- a/src/views/desktop/HomePage.vue +++ b/src/views/desktop/HomePage.vue @@ -203,6 +203,7 @@ import { getUnixTimeBeforeUnixTime, getUnixTimeAfterUnixTime } from '@/lib/datetime.ts'; +import { isUserLogined, isUserUnlocked } from '@/lib/userstate.ts'; import { mdiRefresh, @@ -348,7 +349,7 @@ export default { } }, created() { - if (this.$user.isUserLogined() && this.$user.isUserUnlocked()) { + if (isUserLogined() && isUserUnlocked()) { this.reload(false); } }, diff --git a/src/views/desktop/SignupPage.vue b/src/views/desktop/SignupPage.vue index 412f359e..6f9c7721 100644 --- a/src/views/desktop/SignupPage.vue +++ b/src/views/desktop/SignupPage.vue @@ -248,6 +248,7 @@ import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts'; import { CategoryType } from '@/core/category.ts'; import { ThemeType } from '@/core/theme.ts'; import { categorizedArrayToPlainArray } from '@/lib/common.ts'; +import { isUserLogined } from '@/lib/userstate.ts'; import { setExpenseAndIncomeAmountColor } from '@/lib/ui/common.ts'; import { @@ -436,7 +437,7 @@ export default { user: self.user, presetCategories: presetCategories }).then(response => { - if (!self.$user.isUserLogined()) { + if (!isUserLogined()) { self.submitting = false; if (self.usePresetCategories && !response.presetCategoriesSaved) { diff --git a/src/views/desktop/UnlockPage.vue b/src/views/desktop/UnlockPage.vue index 398cb9ae..160d3c51 100644 --- a/src/views/desktop/UnlockPage.vue +++ b/src/views/desktop/UnlockPage.vue @@ -121,6 +121,12 @@ import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts'; import { ThemeType } from '@/core/theme.ts'; import logger from '@/lib/logger.ts'; import webauthn from '@/lib/webauthn.js'; +import { + unlockTokenByWebAuthn, + unlockTokenByPinCode, + hasWebAuthnConfig, + getWebAuthnCredentialId +} from '@/lib/userstate.ts'; import { setExpenseAndIncomeAmountColor } from '@/lib/ui/common.ts'; export default { @@ -143,7 +149,7 @@ export default { }, isWebAuthnAvailable() { return this.settingsStore.appSettings.applicationLockWebAuthn - && this.$user.getWebAuthnCredentialId() + && hasWebAuthnConfig() && webauthn.isSupported(); }, isDarkMode() { @@ -164,7 +170,7 @@ export default { unlockByWebAuthn() { const self = this; - if (!self.settingsStore.appSettings.applicationLockWebAuthn || !self.$user.getWebAuthnCredentialId()) { + if (!self.settingsStore.appSettings.applicationLockWebAuthn || !hasWebAuthnConfig()) { self.$refs.snackbar.showMessage('WebAuthn is not enabled'); return; } @@ -178,11 +184,11 @@ export default { webauthn.verifyCredential( self.userStore.currentUserBasicInfo, - self.$user.getWebAuthnCredentialId() + getWebAuthnCredentialId() ).then(({ id, userName, userSecret }) => { self.verifyingByWebAuthn = false; - self.$user.unlockTokenByWebAuthn(id, userName, userSecret); + unlockTokenByWebAuthn(id, userName, userSecret); self.transactionsStore.initTransactionDraft(); self.tokensStore.refreshTokenAndRevokeOldToken().then(response => { if (response.user) { @@ -232,7 +238,7 @@ export default { } try { - self.$user.unlockTokenByPinCode(user.username, pinCode); + unlockTokenByPinCode(user.username, pinCode); self.transactionsStore.initTransactionDraft(); self.tokensStore.refreshTokenAndRevokeOldToken().then(response => { if (response.user) { diff --git a/src/views/desktop/VerifyEmailPage.vue b/src/views/desktop/VerifyEmailPage.vue index 23e12a66..162340b4 100644 --- a/src/views/desktop/VerifyEmailPage.vue +++ b/src/views/desktop/VerifyEmailPage.vue @@ -119,6 +119,7 @@ import { useSettingsStore } from '@/stores/setting.ts'; import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts'; import { ThemeType } from '@/core/theme.ts'; import { isUserVerifyEmailEnabled } from '@/lib/server_settings.ts'; +import { isUserLogined } from '@/lib/userstate.ts'; import { mdiChevronLeft @@ -183,7 +184,7 @@ export default { self.rootStore.verifyEmail({ token: self.token, - requestNewToken: !self.$user.isUserLogined() + requestNewToken: !isUserLogined() }).then(() => { self.loading = false; self.verified = true; @@ -219,7 +220,7 @@ export default { }); }, onSnackbarShowStateChanged(newValue) { - if (!newValue && this.verified && this.$user.isUserLogined()) { + if (!newValue && this.verified && isUserLogined()) { this.$router.replace('/'); } }, diff --git a/src/views/desktop/app/settings/tabs/AppLockSettingTab.vue b/src/views/desktop/app/settings/tabs/AppLockSettingTab.vue index 0e5e350a..f45f5f95 100644 --- a/src/views/desktop/app/settings/tabs/AppLockSettingTab.vue +++ b/src/views/desktop/app/settings/tabs/AppLockSettingTab.vue @@ -68,6 +68,14 @@ import { useTransactionsStore } from '@/stores/transaction.js'; import logger from '@/lib/logger.ts'; import webauthn from '@/lib/webauthn.js'; +import { + getUserAppLockState, + encryptToken, + decryptToken, + isCorrectPinCode, + saveWebAuthnConfig, + clearWebAuthnConfig +} from '@/lib/userstate.ts'; export default { data() { @@ -107,12 +115,12 @@ export default { self.enablingWebAuthn = true; webauthn.registerCredential( - self.$user.getUserAppLockState(), + getUserAppLockState(), self.userStore.currentUserBasicInfo, ).then(({ id }) => { self.enablingWebAuthn = false; - self.$user.saveWebAuthnConfig(id); + saveWebAuthnConfig(id); self.settingsStore.setEnableApplicationLockWebAuthn(true); self.$refs.snackbar.showMessage('You have enabled WebAuthn successfully'); }).catch(error => { @@ -132,11 +140,11 @@ export default { self.isEnableApplicationLockWebAuthn = false; self.settingsStore.setEnableApplicationLockWebAuthn(false); - self.$user.clearWebAuthnConfig(); + clearWebAuthnConfig(); }); } else { self.settingsStore.setEnableApplicationLockWebAuthn(false); - self.$user.clearWebAuthnConfig(); + clearWebAuthnConfig(); } } }, @@ -174,12 +182,12 @@ export default { return; } - this.$user.encryptToken(user.username, this.pinCode); + encryptToken(user.username, this.pinCode); this.settingsStore.setEnableApplicationLock(true); this.transactionsStore.saveTransactionDraft(); this.settingsStore.setEnableApplicationLockWebAuthn(false); - this.$user.clearWebAuthnConfig(); + clearWebAuthnConfig(); this.pinCode = ''; }, @@ -189,7 +197,7 @@ export default { return; } - if (!this.$user.isCorrectPinCode(this.pinCode)) { + if (!isCorrectPinCode(this.pinCode)) { this.pinCode = ''; this.$refs.snackbar.showMessage('Incorrect PIN code'); return; @@ -197,12 +205,12 @@ export default { this.pinCode = ''; - this.$user.decryptToken(); + decryptToken(); this.settingsStore.setEnableApplicationLock(false); this.transactionsStore.saveTransactionDraft(); this.settingsStore.setEnableApplicationLockWebAuthn(false); - this.$user.clearWebAuthnConfig(); + clearWebAuthnConfig(); } } } diff --git a/src/views/mobile/ApplicationLockPage.vue b/src/views/mobile/ApplicationLockPage.vue index b5ee9fbb..e91805b6 100644 --- a/src/views/mobile/ApplicationLockPage.vue +++ b/src/views/mobile/ApplicationLockPage.vue @@ -43,6 +43,14 @@ import { useTransactionsStore } from '@/stores/transaction.js'; import logger from '@/lib/logger.ts'; import webauthn from '@/lib/webauthn.js'; +import { + getUserAppLockState, + encryptToken, + decryptToken, + isCorrectPinCode, + saveWebAuthnConfig, + clearWebAuthnConfig +} from '@/lib/userstate.ts'; export default { data() { @@ -81,12 +89,12 @@ export default { self.$showLoading(); webauthn.registerCredential( - self.$user.getUserAppLockState(), + getUserAppLockState(), self.userStore.currentUserBasicInfo, ).then(({ id }) => { self.$hideLoading(); - self.$user.saveWebAuthnConfig(id); + saveWebAuthnConfig(id); self.settingsStore.setEnableApplicationLockWebAuthn(true); self.$toast('You have enabled WebAuthn successfully'); }).catch(error => { @@ -106,11 +114,11 @@ export default { self.isEnableApplicationLockWebAuthn = false; self.settingsStore.setEnableApplicationLockWebAuthn(false); - self.$user.clearWebAuthnConfig(); + clearWebAuthnConfig(); }); } else { self.settingsStore.setEnableApplicationLockWebAuthn(false); - self.$user.clearWebAuthnConfig(); + clearWebAuthnConfig(); } } }, @@ -144,12 +152,12 @@ export default { return; } - this.$user.encryptToken(user.username, pinCode); + encryptToken(user.username, pinCode); this.settingsStore.setEnableApplicationLock(true); this.transactionsStore.saveTransactionDraft(); this.settingsStore.setEnableApplicationLockWebAuthn(false); - this.$user.clearWebAuthnConfig(); + clearWebAuthnConfig(); this.showInputPinCodeSheetForEnable = false; }, @@ -164,17 +172,17 @@ export default { return; } - if (!this.$user.isCorrectPinCode(pinCode)) { + if (!isCorrectPinCode(pinCode)) { this.$alert('Incorrect PIN code'); return; } - this.$user.decryptToken(); + decryptToken(); this.settingsStore.setEnableApplicationLock(false); this.transactionsStore.saveTransactionDraft(); this.settingsStore.setEnableApplicationLockWebAuthn(false); - this.$user.clearWebAuthnConfig(); + clearWebAuthnConfig(); this.showInputPinCodeSheetForDisable = false; } diff --git a/src/views/mobile/HomePage.vue b/src/views/mobile/HomePage.vue index 7f4d2814..aa7d5acb 100644 --- a/src/views/mobile/HomePage.vue +++ b/src/views/mobile/HomePage.vue @@ -210,6 +210,7 @@ import { useOverviewStore } from '@/stores/overview.js'; import { DateRange } from '@/core/datetime.ts'; import { TemplateType } from '@/core/template.ts'; import { formatUnixTime } from '@/lib/datetime.ts'; +import { isUserLogined, isUserUnlocked } from '@/lib/userstate.ts'; export default { props: [ @@ -269,7 +270,7 @@ export default { created() { const self = this; - if (self.$user.isUserLogined() && self.$user.isUserUnlocked()) { + if (isUserLogined() && isUserUnlocked()) { self.loading = true; self.overviewStore.loadTransactionOverview({ diff --git a/src/views/mobile/SignupPage.vue b/src/views/mobile/SignupPage.vue index 296226e7..de0a3168 100644 --- a/src/views/mobile/SignupPage.vue +++ b/src/views/mobile/SignupPage.vue @@ -184,6 +184,7 @@ import { useExchangeRatesStore } from '@/stores/exchangeRates.js'; import { CategoryType } from '@/core/category.ts'; import { getNameByKeyValue, categorizedArrayToPlainArray } from '@/lib/common.ts'; +import { isUserLogined } from '@/lib/userstate.ts'; import { setExpenseAndIncomeAmountColor } from '@/lib/ui/common.ts'; export default { @@ -307,7 +308,7 @@ export default { user: self.user, presetCategories: presetCategories }).then(response => { - if (!self.$user.isUserLogined()) { + if (!isUserLogined()) { self.submitting = false; self.$hideLoading(); diff --git a/src/views/mobile/UnlockPage.vue b/src/views/mobile/UnlockPage.vue index 316ee418..8f8b2d42 100644 --- a/src/views/mobile/UnlockPage.vue +++ b/src/views/mobile/UnlockPage.vue @@ -76,6 +76,12 @@ import { useExchangeRatesStore } from '@/stores/exchangeRates.js'; import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts'; import logger from '@/lib/logger.ts'; import webauthn from '@/lib/webauthn.js'; +import { + unlockTokenByWebAuthn, + unlockTokenByPinCode, + hasWebAuthnConfig, + getWebAuthnCredentialId +} from '@/lib/userstate.ts'; import { setExpenseAndIncomeAmountColor } from '@/lib/ui/common.ts'; import { isModalShowing } from '@/lib/ui/mobile.js'; @@ -101,7 +107,7 @@ export default { }, isWebAuthnAvailable() { return this.settingsStore.appSettings.applicationLockWebAuthn - && this.$user.getWebAuthnCredentialId() + && hasWebAuthnConfig() && webauthn.isSupported(); }, currentLanguageCode() { @@ -116,7 +122,7 @@ export default { const self = this; const router = self.f7router; - if (!self.settingsStore.appSettings.applicationLockWebAuthn || !self.$user.getWebAuthnCredentialId()) { + if (!self.settingsStore.appSettings.applicationLockWebAuthn || !hasWebAuthnConfig()) { self.$toast('WebAuthn is not enabled'); return; } @@ -130,11 +136,11 @@ export default { webauthn.verifyCredential( self.userStore.currentUserBasicInfo, - self.$user.getWebAuthnCredentialId() + getWebAuthnCredentialId() ).then(({ id, userName, userSecret }) => { self.$hideLoading(); - self.$user.unlockTokenByWebAuthn(id, userName, userSecret); + unlockTokenByWebAuthn(id, userName, userSecret); self.transactionsStore.initTransactionDraft(); self.tokensStore.refreshTokenAndRevokeOldToken().then(response => { if (response.user) { @@ -189,7 +195,7 @@ export default { } try { - self.$user.unlockTokenByPinCode(user.username, pinCode); + unlockTokenByPinCode(user.username, pinCode); self.transactionsStore.initTransactionDraft(); self.tokensStore.refreshTokenAndRevokeOldToken().then(response => { if (response.user) {