mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-17 16:24:25 +08:00
migrate root store to composition API and typescript
This commit is contained in:
+1
-1
@@ -23,7 +23,7 @@ import { register } from 'register-service-worker';
|
|||||||
|
|
||||||
import { useI18n } from '@/locales/helpers.ts';
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
|
|
||||||
import { useRootStore } from '@/stores/index.js';
|
import { useRootStore } from '@/stores/index.ts';
|
||||||
import { useSettingsStore } from '@/stores/setting.ts';
|
import { useSettingsStore } from '@/stores/setting.ts';
|
||||||
import { useUserStore } from '@/stores/user.ts';
|
import { useUserStore } from '@/stores/user.ts';
|
||||||
import { useTokensStore } from '@/stores/token.ts';
|
import { useTokensStore } from '@/stores/token.ts';
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ import routes from './router/mobile.js';
|
|||||||
|
|
||||||
import { useI18n } from '@/locales/helpers.ts';
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
|
|
||||||
import { useRootStore } from '@/stores/index.js';
|
import { useRootStore } from '@/stores/index.ts';
|
||||||
import { useSettingsStore } from '@/stores/setting.ts';
|
import { useSettingsStore } from '@/stores/setting.ts';
|
||||||
import { useEnvironmentsStore } from '@/stores/environment.ts';
|
import { useEnvironmentsStore } from '@/stores/environment.ts';
|
||||||
import { useUserStore } from '@/stores/user.ts';
|
import { useUserStore } from '@/stores/user.ts';
|
||||||
|
|||||||
+70
-3
@@ -2,10 +2,9 @@ import { LongDateFormat, ShortDateFormat, LongTimeFormat, ShortTimeFormat } from
|
|||||||
import { DecimalSeparator, DigitGroupingSymbol, DigitGroupingType } from '@/core/numeral.ts';
|
import { DecimalSeparator, DigitGroupingSymbol, DigitGroupingType } from '@/core/numeral.ts';
|
||||||
import { CurrencyDisplayType } from '@/core/currency.ts';
|
import { CurrencyDisplayType } from '@/core/currency.ts';
|
||||||
import { PresetAmountColor } from '@/core/color.ts';
|
import { PresetAmountColor } from '@/core/color.ts';
|
||||||
|
import type { LocalizedPresetCategory } from '@/core/category.ts';
|
||||||
import { TransactionEditScopeType } from '@/core/transaction.ts';
|
import { TransactionEditScopeType } from '@/core/transaction.ts';
|
||||||
|
|
||||||
import type { TransactionCategoryCreateBatchRequest } from './transaction_category.ts';
|
|
||||||
|
|
||||||
export class User {
|
export class User {
|
||||||
public username: string = '';
|
public username: string = '';
|
||||||
public password: string = '';
|
public password: string = '';
|
||||||
@@ -16,12 +15,80 @@ export class User {
|
|||||||
public defaultCurrency: string;
|
public defaultCurrency: string;
|
||||||
public firstDayOfWeek: number;
|
public firstDayOfWeek: number;
|
||||||
|
|
||||||
|
public defaultAccountId?: string;
|
||||||
|
public transactionEditScope?: number;
|
||||||
|
public longDateFormat?: number;
|
||||||
|
public shortDateFormat?: number;
|
||||||
|
public longTimeFormat?: number;
|
||||||
|
public shortTimeFormat?: number;
|
||||||
|
public decimalSeparator?: number;
|
||||||
|
public digitGroupingSymbol?: number;
|
||||||
|
public digitGrouping?: number;
|
||||||
|
public currencyDisplayType?: number;
|
||||||
|
public expenseAmountColor?: number;
|
||||||
|
public incomeAmountColor?: number;
|
||||||
|
|
||||||
private constructor(language: string, defaultCurrency: string, firstDayOfWeek: number) {
|
private constructor(language: string, defaultCurrency: string, firstDayOfWeek: number) {
|
||||||
this.language = language;
|
this.language = language;
|
||||||
this.defaultCurrency = defaultCurrency;
|
this.defaultCurrency = defaultCurrency;
|
||||||
this.firstDayOfWeek = firstDayOfWeek;
|
this.firstDayOfWeek = firstDayOfWeek;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public toRegisterRequest(categories?: LocalizedPresetCategory[]): UserRegisterRequest {
|
||||||
|
return {
|
||||||
|
username: this.username,
|
||||||
|
email: this.email,
|
||||||
|
nickname: this.nickname,
|
||||||
|
password: this.password,
|
||||||
|
language: this.language,
|
||||||
|
defaultCurrency: this.defaultCurrency,
|
||||||
|
firstDayOfWeek: this.firstDayOfWeek,
|
||||||
|
categories: categories
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public toProfileUpdateRequest(currentPassword?: string): UserProfileUpdateRequest {
|
||||||
|
return {
|
||||||
|
email: this.email,
|
||||||
|
nickname: this.nickname,
|
||||||
|
password: this.password,
|
||||||
|
oldPassword: currentPassword,
|
||||||
|
defaultAccountId: this.defaultAccountId,
|
||||||
|
transactionEditScope: this.transactionEditScope,
|
||||||
|
language: this.language,
|
||||||
|
defaultCurrency: this.defaultCurrency,
|
||||||
|
firstDayOfWeek: this.firstDayOfWeek,
|
||||||
|
longDateFormat: this.longDateFormat,
|
||||||
|
shortDateFormat: this.shortDateFormat,
|
||||||
|
longTimeFormat: this.longTimeFormat,
|
||||||
|
shortTimeFormat: this.shortTimeFormat,
|
||||||
|
decimalSeparator: this.decimalSeparator,
|
||||||
|
digitGroupingSymbol: this.digitGroupingSymbol,
|
||||||
|
digitGrouping: this.digitGrouping,
|
||||||
|
currencyDisplayType: this.currencyDisplayType,
|
||||||
|
expenseAmountColor: this.expenseAmountColor,
|
||||||
|
incomeAmountColor: this.incomeAmountColor
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static of(profileResponse: UserProfileResponse): User {
|
||||||
|
const user = new User(profileResponse.language, profileResponse.defaultCurrency, profileResponse.firstDayOfWeek);
|
||||||
|
user.defaultAccountId = profileResponse.defaultAccountId;
|
||||||
|
user.transactionEditScope = profileResponse.transactionEditScope;
|
||||||
|
user.longDateFormat = profileResponse.longDateFormat;
|
||||||
|
user.shortDateFormat = profileResponse.shortDateFormat;
|
||||||
|
user.longTimeFormat = profileResponse.longTimeFormat;
|
||||||
|
user.shortTimeFormat = profileResponse.shortTimeFormat;
|
||||||
|
user.decimalSeparator = profileResponse.decimalSeparator;
|
||||||
|
user.digitGroupingSymbol = profileResponse.digitGroupingSymbol;
|
||||||
|
user.digitGrouping = profileResponse.digitGrouping;
|
||||||
|
user.currencyDisplayType = profileResponse.currencyDisplayType;
|
||||||
|
user.expenseAmountColor = profileResponse.expenseAmountColor;
|
||||||
|
user.incomeAmountColor = profileResponse.incomeAmountColor;
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
public static createNewUser(language: string, defaultCurrency: string, firstDayOfWeek: number): User {
|
public static createNewUser(language: string, defaultCurrency: string, firstDayOfWeek: number): User {
|
||||||
return new User(language, defaultCurrency, firstDayOfWeek);
|
return new User(language, defaultCurrency, firstDayOfWeek);
|
||||||
}
|
}
|
||||||
@@ -64,7 +131,7 @@ export interface UserRegisterRequest {
|
|||||||
readonly language: string;
|
readonly language: string;
|
||||||
readonly defaultCurrency: string;
|
readonly defaultCurrency: string;
|
||||||
readonly firstDayOfWeek: number;
|
readonly firstDayOfWeek: number;
|
||||||
readonly categories?: TransactionCategoryCreateBatchRequest;
|
readonly categories?: LocalizedPresetCategory[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserVerifyEmailResponse {
|
export interface UserVerifyEmailResponse {
|
||||||
|
|||||||
@@ -1,531 +0,0 @@
|
|||||||
import { defineStore } from 'pinia';
|
|
||||||
|
|
||||||
import { useSettingsStore } from './setting.ts';
|
|
||||||
import { useUserStore } from './user.ts';
|
|
||||||
import { useAccountsStore } from './account.ts';
|
|
||||||
import { useTransactionCategoriesStore } from './transactionCategory.ts';
|
|
||||||
import { useTransactionTagsStore } from './transactionTag.ts';
|
|
||||||
import { useTransactionTemplatesStore } from './transactionTemplate.js';
|
|
||||||
import { useTransactionsStore } from './transaction.js';
|
|
||||||
import { useOverviewStore } from './overview.ts';
|
|
||||||
import { useStatisticsStore } from './statistics.js';
|
|
||||||
import { useExchangeRatesStore } from './exchangeRates.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';
|
|
||||||
|
|
||||||
export const useRootStore = defineStore('root', {
|
|
||||||
state: () => ({
|
|
||||||
currentNotification: null
|
|
||||||
}),
|
|
||||||
actions: {
|
|
||||||
resetAllStates(resetUserInfoAndSettings) {
|
|
||||||
if (resetUserInfoAndSettings) {
|
|
||||||
const exchangeRatesStore = useExchangeRatesStore();
|
|
||||||
exchangeRatesStore.resetLatestExchangeRates();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setNotificationContent(null);
|
|
||||||
|
|
||||||
const statisticsStore = useStatisticsStore();
|
|
||||||
statisticsStore.resetTransactionStatistics();
|
|
||||||
|
|
||||||
const overviewStore = useOverviewStore();
|
|
||||||
overviewStore.resetTransactionOverview();
|
|
||||||
|
|
||||||
const transactionsStore = useTransactionsStore();
|
|
||||||
transactionsStore.resetTransactions();
|
|
||||||
|
|
||||||
const transactionTagsStore = useTransactionTagsStore();
|
|
||||||
transactionTagsStore.resetTransactionTags();
|
|
||||||
|
|
||||||
const transactionCategoriesStore = useTransactionCategoriesStore();
|
|
||||||
transactionCategoriesStore.resetTransactionCategories();
|
|
||||||
|
|
||||||
const transactionTemplatesStore = useTransactionTemplatesStore();
|
|
||||||
transactionTemplatesStore.resetTransactionTemplates();
|
|
||||||
|
|
||||||
const accountsStore = useAccountsStore();
|
|
||||||
accountsStore.resetAccounts();
|
|
||||||
|
|
||||||
if (resetUserInfoAndSettings) {
|
|
||||||
const userStore = useUserStore();
|
|
||||||
userStore.resetUserBasicInfo();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
authorize({ loginName, password }) {
|
|
||||||
const settingsStore = useSettingsStore();
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
services.authorize({
|
|
||||||
loginName: loginName,
|
|
||||||
password: password
|
|
||||||
}).then(response => {
|
|
||||||
const data = response.data;
|
|
||||||
|
|
||||||
if (!data || !data.success || !data.result || !data.result.token) {
|
|
||||||
reject({ message: 'Unable to log in' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.result.need2FA) {
|
|
||||||
resolve(data.result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settingsStore.appSettings.applicationLock || hasUserAppLockState()) {
|
|
||||||
const appLockState = getUserAppLockState();
|
|
||||||
|
|
||||||
if (!appLockState || appLockState.username !== data.result.user.username) {
|
|
||||||
clearCurrentTokenAndUserInfo(true);
|
|
||||||
settingsStore.setEnableApplicationLock(false);
|
|
||||||
settingsStore.setEnableApplicationLockWebAuthn(false);
|
|
||||||
clearWebAuthnConfig();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateCurrentToken(data.result.token);
|
|
||||||
|
|
||||||
if (data.result.user && isObject(data.result.user)) {
|
|
||||||
const userStore = useUserStore();
|
|
||||||
userStore.storeUserBasicInfo(data.result.user);
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(data.result);
|
|
||||||
}).catch(error => {
|
|
||||||
logger.error('failed to login', error);
|
|
||||||
|
|
||||||
if (error && error.processed) {
|
|
||||||
reject(error);
|
|
||||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
|
||||||
reject({ error: error.response.data });
|
|
||||||
} else {
|
|
||||||
reject({ message: 'Unable to log in' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
authorize2FA({ token, passcode, recoveryCode }) {
|
|
||||||
const settingsStore = useSettingsStore();
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
let promise = null;
|
|
||||||
|
|
||||||
if (passcode) {
|
|
||||||
promise = services.authorize2FA({
|
|
||||||
passcode: passcode,
|
|
||||||
token: token
|
|
||||||
});
|
|
||||||
} else if (recoveryCode) {
|
|
||||||
promise = services.authorize2FAByBackupCode({
|
|
||||||
recoveryCode: recoveryCode,
|
|
||||||
token: token
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
reject({ message: 'An error occurred' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
promise.then(response => {
|
|
||||||
const data = response.data;
|
|
||||||
|
|
||||||
if (!data || !data.success || !data.result || !data.result.token) {
|
|
||||||
reject({ message: 'Unable to verify' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settingsStore.appSettings.applicationLock || hasUserAppLockState()) {
|
|
||||||
const appLockState = getUserAppLockState();
|
|
||||||
|
|
||||||
if (!appLockState || appLockState.username !== data.result.user.username) {
|
|
||||||
clearCurrentTokenAndUserInfo(true);
|
|
||||||
settingsStore.setEnableApplicationLock(false);
|
|
||||||
settingsStore.setEnableApplicationLockWebAuthn(false);
|
|
||||||
clearWebAuthnConfig();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateCurrentToken(data.result.token);
|
|
||||||
|
|
||||||
if (data.result.user && isObject(data.result.user)) {
|
|
||||||
const userStore = useUserStore();
|
|
||||||
userStore.storeUserBasicInfo(data.result.user);
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(data.result);
|
|
||||||
}).catch(error => {
|
|
||||||
logger.error('failed to verify 2fa', error);
|
|
||||||
|
|
||||||
if (error && error.processed) {
|
|
||||||
reject(error);
|
|
||||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
|
||||||
reject({ error: error.response.data });
|
|
||||||
} else {
|
|
||||||
reject({ message: 'Unable to verify' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
register({ user, presetCategories }) {
|
|
||||||
const settingsStore = useSettingsStore();
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
services.register({
|
|
||||||
username: user.username,
|
|
||||||
password: user.password,
|
|
||||||
email: user.email,
|
|
||||||
nickname: user.nickname,
|
|
||||||
language: user.language,
|
|
||||||
defaultCurrency: user.defaultCurrency,
|
|
||||||
firstDayOfWeek: user.firstDayOfWeek,
|
|
||||||
categories: presetCategories
|
|
||||||
}).then(response => {
|
|
||||||
const data = response.data;
|
|
||||||
|
|
||||||
if (!data || !data.success || !data.result) {
|
|
||||||
reject({ message: 'Unable to sign up' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settingsStore.appSettings.applicationLock) {
|
|
||||||
settingsStore.setEnableApplicationLock(false);
|
|
||||||
settingsStore.setEnableApplicationLockWebAuthn(false);
|
|
||||||
clearWebAuthnConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.result.token && isString(data.result.token)) {
|
|
||||||
updateCurrentToken(data.result.token);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.result.user && isObject(data.result.user)) {
|
|
||||||
const userStore = useUserStore();
|
|
||||||
userStore.storeUserBasicInfo(data.result.user);
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(data.result);
|
|
||||||
}).catch(error => {
|
|
||||||
logger.error('failed to sign up', error);
|
|
||||||
|
|
||||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
|
||||||
reject({ error: error.response.data });
|
|
||||||
} else if (!error.processed) {
|
|
||||||
reject({ message: 'Unable to sign up' });
|
|
||||||
} else {
|
|
||||||
reject(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
lock() {
|
|
||||||
clearCurrentSessionToken();
|
|
||||||
this.resetAllStates(false);
|
|
||||||
},
|
|
||||||
logout() {
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
services.logout().then(response => {
|
|
||||||
const data = response.data;
|
|
||||||
|
|
||||||
if (!data || !data.success || !data.result) {
|
|
||||||
reject({ message: 'Unable to logout' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearCurrentTokenAndUserInfo(true);
|
|
||||||
clearWebAuthnConfig();
|
|
||||||
self.resetAllStates(true);
|
|
||||||
|
|
||||||
resolve(data.result);
|
|
||||||
}).catch(error => {
|
|
||||||
logger.error('failed to log out', error);
|
|
||||||
|
|
||||||
if (error && error.processed) {
|
|
||||||
reject(error);
|
|
||||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
|
||||||
reject({ error: error.response.data });
|
|
||||||
} else {
|
|
||||||
reject({ message: 'Unable to logout' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
forceLogout() {
|
|
||||||
clearCurrentTokenAndUserInfo(true);
|
|
||||||
clearWebAuthnConfig();
|
|
||||||
this.resetAllStates(true);
|
|
||||||
},
|
|
||||||
verifyEmail({ token, requestNewToken }) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
services.verifyEmail({
|
|
||||||
token,
|
|
||||||
requestNewToken
|
|
||||||
}).then(response => {
|
|
||||||
const data = response.data;
|
|
||||||
|
|
||||||
if (!data || !data.success || !data.result) {
|
|
||||||
reject({ message: 'Unable to verify email' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.result.newToken && isString(data.result.newToken)) {
|
|
||||||
updateCurrentToken(data.result.newToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.result.user && isObject(data.result.user)) {
|
|
||||||
const userStore = useUserStore();
|
|
||||||
userStore.storeUserBasicInfo(data.result.user);
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(data.result);
|
|
||||||
}).catch(error => {
|
|
||||||
logger.error('failed to verify email', error);
|
|
||||||
|
|
||||||
if (error && error.processed) {
|
|
||||||
reject(error);
|
|
||||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
|
||||||
reject({ error: error.response.data });
|
|
||||||
} else {
|
|
||||||
reject({ message: 'Unable to verify email' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
resendVerifyEmailByUnloginUser({ email, password }) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
services.resendVerifyEmailByUnloginUser({
|
|
||||||
email,
|
|
||||||
password
|
|
||||||
}).then(response => {
|
|
||||||
const data = response.data;
|
|
||||||
|
|
||||||
if (!data || !data.success || !data.result) {
|
|
||||||
reject({ message: 'Unable to resend validation email' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(data.result);
|
|
||||||
}).catch(error => {
|
|
||||||
logger.error('failed to resend verify email', error);
|
|
||||||
|
|
||||||
if (error && error.processed) {
|
|
||||||
reject(error);
|
|
||||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
|
||||||
reject({ error: error.response.data });
|
|
||||||
} else {
|
|
||||||
reject({ message: 'Unable to resend validation email' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
requestResetPassword({ email }) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
services.requestResetPassword({
|
|
||||||
email
|
|
||||||
}).then(response => {
|
|
||||||
const data = response.data;
|
|
||||||
|
|
||||||
if (!data || !data.success || !data.result) {
|
|
||||||
reject({ message: 'Unable to send password reset email' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(data.result);
|
|
||||||
}).catch(error => {
|
|
||||||
logger.error('failed to send password reset email', error);
|
|
||||||
|
|
||||||
if (error && error.processed) {
|
|
||||||
reject(error);
|
|
||||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
|
||||||
reject({ error: error.response.data });
|
|
||||||
} else {
|
|
||||||
reject({ message: 'Unable to send password reset email' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
resetPassword({ email, token, password }) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
services.resetPassword({
|
|
||||||
token,
|
|
||||||
email,
|
|
||||||
password
|
|
||||||
}).then(response => {
|
|
||||||
const data = response.data;
|
|
||||||
|
|
||||||
if (!data || !data.success || !data.result) {
|
|
||||||
reject({ message: 'Unable to reset password' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(data.result);
|
|
||||||
}).catch(error => {
|
|
||||||
logger.error('failed to reset password', error);
|
|
||||||
|
|
||||||
if (error && error.processed) {
|
|
||||||
reject(error);
|
|
||||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
|
||||||
reject({ error: error.response.data });
|
|
||||||
} else {
|
|
||||||
reject({ message: 'Unable to reset password' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
updateUserProfile({ profile, currentPassword }) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
services.updateProfile({
|
|
||||||
password: profile.password,
|
|
||||||
oldPassword: currentPassword,
|
|
||||||
email: profile.email,
|
|
||||||
nickname: profile.nickname,
|
|
||||||
defaultAccountId: profile.defaultAccountId,
|
|
||||||
transactionEditScope: profile.transactionEditScope,
|
|
||||||
language: profile.language,
|
|
||||||
defaultCurrency: profile.defaultCurrency,
|
|
||||||
firstDayOfWeek: profile.firstDayOfWeek,
|
|
||||||
longDateFormat: profile.longDateFormat,
|
|
||||||
shortDateFormat: profile.shortDateFormat,
|
|
||||||
longTimeFormat: profile.longTimeFormat,
|
|
||||||
shortTimeFormat: profile.shortTimeFormat,
|
|
||||||
decimalSeparator: profile.decimalSeparator,
|
|
||||||
digitGroupingSymbol: profile.digitGroupingSymbol,
|
|
||||||
digitGrouping: profile.digitGrouping,
|
|
||||||
currencyDisplayType: profile.currencyDisplayType,
|
|
||||||
expenseAmountColor: profile.expenseAmountColor,
|
|
||||||
incomeAmountColor: profile.incomeAmountColor
|
|
||||||
}).then(response => {
|
|
||||||
const data = response.data;
|
|
||||||
|
|
||||||
if (!data || !data.success || !data.result) {
|
|
||||||
reject({ message: 'Unable to update user profile' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.result.newToken && isString(data.result.newToken)) {
|
|
||||||
updateCurrentToken(data.result.newToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.result.user && isObject(data.result.user)) {
|
|
||||||
const userStore = useUserStore();
|
|
||||||
userStore.storeUserBasicInfo(data.result.user);
|
|
||||||
}
|
|
||||||
|
|
||||||
const accountsStore = useAccountsStore();
|
|
||||||
if (!accountsStore.accountListStateInvalid) {
|
|
||||||
accountsStore.updateAccountListInvalidState(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const overviewStore = useOverviewStore();
|
|
||||||
if (!overviewStore.transactionOverviewStateInvalid) {
|
|
||||||
overviewStore.updateTransactionOverviewInvalidState(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const statisticsStore = useStatisticsStore();
|
|
||||||
if (!statisticsStore.transactionStatisticsStateInvalid) {
|
|
||||||
statisticsStore.updateTransactionStatisticsInvalidState(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(data.result);
|
|
||||||
}).catch(error => {
|
|
||||||
logger.error('failed to save user profile', error);
|
|
||||||
|
|
||||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
|
||||||
reject({ error: error.response.data });
|
|
||||||
} else if (!error.processed) {
|
|
||||||
reject({ message: 'Unable to update user profile' });
|
|
||||||
} else {
|
|
||||||
reject(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
resendVerifyEmailByLoginedUser() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
services.resendVerifyEmailByLoginedUser().then(response => {
|
|
||||||
const data = response.data;
|
|
||||||
|
|
||||||
if (!data || !data.success || !data.result) {
|
|
||||||
reject({ message: 'Unable to resend validation email' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(data.result);
|
|
||||||
}).catch(error => {
|
|
||||||
logger.error('failed to resend verify email', error);
|
|
||||||
|
|
||||||
if (error && error.processed) {
|
|
||||||
reject(error);
|
|
||||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
|
||||||
reject({ error: error.response.data });
|
|
||||||
} else {
|
|
||||||
reject({ message: 'Unable to resend validation email' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
clearUserData({ password }) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
services.clearData({
|
|
||||||
password: password
|
|
||||||
}).then(response => {
|
|
||||||
const data = response.data;
|
|
||||||
|
|
||||||
if (!data || !data.success || !data.result) {
|
|
||||||
reject({ message: 'Unable to clear user data' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const accountsStore = useAccountsStore();
|
|
||||||
if (!accountsStore.accountListStateInvalid) {
|
|
||||||
accountsStore.updateAccountListInvalidState(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const transactionCategoriesStore = useTransactionCategoriesStore();
|
|
||||||
if (!transactionCategoriesStore.transactionCategoryListStateInvalid) {
|
|
||||||
transactionCategoriesStore.updateTransactionCategoryListInvalidState(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const transactionTagsStore = useTransactionTagsStore();
|
|
||||||
if (!transactionTagsStore.transactionTagListStateInvalid) {
|
|
||||||
transactionTagsStore.updateTransactionTagListInvalidState(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const overviewStore = useOverviewStore();
|
|
||||||
if (!overviewStore.transactionOverviewStateInvalid) {
|
|
||||||
overviewStore.updateTransactionOverviewInvalidState(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const statisticsStore = useStatisticsStore();
|
|
||||||
if (!statisticsStore.transactionStatisticsStateInvalid) {
|
|
||||||
statisticsStore.updateTransactionStatisticsInvalidState(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(data.result);
|
|
||||||
}).catch(error => {
|
|
||||||
logger.error('failed to clear user data', error);
|
|
||||||
|
|
||||||
if (error && error.processed) {
|
|
||||||
reject(error);
|
|
||||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
|
||||||
reject({ error: error.response.data });
|
|
||||||
} else {
|
|
||||||
reject({ message: 'Unable to clear user data' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
setNotificationContent(content) {
|
|
||||||
this.currentNotification = content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,529 @@
|
|||||||
|
import { ref } from 'vue';
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
import { useSettingsStore } from './setting.ts';
|
||||||
|
import { useUserStore } from './user.ts';
|
||||||
|
import { useAccountsStore } from './account.ts';
|
||||||
|
import { useTransactionCategoriesStore } from './transactionCategory.ts';
|
||||||
|
import { useTransactionTagsStore } from './transactionTag.ts';
|
||||||
|
// @ts-expect-error the above file is migrating to ts
|
||||||
|
import { useTransactionTemplatesStore } from './transactionTemplate.js';
|
||||||
|
// @ts-expect-error the above file is migrating to ts
|
||||||
|
import { useTransactionsStore } from './transaction.js';
|
||||||
|
import { useOverviewStore } from './overview.ts';
|
||||||
|
// @ts-expect-error the above file is migrating to ts
|
||||||
|
import { useStatisticsStore } from './statistics.js';
|
||||||
|
import { useExchangeRatesStore } from './exchangeRates.ts';
|
||||||
|
|
||||||
|
import type { AuthResponse, RegisterResponse } from '@/models/auth_response.ts';
|
||||||
|
import type { User, UserLoginRequest, UserResendVerifyEmailRequest, UserVerifyEmailResponse, UserProfileUpdateResponse } from '@/models/user.ts';
|
||||||
|
import type { LocalizedPresetCategory } from '@/core/category.ts';
|
||||||
|
import type { ForgetPasswordRequest } from '@/models/forget_password.ts';
|
||||||
|
|
||||||
|
import {
|
||||||
|
hasUserAppLockState,
|
||||||
|
getUserAppLockState,
|
||||||
|
updateCurrentToken,
|
||||||
|
clearWebAuthnConfig,
|
||||||
|
clearCurrentSessionToken,
|
||||||
|
clearCurrentTokenAndUserInfo
|
||||||
|
} from '@/lib/userstate.ts';
|
||||||
|
import services, { type ApiResponsePromise } from '@/lib/services.ts';
|
||||||
|
import logger from '@/lib/logger.ts';
|
||||||
|
import { isObject, isString } from '@/lib/common.ts';
|
||||||
|
|
||||||
|
export const useRootStore = defineStore('root', () => {
|
||||||
|
const settingsStore = useSettingsStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const accountsStore = useAccountsStore();
|
||||||
|
const transactionCategoriesStore = useTransactionCategoriesStore();
|
||||||
|
const transactionTagsStore = useTransactionTagsStore();
|
||||||
|
const transactionTemplatesStore = useTransactionTemplatesStore();
|
||||||
|
const transactionsStore = useTransactionsStore();
|
||||||
|
const overviewStore = useOverviewStore();
|
||||||
|
const statisticsStore = useStatisticsStore();
|
||||||
|
const exchangeRatesStore = useExchangeRatesStore();
|
||||||
|
|
||||||
|
const currentNotification = ref<string | null>(null);
|
||||||
|
|
||||||
|
function resetAllStates(resetUserInfoAndSettings: boolean): void {
|
||||||
|
if (resetUserInfoAndSettings) {
|
||||||
|
exchangeRatesStore.resetLatestExchangeRates();
|
||||||
|
}
|
||||||
|
|
||||||
|
setNotificationContent(null);
|
||||||
|
|
||||||
|
statisticsStore.resetTransactionStatistics();
|
||||||
|
overviewStore.resetTransactionOverview();
|
||||||
|
transactionsStore.resetTransactions();
|
||||||
|
transactionTagsStore.resetTransactionTags();
|
||||||
|
transactionCategoriesStore.resetTransactionCategories();
|
||||||
|
transactionTemplatesStore.resetTransactionTemplates();
|
||||||
|
accountsStore.resetAccounts();
|
||||||
|
|
||||||
|
if (resetUserInfoAndSettings) {
|
||||||
|
userStore.resetUserBasicInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNotificationContent(content: string | null): void {
|
||||||
|
currentNotification.value = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
function authorize(req: UserLoginRequest): Promise<AuthResponse> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
services.authorize(req).then(response => {
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result || !data.result.token) {
|
||||||
|
reject({ message: 'Unable to log in' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.result.need2FA) {
|
||||||
|
resolve(data.result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settingsStore.appSettings.applicationLock || hasUserAppLockState()) {
|
||||||
|
const appLockState = getUserAppLockState();
|
||||||
|
|
||||||
|
if (!appLockState || appLockState.username !== data.result.user?.username) {
|
||||||
|
clearCurrentTokenAndUserInfo(true);
|
||||||
|
settingsStore.setEnableApplicationLock(false);
|
||||||
|
settingsStore.setEnableApplicationLockWebAuthn(false);
|
||||||
|
clearWebAuthnConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCurrentToken(data.result.token);
|
||||||
|
|
||||||
|
if (data.result.user && isObject(data.result.user)) {
|
||||||
|
userStore.storeUserBasicInfo(data.result.user);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(data.result);
|
||||||
|
}).catch(error => {
|
||||||
|
logger.error('failed to login', error);
|
||||||
|
|
||||||
|
if (error && error.processed) {
|
||||||
|
reject(error);
|
||||||
|
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||||
|
reject({ error: error.response.data });
|
||||||
|
} else {
|
||||||
|
reject({ message: 'Unable to log in' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function authorize2FA({ token, passcode, recoveryCode }: { token: string, passcode: string | null, recoveryCode: string | null }): Promise<AuthResponse> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let promise: ApiResponsePromise<AuthResponse>;
|
||||||
|
|
||||||
|
if (passcode) {
|
||||||
|
promise = services.authorize2FA({
|
||||||
|
passcode: passcode,
|
||||||
|
token: token
|
||||||
|
});
|
||||||
|
} else if (recoveryCode) {
|
||||||
|
promise = services.authorize2FAByBackupCode({
|
||||||
|
recoveryCode: recoveryCode,
|
||||||
|
token: token
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
reject({ message: 'An error occurred' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.then(response => {
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result || !data.result.token) {
|
||||||
|
reject({ message: 'Unable to verify' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settingsStore.appSettings.applicationLock || hasUserAppLockState()) {
|
||||||
|
const appLockState = getUserAppLockState();
|
||||||
|
|
||||||
|
if (!appLockState || appLockState.username !== data.result.user?.username) {
|
||||||
|
clearCurrentTokenAndUserInfo(true);
|
||||||
|
settingsStore.setEnableApplicationLock(false);
|
||||||
|
settingsStore.setEnableApplicationLockWebAuthn(false);
|
||||||
|
clearWebAuthnConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCurrentToken(data.result.token);
|
||||||
|
|
||||||
|
if (data.result.user && isObject(data.result.user)) {
|
||||||
|
userStore.storeUserBasicInfo(data.result.user);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(data.result);
|
||||||
|
}).catch(error => {
|
||||||
|
logger.error('failed to verify 2fa', error);
|
||||||
|
|
||||||
|
if (error && error.processed) {
|
||||||
|
reject(error);
|
||||||
|
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||||
|
reject({ error: error.response.data });
|
||||||
|
} else {
|
||||||
|
reject({ message: 'Unable to verify' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function register({ user, presetCategories }: { user: User, presetCategories?: LocalizedPresetCategory[] }): Promise<RegisterResponse> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
services.register(user.toRegisterRequest(presetCategories)).then(response => {
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result) {
|
||||||
|
reject({ message: 'Unable to sign up' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settingsStore.appSettings.applicationLock) {
|
||||||
|
settingsStore.setEnableApplicationLock(false);
|
||||||
|
settingsStore.setEnableApplicationLockWebAuthn(false);
|
||||||
|
clearWebAuthnConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.result.token && isString(data.result.token)) {
|
||||||
|
updateCurrentToken(data.result.token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.result.user && isObject(data.result.user)) {
|
||||||
|
userStore.storeUserBasicInfo(data.result.user);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(data.result);
|
||||||
|
}).catch(error => {
|
||||||
|
logger.error('failed to sign up', error);
|
||||||
|
|
||||||
|
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||||
|
reject({ error: error.response.data });
|
||||||
|
} else if (!error.processed) {
|
||||||
|
reject({ message: 'Unable to sign up' });
|
||||||
|
} else {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function lock(): void {
|
||||||
|
clearCurrentSessionToken();
|
||||||
|
resetAllStates(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function logout(): Promise<boolean> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
services.logout().then(response => {
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result) {
|
||||||
|
reject({ message: 'Unable to logout' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCurrentTokenAndUserInfo(true);
|
||||||
|
clearWebAuthnConfig();
|
||||||
|
resetAllStates(true);
|
||||||
|
|
||||||
|
resolve(data.result);
|
||||||
|
}).catch(error => {
|
||||||
|
logger.error('failed to log out', error);
|
||||||
|
|
||||||
|
if (error && error.processed) {
|
||||||
|
reject(error);
|
||||||
|
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||||
|
reject({ error: error.response.data });
|
||||||
|
} else {
|
||||||
|
reject({ message: 'Unable to logout' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function forceLogout(): void {
|
||||||
|
clearCurrentTokenAndUserInfo(true);
|
||||||
|
clearWebAuthnConfig();
|
||||||
|
resetAllStates(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyEmail({ token, requestNewToken }: { token: string, requestNewToken: boolean }): Promise<UserVerifyEmailResponse> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
services.verifyEmail({
|
||||||
|
token,
|
||||||
|
requestNewToken
|
||||||
|
}).then(response => {
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result) {
|
||||||
|
reject({ message: 'Unable to verify email' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.result.newToken && isString(data.result.newToken)) {
|
||||||
|
updateCurrentToken(data.result.newToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.result.user && isObject(data.result.user)) {
|
||||||
|
userStore.storeUserBasicInfo(data.result.user);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(data.result);
|
||||||
|
}).catch(error => {
|
||||||
|
logger.error('failed to verify email', error);
|
||||||
|
|
||||||
|
if (error && error.processed) {
|
||||||
|
reject(error);
|
||||||
|
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||||
|
reject({ error: error.response.data });
|
||||||
|
} else {
|
||||||
|
reject({ message: 'Unable to verify email' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function resendVerifyEmailByUnloginUser(req: UserResendVerifyEmailRequest): Promise<boolean> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
services.resendVerifyEmailByUnloginUser(req).then(response => {
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result) {
|
||||||
|
reject({ message: 'Unable to resend validation email' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(data.result);
|
||||||
|
}).catch(error => {
|
||||||
|
logger.error('failed to resend verify email', error);
|
||||||
|
|
||||||
|
if (error && error.processed) {
|
||||||
|
reject(error);
|
||||||
|
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||||
|
reject({ error: error.response.data });
|
||||||
|
} else {
|
||||||
|
reject({ message: 'Unable to resend validation email' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestResetPassword(req: ForgetPasswordRequest): Promise<boolean> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
services.requestResetPassword(req).then(response => {
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result) {
|
||||||
|
reject({ message: 'Unable to send password reset email' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(data.result);
|
||||||
|
}).catch(error => {
|
||||||
|
logger.error('failed to send password reset email', error);
|
||||||
|
|
||||||
|
if (error && error.processed) {
|
||||||
|
reject(error);
|
||||||
|
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||||
|
reject({ error: error.response.data });
|
||||||
|
} else {
|
||||||
|
reject({ message: 'Unable to send password reset email' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetPassword({ email, token, password }: { email: string, token: string, password: string }): Promise<boolean> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
services.resetPassword({
|
||||||
|
email,
|
||||||
|
token,
|
||||||
|
password
|
||||||
|
}).then(response => {
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result) {
|
||||||
|
reject({ message: 'Unable to reset password' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(data.result);
|
||||||
|
}).catch(error => {
|
||||||
|
logger.error('failed to reset password', error);
|
||||||
|
|
||||||
|
if (error && error.processed) {
|
||||||
|
reject(error);
|
||||||
|
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||||
|
reject({ error: error.response.data });
|
||||||
|
} else {
|
||||||
|
reject({ message: 'Unable to reset password' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUserProfile({ profile, currentPassword }: { profile: User, currentPassword?: string }): Promise<UserProfileUpdateResponse> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
services.updateProfile({
|
||||||
|
password: profile.password,
|
||||||
|
oldPassword: currentPassword,
|
||||||
|
email: profile.email,
|
||||||
|
nickname: profile.nickname,
|
||||||
|
defaultAccountId: profile.defaultAccountId,
|
||||||
|
transactionEditScope: profile.transactionEditScope,
|
||||||
|
language: profile.language,
|
||||||
|
defaultCurrency: profile.defaultCurrency,
|
||||||
|
firstDayOfWeek: profile.firstDayOfWeek,
|
||||||
|
longDateFormat: profile.longDateFormat,
|
||||||
|
shortDateFormat: profile.shortDateFormat,
|
||||||
|
longTimeFormat: profile.longTimeFormat,
|
||||||
|
shortTimeFormat: profile.shortTimeFormat,
|
||||||
|
decimalSeparator: profile.decimalSeparator,
|
||||||
|
digitGroupingSymbol: profile.digitGroupingSymbol,
|
||||||
|
digitGrouping: profile.digitGrouping,
|
||||||
|
currencyDisplayType: profile.currencyDisplayType,
|
||||||
|
expenseAmountColor: profile.expenseAmountColor,
|
||||||
|
incomeAmountColor: profile.incomeAmountColor
|
||||||
|
}).then(response => {
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result) {
|
||||||
|
reject({ message: 'Unable to update user profile' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.result.newToken && isString(data.result.newToken)) {
|
||||||
|
updateCurrentToken(data.result.newToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.result.user && isObject(data.result.user)) {
|
||||||
|
userStore.storeUserBasicInfo(data.result.user);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!accountsStore.accountListStateInvalid) {
|
||||||
|
accountsStore.updateAccountListInvalidState(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!overviewStore.transactionOverviewStateInvalid) {
|
||||||
|
overviewStore.updateTransactionOverviewInvalidState(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!statisticsStore.transactionStatisticsStateInvalid) {
|
||||||
|
statisticsStore.updateTransactionStatisticsInvalidState(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(data.result);
|
||||||
|
}).catch(error => {
|
||||||
|
logger.error('failed to save user profile', error);
|
||||||
|
|
||||||
|
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||||
|
reject({ error: error.response.data });
|
||||||
|
} else if (!error.processed) {
|
||||||
|
reject({ message: 'Unable to update user profile' });
|
||||||
|
} else {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function resendVerifyEmailByLoginedUser(): Promise<boolean> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
services.resendVerifyEmailByLoginedUser().then(response => {
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result) {
|
||||||
|
reject({ message: 'Unable to resend validation email' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(data.result);
|
||||||
|
}).catch(error => {
|
||||||
|
logger.error('failed to resend verify email', error);
|
||||||
|
|
||||||
|
if (error && error.processed) {
|
||||||
|
reject(error);
|
||||||
|
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||||
|
reject({ error: error.response.data });
|
||||||
|
} else {
|
||||||
|
reject({ message: 'Unable to resend validation email' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearUserData({ password }: { password: string }): Promise<boolean> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
services.clearData({
|
||||||
|
password: password
|
||||||
|
}).then(response => {
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result) {
|
||||||
|
reject({ message: 'Unable to clear user data' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!accountsStore.accountListStateInvalid) {
|
||||||
|
accountsStore.updateAccountListInvalidState(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!transactionCategoriesStore.transactionCategoryListStateInvalid) {
|
||||||
|
transactionCategoriesStore.updateTransactionCategoryListInvalidState(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!transactionTagsStore.transactionTagListStateInvalid) {
|
||||||
|
transactionTagsStore.updateTransactionTagListInvalidState(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!overviewStore.transactionOverviewStateInvalid) {
|
||||||
|
overviewStore.updateTransactionOverviewInvalidState(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!statisticsStore.transactionStatisticsStateInvalid) {
|
||||||
|
statisticsStore.updateTransactionStatisticsInvalidState(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(data.result);
|
||||||
|
}).catch(error => {
|
||||||
|
logger.error('failed to clear user data', error);
|
||||||
|
|
||||||
|
if (error && error.processed) {
|
||||||
|
reject(error);
|
||||||
|
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||||
|
reject({ error: error.response.data });
|
||||||
|
} else {
|
||||||
|
reject({ message: 'Unable to clear user data' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
// states
|
||||||
|
currentNotification,
|
||||||
|
// functions
|
||||||
|
setNotificationContent,
|
||||||
|
authorize,
|
||||||
|
authorize2FA,
|
||||||
|
register,
|
||||||
|
lock,
|
||||||
|
logout,
|
||||||
|
forceLogout,
|
||||||
|
verifyEmail,
|
||||||
|
resendVerifyEmailByUnloginUser,
|
||||||
|
requestResetPassword,
|
||||||
|
resetPassword,
|
||||||
|
updateUserProfile,
|
||||||
|
resendVerifyEmailByLoginedUser,
|
||||||
|
clearUserData
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -2,8 +2,7 @@ import { ref, computed } from 'vue';
|
|||||||
|
|
||||||
import { useI18n } from '@/locales/helpers.ts';
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
|
|
||||||
// @ts-expect-error the above file is migrating to ts
|
import { useRootStore } from '@/stores/index.ts';
|
||||||
import { useRootStore } from '@/stores/index.js';
|
|
||||||
import { useSettingsStore } from '@/stores/setting.ts';
|
import { useSettingsStore } from '@/stores/setting.ts';
|
||||||
import { useExchangeRatesStore } from '@/stores/exchangeRates.ts';
|
import { useExchangeRatesStore } from '@/stores/exchangeRates.ts';
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import { ref, computed } from 'vue';
|
|||||||
|
|
||||||
import { useI18n } from '@/locales/helpers.ts';
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
|
|
||||||
// @ts-expect-error the above file is migrating to ts
|
import { useRootStore } from '@/stores/index.ts';
|
||||||
import { useRootStore } from '@/stores/index.js';
|
|
||||||
import { useSettingsStore } from '@/stores/setting.ts';
|
import { useSettingsStore } from '@/stores/setting.ts';
|
||||||
import { useUserStore } from '@/stores/user.ts';
|
import { useUserStore } from '@/stores/user.ts';
|
||||||
import { useExchangeRatesStore } from '@/stores/exchangeRates.ts';
|
import { useExchangeRatesStore } from '@/stores/exchangeRates.ts';
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import { ref, computed } from 'vue';
|
|||||||
|
|
||||||
import { useI18n } from '@/locales/helpers.ts';
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
|
|
||||||
// @ts-expect-error the above file is migrating to ts
|
import { useRootStore } from '@/stores/index.ts';
|
||||||
import { useRootStore } from '@/stores/index.js';
|
|
||||||
import { useSettingsStore } from '@/stores/setting.ts';
|
import { useSettingsStore } from '@/stores/setting.ts';
|
||||||
import { useUserStore } from '@/stores/user.ts';
|
import { useUserStore } from '@/stores/user.ts';
|
||||||
import { useTokensStore } from '@/stores/token.ts';
|
import { useTokensStore } from '@/stores/token.ts';
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ import { useTheme } from 'vuetify';
|
|||||||
import type { LanguageOption } from '@/locales/index.ts';
|
import type { LanguageOption } from '@/locales/index.ts';
|
||||||
import { useI18n } from '@/locales/helpers.ts';
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
|
|
||||||
import { useRootStore } from '@/stores/index.js';
|
import { useRootStore } from '@/stores/index.ts';
|
||||||
import { useSettingsStore } from '@/stores/setting.ts';
|
import { useSettingsStore } from '@/stores/setting.ts';
|
||||||
|
|
||||||
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
|
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ import type { LanguageOption } from '@/locales/index.ts';
|
|||||||
import { useI18n } from '@/locales/helpers.ts';
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
import { useLoginPageBase } from '@/views/base/LoginPageBase.ts';
|
import { useLoginPageBase } from '@/views/base/LoginPageBase.ts';
|
||||||
|
|
||||||
import { useRootStore } from '@/stores/index.js';
|
import { useRootStore } from '@/stores/index.ts';
|
||||||
|
|
||||||
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
|
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
|
||||||
import { KnownErrorCode } from '@/consts/api.ts';
|
import { KnownErrorCode } from '@/consts/api.ts';
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ import { useRoute, useRouter } from 'vue-router';
|
|||||||
|
|
||||||
import { useI18n } from '@/locales/helpers.ts';
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
|
|
||||||
import { useRootStore } from '@/stores/index.js';
|
import { useRootStore } from '@/stores/index.ts';
|
||||||
import { useSettingsStore } from '@/stores/setting.ts';
|
import { useSettingsStore } from '@/stores/setting.ts';
|
||||||
import { useUserStore } from '@/stores/user.ts';
|
import { useUserStore } from '@/stores/user.ts';
|
||||||
|
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ import { useTheme } from 'vuetify';
|
|||||||
import type { LanguageOption } from '@/locales/index.ts';
|
import type { LanguageOption } from '@/locales/index.ts';
|
||||||
import { useI18n } from '@/locales/helpers.ts';
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
|
|
||||||
import { useRootStore } from '@/stores/index.js';
|
import { useRootStore } from '@/stores/index.ts';
|
||||||
import { useSettingsStore } from '@/stores/setting.ts';
|
import { useSettingsStore } from '@/stores/setting.ts';
|
||||||
|
|
||||||
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
|
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ import type { LanguageOption } from '@/locales/index.ts';
|
|||||||
import { useI18n } from '@/locales/helpers.ts';
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
import { useSignupPageBase } from '@/views/base/SignupPageBase.ts';
|
import { useSignupPageBase } from '@/views/base/SignupPageBase.ts';
|
||||||
|
|
||||||
import { useRootStore } from '@/stores/index.js';
|
import { useRootStore } from '@/stores/index.ts';
|
||||||
|
|
||||||
import type { PartialRecord, TypeAndDisplayName } from '@/core/base.ts';
|
import type { PartialRecord, TypeAndDisplayName } from '@/core/base.ts';
|
||||||
import type { LocalizedCurrencyInfo } from '@/core/currency.ts';
|
import type { LocalizedCurrencyInfo } from '@/core/currency.ts';
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ import { useTheme } from 'vuetify';
|
|||||||
import type { LanguageOption } from '@/locales/index.ts';
|
import type { LanguageOption } from '@/locales/index.ts';
|
||||||
import { useI18n } from '@/locales/helpers.ts';
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
|
|
||||||
import { useRootStore } from '@/stores/index.js';
|
import { useRootStore } from '@/stores/index.ts';
|
||||||
import { useSettingsStore } from '@/stores/setting.ts';
|
import { useSettingsStore } from '@/stores/setting.ts';
|
||||||
|
|
||||||
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
|
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
|
||||||
|
|||||||
@@ -331,7 +331,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useRootStore } from '@/stores/index.js';
|
import { useRootStore } from '@/stores/index.ts';
|
||||||
import { useSettingsStore } from '@/stores/setting.ts';
|
import { useSettingsStore } from '@/stores/setting.ts';
|
||||||
import { useUserStore } from '@/stores/user.ts';
|
import { useUserStore } from '@/stores/user.ts';
|
||||||
import { useAccountsStore } from '@/stores/account.ts';
|
import { useAccountsStore } from '@/stores/account.ts';
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ import { ref, useTemplateRef } from 'vue';
|
|||||||
import { useI18n } from '@/locales/helpers.ts';
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
import { useDataManagementPageBase } from '@/views/base/users/DataManagementPageBase.ts';
|
import { useDataManagementPageBase } from '@/views/base/users/DataManagementPageBase.ts';
|
||||||
|
|
||||||
import { useRootStore } from '@/stores/index.js';
|
import { useRootStore } from '@/stores/index.ts';
|
||||||
import { useUserStore } from '@/stores/user.ts';
|
import { useUserStore } from '@/stores/user.ts';
|
||||||
|
|
||||||
import { isEquals } from '@/lib/common.ts';
|
import { isEquals } from '@/lib/common.ts';
|
||||||
|
|||||||
@@ -130,7 +130,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useRootStore } from '@/stores/index.js';
|
import { useRootStore } from '@/stores/index.ts';
|
||||||
import { useSettingsStore } from '@/stores/setting.ts';
|
import { useSettingsStore } from '@/stores/setting.ts';
|
||||||
import { useUserStore } from '@/stores/user.ts';
|
import { useUserStore } from '@/stores/user.ts';
|
||||||
import { useTokensStore } from '@/stores/token.ts';
|
import { useTokensStore } from '@/stores/token.ts';
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ import type { LanguageOption } from '@/locales/index.ts';
|
|||||||
import { useI18n } from '@/locales/helpers.ts';
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
import { useLoginPageBase } from '@/views/base/LoginPageBase.ts';
|
import { useLoginPageBase } from '@/views/base/LoginPageBase.ts';
|
||||||
|
|
||||||
import { useRootStore } from '@/stores/index.js';
|
import { useRootStore } from '@/stores/index.ts';
|
||||||
|
|
||||||
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
|
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
|
||||||
import { KnownErrorCode } from '@/consts/api.ts';
|
import { KnownErrorCode } from '@/consts/api.ts';
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ import { useI18n } from '@/locales/helpers.ts';
|
|||||||
import { useI18nUIComponents, showLoading, hideLoading } from '@/lib/ui/mobile.ts';
|
import { useI18nUIComponents, showLoading, hideLoading } from '@/lib/ui/mobile.ts';
|
||||||
import { useAppSettingPageBase } from '@/views/base/settings/AppSettingsPageBase.ts';
|
import { useAppSettingPageBase } from '@/views/base/settings/AppSettingsPageBase.ts';
|
||||||
|
|
||||||
import { useRootStore } from '@/stores/index.js';
|
import { useRootStore } from '@/stores/index.ts';
|
||||||
import { useSettingsStore } from '@/stores/setting.ts';
|
import { useSettingsStore } from '@/stores/setting.ts';
|
||||||
import { useUserStore } from '@/stores/user.ts';
|
import { useUserStore } from '@/stores/user.ts';
|
||||||
import { useExchangeRatesStore } from '@/stores/exchangeRates.ts';
|
import { useExchangeRatesStore } from '@/stores/exchangeRates.ts';
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ import { useI18n } from '@/locales/helpers.ts';
|
|||||||
import { useI18nUIComponents, showLoading, hideLoading } from '@/lib/ui/mobile.ts';
|
import { useI18nUIComponents, showLoading, hideLoading } from '@/lib/ui/mobile.ts';
|
||||||
import { useSignupPageBase } from '@/views/base/SignupPageBase.ts';
|
import { useSignupPageBase } from '@/views/base/SignupPageBase.ts';
|
||||||
|
|
||||||
import { useRootStore } from '@/stores/index.js';
|
import { useRootStore } from '@/stores/index.ts';
|
||||||
|
|
||||||
import type { PartialRecord, TypeAndDisplayName } from '@/core/base.ts';
|
import type { PartialRecord, TypeAndDisplayName } from '@/core/base.ts';
|
||||||
import type { LocalizedCurrencyInfo } from '@/core/currency.ts';
|
import type { LocalizedCurrencyInfo } from '@/core/currency.ts';
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ import { useI18n } from '@/locales/helpers.ts';
|
|||||||
import { useI18nUIComponents, showLoading, hideLoading } from '@/lib/ui/mobile.ts';
|
import { useI18nUIComponents, showLoading, hideLoading } from '@/lib/ui/mobile.ts';
|
||||||
import { useDataManagementPageBase } from '@/views/base/users/DataManagementPageBase.ts';
|
import { useDataManagementPageBase } from '@/views/base/users/DataManagementPageBase.ts';
|
||||||
|
|
||||||
import { useRootStore } from '@/stores/index.js';
|
import { useRootStore } from '@/stores/index.ts';
|
||||||
import { useUserStore } from '@/stores/user.ts';
|
import { useUserStore } from '@/stores/user.ts';
|
||||||
|
|
||||||
import { isDataExportingEnabled } from '@/lib/server_settings.ts';
|
import { isDataExportingEnabled } from '@/lib/server_settings.ts';
|
||||||
|
|||||||
@@ -333,7 +333,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useRootStore } from '@/stores/index.js';
|
import { useRootStore } from '@/stores/index.ts';
|
||||||
import { useSettingsStore } from '@/stores/setting.ts';
|
import { useSettingsStore } from '@/stores/setting.ts';
|
||||||
import { useUserStore } from '@/stores/user.ts';
|
import { useUserStore } from '@/stores/user.ts';
|
||||||
import { useAccountsStore } from '@/stores/account.ts';
|
import { useAccountsStore } from '@/stores/account.ts';
|
||||||
|
|||||||
Reference in New Issue
Block a user