import axios, { type AxiosRequestConfig, type AxiosRequestHeaders, type AxiosResponse } from 'axios'; import type { ApiResponse } from '@/core/api.ts'; import type { ApplicationCloudSetting } from '@/core/setting.ts'; import type { VersionInfo } from '@/core/version.ts'; import type { ImportFileTypeSupportedAdditionalOptions } from '@/core/file.ts'; import { TransactionType } from '@/core/transaction.ts'; import { BASE_API_URL_PATH, BASE_QRCODE_PATH, BASE_PROXY_URL_PATH, BASE_AMAP_API_PROXY_URL_PATH, DEFAULT_API_TIMEOUT, DEFAULT_UPLOAD_API_TIMEOUT, DEFAULT_EXPORT_API_TIMEOUT, DEFAULT_IMPORT_API_TIMEOUT, DEFAULT_CLEAR_ALL_TRANSACTIONS_API_TIMEOUT, DEFAULT_LLM_API_TIMEOUT, GOOGLE_MAP_JAVASCRIPT_URL, BAIDU_MAP_JAVASCRIPT_URL, AMAP_JAVASCRIPT_URL } from '@/consts/api.ts'; import type { AccountCreateRequest, AccountModifyRequest, AccountInfoResponse, AccountHideRequest, AccountMoveRequest, AccountDeleteRequest } from '@/models/account.ts'; import type { AuthResponse, RegisterResponse } from '@/models/auth_response.ts'; import type { ExportTransactionDataRequest, ClearDataRequest, ClearAccountTransactionsRequest, DataStatisticsResponse } from '@/models/data_management.ts'; import type { UserCustomExchangeRateUpdateRequest, UserCustomExchangeRateDeleteRequest, UserCustomExchangeRateUpdateResponse, LatestExchangeRateResponse } from '@/models/exchange_rate.ts'; import type { ForgetPasswordRequest } from '@/models/forget_password.ts'; import type { ImportTransactionResponsePageWrapper } from '@/models/imported_transaction.ts'; import type { TransactionCreateRequest, TransactionModifyRequest, TransactionMoveBetweenAccountsRequest, TransactionDeleteRequest, TransactionImportRequest, TransactionListByMaxTimeRequest, TransactionListInMonthByPageRequest, TransactionAllListRequest, TransactionInfoResponse, TransactionInfoPageWrapperResponse, TransactionInfoPageWrapperResponse2, TransactionReconciliationStatementRequest, TransactionReconciliationStatementResponse, TransactionStatisticRequest, TransactionStatisticResponse, TransactionStatisticTrendsRequest, TransactionStatisticTrendsResponseItem, TransactionStatisticAssetTrendsRequest, TransactionStatisticAssetTrendsResponseItem, TransactionAmountsRequestParams, TransactionAmountsResponse } from '@/models/transaction.ts'; import { TransactionAmountsRequest } from '@/models/transaction.ts'; import type { TransactionCategoryCreateRequest, TransactionCategoryCreateBatchRequest, TransactionCategoryModifyRequest, TransactionCategoryHideRequest, TransactionCategoryMoveRequest, TransactionCategoryDeleteRequest, TransactionCategoryInfoResponse } from '@/models/transaction_category.ts'; import type { TransactionPictureUnusedDeleteRequest, TransactionPictureInfoBasicResponse } from '@/models/transaction_picture_info.ts'; import type { TransactionTagGroupCreateRequest, TransactionTagGroupModifyRequest, TransactionTagGroupMoveRequest, TransactionTagGroupDeleteRequest, TransactionTagGroupInfoResponse } from '@/models/transaction_tag_group.ts'; import type { TransactionTagCreateRequest, TransactionTagCreateBatchRequest, TransactionTagModifyRequest, TransactionTagHideRequest, TransactionTagMoveRequest, TransactionTagDeleteRequest, TransactionTagInfoResponse } from '@/models/transaction_tag.ts'; import type { TransactionTemplateCreateRequest, TransactionTemplateModifyRequest, TransactionTemplateHideRequest, TransactionTemplateMoveRequest, TransactionTemplateDeleteRequest, TransactionTemplateInfoResponse } from '@/models/transaction_template.ts'; import type { InsightsExplorerCreateRequest, InsightsExplorerModifyRequest, InsightsExplorerHideRequest, InsightsExplorerMoveRequest, InsightsExplorerDeleteRequest, InsightsExplorerInfoResponse, } from '@/models/explorer.ts'; import type { TokenGenerateAPIRequest, TokenGenerateMCPRequest, TokenRevokeRequest, TokenGenerateAPIResponse, TokenGenerateMCPResponse, TokenRefreshResponse, TokenInfoResponse } from '@/models/token.ts'; import type { TwoFactorEnableConfirmRequest, TwoFactorEnableResponse, TwoFactorEnableConfirmResponse, TwoFactorDisableRequest, TwoFactorRegenerateRecoveryCodeRequest, TwoFactorStatusResponse } from '@/models/two_factor.ts'; import type { UserLoginRequest, UserRegisterRequest, UserVerifyEmailResponse, UserResendVerifyEmailRequest, UserProfileResponse, UserProfileUpdateRequest, UserProfileUpdateResponse } from '@/models/user.ts'; import type { UserExternalAuthUnlinkRequest, UserExternalAuthInfoResponse } from '@/models/user_external_auth.ts'; import type { OAuth2CallbackLoginRequest } from '@/models/oauth2.ts'; import type { UserApplicationCloudSettingsUpdateRequest } from '@/models/user_app_cloud_setting.ts'; import type { RecognizedReceiptImageResponse } from '@/models/large_language_model.ts'; import { getCurrentToken, clearCurrentTokenAndUserInfo } from './userstate.ts'; import { isDefined, isBoolean, objectFieldWithValueToArrayItem } from './common.ts'; import { getTimeZone } from './settings.ts'; import { getGoogleMapAPIKey, getBaiduMapAK, getAmapApplicationKey, getExchangeRatesRequestTimeout } from './server_settings.ts'; import { getTimezoneOffsetMinutes, getBrowserTimezoneName, getCurrentUnixTime } from './datetime.ts'; import { generateRandomUUID } from './misc.ts'; import { getBasePath } from './web.ts'; import logger from './logger.ts'; interface ApiRequestConfig extends AxiosRequestConfig { readonly headers: AxiosRequestHeaders; readonly noAuth?: boolean; readonly ignoreBlocked?: boolean; readonly ignoreError?: boolean; readonly timeout?: number; readonly cancelableUuid?: string; } export type ApiResponsePromise = Promise>>; let needBlockRequest = false; const blockedRequests: ((token: string | undefined) => void)[] = []; const cancelableRequests: Record = {}; axios.defaults.baseURL = getBasePath() + BASE_API_URL_PATH; axios.defaults.timeout = DEFAULT_API_TIMEOUT; axios.interceptors.request.use((config: ApiRequestConfig) => { const token = getCurrentToken(); if (token && !config.noAuth) { config.headers.Authorization = `Bearer ${token}`; } config.headers['X-Timezone-Offset'] = getTimezoneOffsetMinutes(getCurrentUnixTime()); let timezoneName = getTimeZone(); if (!timezoneName || timezoneName.trim().length < 1) { timezoneName = getBrowserTimezoneName(); } config.headers['X-Timezone-Name'] = timezoneName; if (needBlockRequest && !config.ignoreBlocked) { return new Promise(resolve => { blockedRequests.push(newToken => { if (newToken) { config.headers.Authorization = `Bearer ${newToken}`; } resolve(config); }); }); } return config; }, error => { return Promise.reject(error); }); axios.interceptors.response.use(response => { if ('cancelableUuid' in response.config && response.config.cancelableUuid && cancelableRequests[response.config.cancelableUuid as string]) { logger.debug('Response canceled by user request, url: ' + response.config.url + ', cancelableUuid: ' + response.config.cancelableUuid); delete cancelableRequests[response.config.cancelableUuid as string]; return Promise.reject({ canceled: true }); } return response; }, error => { if ('cancelableUuid' in error.response.config && error.response.config.cancelableUuid && cancelableRequests[error.response.config.cancelableUuid]) { logger.debug('Response canceled by user request, url: ' + error.response.config.url + ', cancelableUuid: ' + error.response.config.cancelableUuid); delete cancelableRequests[error.response.config.cancelableUuid]; return Promise.reject({ canceled: true }); } if (error.response && !error.response.config.ignoreError && error.response.data && error.response.data.errorCode) { const errorCode = error.response.data.errorCode; if (errorCode === 202001 // unauthorized access || errorCode === 202002 // current token is invalid || errorCode === 202003 // current token is expired || errorCode === 202004 // current token type is invalid || errorCode === 202005 // current token requires two-factor authorization || errorCode === 202006 // current token does not require two-factor authorization || errorCode === 202012 // token is empty ) { clearCurrentTokenAndUserInfo(false); location.reload(); return Promise.reject({ processed: true }); } } return Promise.reject(error); }); export default { setLocale: (locale: string) => { axios.defaults.headers.common['Accept-Language'] = locale; }, authorize: (data: UserLoginRequest): ApiResponsePromise => { return axios.post>('authorize.json', data); }, authorize2FA: ({ passcode, token }: { passcode: string, token: string }): ApiResponsePromise => { return axios.post>('2fa/authorize.json', { passcode: passcode }, { noAuth: true, headers: { Authorization: `Bearer ${token}` } } as ApiRequestConfig); }, authorize2FAByBackupCode: ({ recoveryCode, token }: { recoveryCode: string, token: string }): ApiResponsePromise => { return axios.post>('2fa/recovery.json', { recoveryCode: recoveryCode }, { noAuth: true, headers: { Authorization: `Bearer ${token}` } } as ApiRequestConfig); }, authorizeOAuth2: ({ password, passcode, callbackToken }: { password?: string, passcode?: string, callbackToken: string }): ApiResponsePromise => { const req: OAuth2CallbackLoginRequest = { password, passcode, token: getCurrentToken() || undefined }; return axios.post>('oauth2/authorize.json', req, { noAuth: true, headers: { Authorization: `Bearer ${callbackToken}` } } as ApiRequestConfig); }, register: (req: UserRegisterRequest): ApiResponsePromise => { return axios.post>('register.json', req); }, verifyEmail: ({ token, requestNewToken }: { token: string, requestNewToken: boolean }): ApiResponsePromise => { return axios.post>('verify_email/by_token.json?token=' + token, { requestNewToken: requestNewToken }, { noAuth: true, ignoreError: true } as ApiRequestConfig); }, resendVerifyEmailByUnloginUser: (req: UserResendVerifyEmailRequest): ApiResponsePromise => { return axios.post>('verify_email/resend.json', req); }, requestResetPassword: (req: ForgetPasswordRequest): ApiResponsePromise => { return axios.post>('forget_password/request.json', req); }, resetPassword: ({ email, token, password }: { email: string, token: string, password: string }): ApiResponsePromise => { return axios.post>('forget_password/reset/by_token.json?token=' + token, { email: email, password: password }, { noAuth: true, ignoreError: true } as ApiRequestConfig); }, logout: (): ApiResponsePromise => { return axios.get>('logout.json'); }, refreshToken: (): ApiResponsePromise => { return new Promise((resolve) => { needBlockRequest = true; axios.post>('v1/tokens/refresh.json', {}, { ignoreBlocked: true } as ApiRequestConfig).then(response => { const data = response.data; resolve(response); needBlockRequest = false; return data.result.newToken; }).then(newToken => { blockedRequests.forEach(func => func(newToken)); blockedRequests.length = 0; }); }); }, getExternalAuths: (): ApiResponsePromise => { return axios.get>('v1/users/external_auth/list.json'); }, unlinkExternalAuth: (req: UserExternalAuthUnlinkRequest): ApiResponsePromise => { return axios.post>('v1/users/external_auth/unlink.json', req); }, getTokens: (): ApiResponsePromise => { return axios.get>('v1/tokens/list.json'); }, generateAPIToken: (req: TokenGenerateAPIRequest): ApiResponsePromise => { return axios.post>('v1/tokens/generate/api.json', req); }, generateMCPToken: (req: TokenGenerateMCPRequest): ApiResponsePromise => { return axios.post>('v1/tokens/generate/mcp.json', req); }, revokeToken: ({ tokenId, ignoreError }: { tokenId: string, ignoreError?: boolean }): ApiResponsePromise => { const req: TokenRevokeRequest = { tokenId: tokenId }; return axios.post>('v1/tokens/revoke.json', req, { ignoreError: !!ignoreError } as ApiRequestConfig); }, revokeAllTokens: (): ApiResponsePromise => { return axios.post>('v1/tokens/revoke_all.json'); }, getProfile: (): ApiResponsePromise => { return axios.get>('v1/users/profile/get.json'); }, updateProfile: (req: UserProfileUpdateRequest): ApiResponsePromise => { return axios.post>('v1/users/profile/update.json', req); }, updateAvatar: ({ avatarFile }: { avatarFile: File }): ApiResponsePromise => { return axios.postForm>('v1/users/avatar/update.json', { avatar: avatarFile }, { timeout: DEFAULT_UPLOAD_API_TIMEOUT }); }, removeAvatar: (): ApiResponsePromise => { return axios.post>('v1/users/avatar/remove.json'); }, resendVerifyEmailByLoginedUser: (): ApiResponsePromise => { return axios.post>('v1/users/verify_email/resend.json'); }, getUserApplicationCloudSettings: (): ApiResponsePromise => { return axios.get>('v1/users/settings/cloud/get.json'); }, updateUserApplicationCloudSettings: (req: UserApplicationCloudSettingsUpdateRequest): ApiResponsePromise => { return axios.post>('v1/users/settings/cloud/update.json', req); }, disableUserApplicationCloudSettings: (): ApiResponsePromise => { return axios.post>('v1/users/settings/cloud/disable.json'); }, get2FAStatus: (): ApiResponsePromise => { return axios.get>('v1/users/2fa/status.json'); }, enable2FA: (): ApiResponsePromise => { return axios.post>('v1/users/2fa/enable/request.json'); }, confirmEnable2FA: (req: TwoFactorEnableConfirmRequest): ApiResponsePromise => { return axios.post>('v1/users/2fa/enable/confirm.json', req); }, disable2FA: (req: TwoFactorDisableRequest): ApiResponsePromise => { return axios.post>('v1/users/2fa/disable.json', req); }, regenerate2FARecoveryCode: (req: TwoFactorRegenerateRecoveryCodeRequest): ApiResponsePromise => { return axios.post>('v1/users/2fa/recovery/regenerate.json', req); }, getUserDataStatistics: (): ApiResponsePromise => { return axios.get>('v1/data/statistics.json'); }, getExportedUserData: (fileType: string, req?: ExportTransactionDataRequest): Promise> => { let params = ''; if (req) { const tagFilter = encodeURIComponent(req.tagFilter); const amountFilter = encodeURIComponent(req.amountFilter); const keyword = encodeURIComponent(req.keyword); params = `max_time=${req.maxTime}&min_time=${req.minTime}&type=${req.type}&category_ids=${req.categoryIds}&account_ids=${req.accountIds}&tag_filter=${tagFilter}&amount_filter=${amountFilter}&keyword=${keyword}`; } else { params = 'max_time=0&min_time=0&type=0&category_ids=&account_ids=&tag_filter=&amount_filter=&keyword='; } if (fileType === 'csv') { return axios.get('v1/data/export.csv?' + params, { timeout: DEFAULT_EXPORT_API_TIMEOUT } as ApiRequestConfig); } else if (fileType === 'tsv') { return axios.get('v1/data/export.tsv?' + params, { timeout: DEFAULT_EXPORT_API_TIMEOUT } as ApiRequestConfig); } else { return Promise.reject('Parameter Invalid'); } }, clearAllData: (req: ClearDataRequest): ApiResponsePromise => { return axios.post>('v1/data/clear/all.json', req, { timeout: DEFAULT_CLEAR_ALL_TRANSACTIONS_API_TIMEOUT } as ApiRequestConfig); }, clearAllTransactions: (req: ClearDataRequest): ApiResponsePromise => { return axios.post>('v1/data/clear/transactions.json', req, { timeout: DEFAULT_CLEAR_ALL_TRANSACTIONS_API_TIMEOUT } as ApiRequestConfig); }, clearAllTransactionsOfAccount: (req: ClearAccountTransactionsRequest): ApiResponsePromise => { return axios.post>('v1/data/clear/transactions/by_account.json', req, { timeout: DEFAULT_CLEAR_ALL_TRANSACTIONS_API_TIMEOUT } as ApiRequestConfig); }, getAllAccounts: ({ visibleOnly }: { visibleOnly: boolean }): ApiResponsePromise => { return axios.get>('v1/accounts/list.json?visible_only=' + visibleOnly); }, getAccount: ({ id }: { id: string }): ApiResponsePromise => { return axios.get>('v1/accounts/get.json?id=' + id); }, addAccount: (req: AccountCreateRequest): ApiResponsePromise => { return axios.post>('v1/accounts/add.json', req); }, modifyAccount: (req: AccountModifyRequest): ApiResponsePromise => { return axios.post>('v1/accounts/modify.json', req); }, hideAccount: (req: AccountHideRequest): ApiResponsePromise => { return axios.post>('v1/accounts/hide.json', req); }, moveAccount: (req: AccountMoveRequest): ApiResponsePromise => { return axios.post>('v1/accounts/move.json', req); }, deleteAccount: (req: AccountDeleteRequest): ApiResponsePromise => { return axios.post>('v1/accounts/delete.json', req); }, deleteSubAccount: (req: AccountDeleteRequest): ApiResponsePromise => { return axios.post>('v1/accounts/sub_account/delete.json', req); }, getTransactions: (req: TransactionListByMaxTimeRequest): ApiResponsePromise => { const tagFilter = encodeURIComponent(req.tagFilter); const amountFilter = encodeURIComponent(req.amountFilter); const keyword = encodeURIComponent(req.keyword); return axios.get>(`v1/transactions/list.json?max_time=${req.maxTime}&min_time=${req.minTime}&type=${req.type}&category_ids=${req.categoryIds}&account_ids=${req.accountIds}&tag_filter=${tagFilter}&amount_filter=${amountFilter}&keyword=${keyword}&count=${req.count}&page=${req.page}&with_count=${req.withCount}&trim_account=true&trim_category=true&trim_tag=true`); }, getAllTransactionsByMonth: (req: TransactionListInMonthByPageRequest): ApiResponsePromise => { const tagFilter = encodeURIComponent(req.tagFilter); const amountFilter = encodeURIComponent(req.amountFilter); const keyword = encodeURIComponent(req.keyword); return axios.get>(`v1/transactions/list/by_month.json?year=${req.year}&month=${req.month}&type=${req.type}&category_ids=${req.categoryIds}&account_ids=${req.accountIds}&tag_filter=${tagFilter}&amount_filter=${amountFilter}&keyword=${keyword}&trim_account=true&trim_category=true&trim_tag=true`); }, getAllTransactions: (req: TransactionAllListRequest): ApiResponsePromise => { return axios.get>(`v1/transactions/list/all.json?trim_account=true&with_pictures=${!!req.withPictures}&trim_category=true&trim_tag=true&start_time=${req.startTime}&end_time=${req.endTime}`); }, getReconciliationStatements: (req: TransactionReconciliationStatementRequest): ApiResponsePromise => { return axios.get>(`v1/transactions/reconciliation_statements.json?account_id=${req.accountId}&start_time=${req.startTime}&end_time=${req.endTime}`); }, getTransactionStatistics: (req: TransactionStatisticRequest): ApiResponsePromise => { const queryParams: string[] = []; if (req.startTime) { queryParams.push(`start_time=${req.startTime}`); } if (req.endTime) { queryParams.push(`end_time=${req.endTime}`); } if (req.tagFilter) { queryParams.push(`tag_filter=${encodeURIComponent(req.tagFilter)}`); } if (req.keyword) { queryParams.push(`keyword=${encodeURIComponent(req.keyword)}`); } return axios.get>(`v1/transactions/statistics.json?use_transaction_timezone=${req.useTransactionTimezone}` + (queryParams.length ? '&' + queryParams.join('&') : '')); }, getTransactionStatisticsTrends: (req: TransactionStatisticTrendsRequest): ApiResponsePromise => { const queryParams: string[] = []; if (req.startYearMonth) { queryParams.push(`start_year_month=${req.startYearMonth}`); } if (req.endYearMonth) { queryParams.push(`end_year_month=${req.endYearMonth}`); } if (req.tagFilter) { queryParams.push(`tag_filter=${encodeURIComponent(req.tagFilter)}`); } if (req.keyword) { queryParams.push(`keyword=${encodeURIComponent(req.keyword)}`); } return axios.get>(`v1/transactions/statistics/trends.json?use_transaction_timezone=${req.useTransactionTimezone}` + (queryParams.length ? '&' + queryParams.join('&') : '')); }, getTransactionStatisticsAssetTrends: (req: TransactionStatisticAssetTrendsRequest): ApiResponsePromise => { const queryParams: string[] = []; if (req.startTime) { queryParams.push(`start_time=${req.startTime}`); } if (req.endTime) { queryParams.push(`end_time=${req.endTime}`); } return axios.get>('v1/transactions/statistics/asset_trends.json' + (queryParams.length ? '?' + queryParams.join('&') : '')); }, getTransactionAmounts: (params: TransactionAmountsRequestParams, excludeAccountIds: string[], excludeCategoryIds: string[]): ApiResponsePromise => { const req = TransactionAmountsRequest.of(params); let queryParams = req.buildQuery(); if (excludeAccountIds && excludeAccountIds.length) { queryParams = queryParams + `&exclude_account_ids=${excludeAccountIds.join(',')}`; } if (excludeCategoryIds && excludeCategoryIds.length) { queryParams = queryParams + `&exclude_category_ids=${excludeCategoryIds.join(',')}`; } return axios.get>(`v1/transactions/amounts.json?${queryParams}`); }, getTransaction: ({ id, withPictures }: { id: string, withPictures: boolean | undefined }): ApiResponsePromise => { if (!isDefined(withPictures)) { withPictures = true; } return axios.get>(`v1/transactions/get.json?id=${id}&with_pictures=${withPictures}&trim_account=true&trim_category=true&trim_tag=true`); }, addTransaction: (req: TransactionCreateRequest): ApiResponsePromise => { return axios.post>('v1/transactions/add.json', req); }, modifyTransaction: (req: TransactionModifyRequest): ApiResponsePromise => { return axios.post>('v1/transactions/modify.json', req); }, moveAllTransactionsBetweenAccounts: (req: TransactionMoveBetweenAccountsRequest): ApiResponsePromise => { return axios.post>('v1/transactions/move/all.json', req); }, deleteTransaction: (req: TransactionDeleteRequest): ApiResponsePromise => { return axios.post>('v1/transactions/delete.json', req); }, parseImportCustomFile: ({ fileType, fileEncoding, importFile }: { fileType: string, fileEncoding?: string, importFile: File }): ApiResponsePromise => { return axios.postForm>('v1/transactions/parse_custom_file.json', { fileType: fileType, fileEncoding: fileEncoding, file: importFile }, { timeout: DEFAULT_UPLOAD_API_TIMEOUT } as ApiRequestConfig); }, parseImportTransaction: ({ fileType, additionalOptions, fileEncoding, importFile, columnMapping, transactionTypeMapping, hasHeaderLine, timeFormat, timezoneFormat, amountDecimalSeparator, amountDigitGroupingSymbol, geoSeparator, geoOrder, tagSeparator }: { fileType: string, additionalOptions?: ImportFileTypeSupportedAdditionalOptions, fileEncoding?: string, importFile: File, columnMapping?: Record, transactionTypeMapping?: Record, hasHeaderLine?: boolean, timeFormat?: string, timezoneFormat?: string, amountDecimalSeparator?: string, amountDigitGroupingSymbol?: string, geoSeparator?: string, geoOrder?: string, tagSeparator?: string }): ApiResponsePromise => { let textualAdditionalOptions: string | undefined = undefined; let textualColumnMapping: string | undefined = undefined; let textualTransactionTypeMapping: string | undefined = undefined; let textualHasHeaderLine: string | undefined = undefined; if (additionalOptions) { textualAdditionalOptions = objectFieldWithValueToArrayItem(additionalOptions, true).join(','); } if (columnMapping) { textualColumnMapping = JSON.stringify(columnMapping); } if (transactionTypeMapping) { textualTransactionTypeMapping = JSON.stringify(transactionTypeMapping); } if (hasHeaderLine) { textualHasHeaderLine = 'true'; } return axios.postForm>('v1/transactions/parse_import.json', { fileType: fileType, options: textualAdditionalOptions, fileEncoding: fileEncoding, file: importFile, columnMapping: textualColumnMapping, transactionTypeMapping: textualTransactionTypeMapping, hasHeaderLine: textualHasHeaderLine, timeFormat: timeFormat, timezoneFormat: timezoneFormat, amountDecimalSeparator: amountDecimalSeparator, amountDigitGroupingSymbol: amountDigitGroupingSymbol, geoSeparator: geoSeparator, geoOrder: geoOrder, tagSeparator: tagSeparator }, { timeout: DEFAULT_UPLOAD_API_TIMEOUT } as ApiRequestConfig); }, importTransactions: (req: TransactionImportRequest): ApiResponsePromise => { return axios.post>('v1/transactions/import.json', req, { timeout: DEFAULT_IMPORT_API_TIMEOUT } as ApiRequestConfig); }, getImportTransactionsProcess: (clientSessionId: string): ApiResponsePromise => { return axios.get>('v1/transactions/import/process.json?client_session_id=' + clientSessionId, { ignoreError: true } as ApiRequestConfig); }, uploadTransactionPicture: ({ pictureFile, clientSessionId }: { pictureFile: File, clientSessionId?: string }): ApiResponsePromise => { return axios.postForm>('v1/transaction/pictures/upload.json', { picture: pictureFile, clientSessionId: clientSessionId }, { timeout: DEFAULT_UPLOAD_API_TIMEOUT } as ApiRequestConfig); }, removeUnusedTransactionPicture: (req: TransactionPictureUnusedDeleteRequest): ApiResponsePromise => { return axios.post>('v1/transaction/pictures/remove_unused.json', req); }, getAllTransactionCategories: (): ApiResponsePromise> => { return axios.get>>('v1/transaction/categories/list.json'); }, getTransactionCategory: ({ id }: { id: string }): ApiResponsePromise => { return axios.get>('v1/transaction/categories/get.json?id=' + id); }, addTransactionCategory: (req: TransactionCategoryCreateRequest): ApiResponsePromise => { return axios.post>('v1/transaction/categories/add.json', req); }, addTransactionCategoryBatch: (req: TransactionCategoryCreateBatchRequest): ApiResponsePromise> => { return axios.post>>('v1/transaction/categories/add_batch.json', req); }, modifyTransactionCategory: (req: TransactionCategoryModifyRequest): ApiResponsePromise => { return axios.post>('v1/transaction/categories/modify.json', req); }, hideTransactionCategory: (req: TransactionCategoryHideRequest): ApiResponsePromise => { return axios.post>('v1/transaction/categories/hide.json', req); }, moveTransactionCategory: (req: TransactionCategoryMoveRequest): ApiResponsePromise => { return axios.post>('v1/transaction/categories/move.json', req); }, deleteTransactionCategory: (req: TransactionCategoryDeleteRequest): ApiResponsePromise => { return axios.post>('v1/transaction/categories/delete.json', req); }, getAllTransactionTagGroups: (): ApiResponsePromise => { return axios.get>('v1/transaction/tags/groups/list.json'); }, getTransactionTagGroup: ({ id }: { id: string }): ApiResponsePromise => { return axios.get>('v1/transaction/tags/groups/get.json?id=' + id); }, addTransactionTagGroup: (req: TransactionTagGroupCreateRequest): ApiResponsePromise => { return axios.post>('v1/transaction/tags/groups/add.json', req); }, modifyTransactionTagGroup: (req: TransactionTagGroupModifyRequest): ApiResponsePromise => { return axios.post>('v1/transaction/tags/groups/modify.json', req); }, moveTransactionTagGroup: (req: TransactionTagGroupMoveRequest): ApiResponsePromise => { return axios.post>('v1/transaction/tags/groups/move.json', req); }, deleteTransactionTagGroup: (req: TransactionTagGroupDeleteRequest): ApiResponsePromise => { return axios.post>('v1/transaction/tags/groups/delete.json', req); }, getAllTransactionTags: (): ApiResponsePromise => { return axios.get>('v1/transaction/tags/list.json'); }, getTransactionTag: ({ id }: { id: string }): ApiResponsePromise => { return axios.get>('v1/transaction/tags/get.json?id=' + id); }, addTransactionTag: (req: TransactionTagCreateRequest): ApiResponsePromise => { return axios.post>('v1/transaction/tags/add.json', req); }, addTransactionTagBatch: (req: TransactionTagCreateBatchRequest): ApiResponsePromise => { return axios.post>('v1/transaction/tags/add_batch.json', req); }, modifyTransactionTag: (req: TransactionTagModifyRequest): ApiResponsePromise => { return axios.post>('v1/transaction/tags/modify.json', req); }, hideTransactionTag: (req: TransactionTagHideRequest): ApiResponsePromise => { return axios.post>('v1/transaction/tags/hide.json', req); }, moveTransactionTag: (req: TransactionTagMoveRequest): ApiResponsePromise => { return axios.post>('v1/transaction/tags/move.json', req); }, deleteTransactionTag: (req: TransactionTagDeleteRequest): ApiResponsePromise => { return axios.post>('v1/transaction/tags/delete.json', req); }, getAllTransactionTemplates: ({ templateType }: { templateType: number }): ApiResponsePromise => { return axios.get>('v1/transaction/templates/list.json?templateType=' + templateType); }, getTransactionTemplate: ({ id }: { id: string }): ApiResponsePromise => { return axios.get>('v1/transaction/templates/get.json?id=' + id); }, addTransactionTemplate: (req: TransactionTemplateCreateRequest): ApiResponsePromise => { return axios.post>('v1/transaction/templates/add.json', req); }, modifyTransactionTemplate: (req: TransactionTemplateModifyRequest): ApiResponsePromise => { return axios.post>('v1/transaction/templates/modify.json', req); }, hideTransactionTemplate: (req: TransactionTemplateHideRequest): ApiResponsePromise => { return axios.post>('v1/transaction/templates/hide.json', req); }, moveTransactionTemplate: (req: TransactionTemplateMoveRequest): ApiResponsePromise => { return axios.post>('v1/transaction/templates/move.json', req); }, deleteTransactionTemplate: (req: TransactionTemplateDeleteRequest): ApiResponsePromise => { return axios.post>('v1/transaction/templates/delete.json', req); }, getAllInsightsExplorers: (): ApiResponsePromise => { return axios.get>('v1/insights/explorers/list.json'); }, getInsightsExplorer: ({ id }: { id: string }): ApiResponsePromise => { return axios.get>('v1/insights/explorers/get.json?id=' + id); }, addInsightsExplorer: (req: InsightsExplorerCreateRequest): ApiResponsePromise => { return axios.post>('v1/insights/explorers/add.json', req); }, modifyInsightsExplorer: (req: InsightsExplorerModifyRequest): ApiResponsePromise => { return axios.post>('v1/insights/explorers/modify.json', req); }, hideInsightsExplorer: (req: InsightsExplorerHideRequest): ApiResponsePromise => { return axios.post>('v1/insights/explorers/hide.json', req); }, moveInsightsExplorer: (req: InsightsExplorerMoveRequest): ApiResponsePromise => { return axios.post>('v1/insights/explorers/move.json', req); }, deleteInsightsExplorer: (req: InsightsExplorerDeleteRequest): ApiResponsePromise => { return axios.post>('v1/insights/explorers/delete.json', req); }, recognizeReceiptImage: ({ imageFile, cancelableUuid }: { imageFile: File, cancelableUuid?: string }): ApiResponsePromise => { return axios.postForm>('v1/llm/transactions/recognize_receipt_image.json', { image: imageFile }, { timeout: DEFAULT_LLM_API_TIMEOUT, cancelableUuid: cancelableUuid } as ApiRequestConfig); }, getLatestExchangeRates: (param: { ignoreError?: boolean }): ApiResponsePromise => { return axios.get>('v1/exchange_rates/latest.json', { ignoreError: !!param.ignoreError, timeout: getExchangeRatesRequestTimeout() || DEFAULT_API_TIMEOUT } as ApiRequestConfig); }, updateUserCustomExchangeRate: (req: UserCustomExchangeRateUpdateRequest): ApiResponsePromise => { return axios.post>('v1/exchange_rates/user_custom/update.json', req); }, deleteUserCustomExchangeRate: (req: UserCustomExchangeRateDeleteRequest): ApiResponsePromise => { return axios.post>('v1/exchange_rates/user_custom/delete.json', req); }, getServerVersion: (): ApiResponsePromise => { return axios.get>('v1/systems/version.json'); }, cancelRequest: (cancelableUuid: string) => { cancelableRequests[cancelableUuid] = true; }, generateOAuth2LoginUrl: (platform: 'mobile' | 'desktop', clientSessionId: string): string => { return `${getBasePath()}/oauth2/login?platform=${platform}&client_session_id=${clientSessionId}`; }, generateOAuth2LinkUrl: (platform: 'mobile' | 'desktop', clientSessionId: string): string => { return `${getBasePath()}/oauth2/login?platform=${platform}&client_session_id=${clientSessionId}&token=${getCurrentToken()}`; }, generateQrCodeUrl: (qrCodeName: string): string => { return `${getBasePath()}${BASE_QRCODE_PATH}/${qrCodeName}.png`; }, getMapProxyTileImageAndAnnotationImageUrlPatterns(): string[] { return [ `.*${BASE_PROXY_URL_PATH}/map/tile/[^/]+/[^/]+/[^/]+\\.png\\?provider=[^&]+.*$`, `.*${BASE_PROXY_URL_PATH}/map/annotation/[^/]+/[^/]+/[^/]+\\.png\\?provider=[^&]+.*$` ]; }, generateMapProxyTileImageUrl: (mapProvider: string, language: string): string => { const token = getCurrentToken(); let url = `${getBasePath()}${BASE_PROXY_URL_PATH}/map/tile/{z}/{x}/{y}.png?provider=${mapProvider}&token=${token}`; if (language) { url = url + `&language=${language}`; } return url; }, generateMapProxyAnnotationImageUrl: (mapProvider: string, language: string): string => { const token = getCurrentToken(); let url = `${getBasePath()}${BASE_PROXY_URL_PATH}/map/annotation/{z}/{x}/{y}.png?provider=${mapProvider}&token=${token}`; if (language) { url = url + `&language=${language}`; } return url; }, generateGoogleMapJavascriptUrl: (language: string | undefined, callbackFnName: string): string => { let url = `${GOOGLE_MAP_JAVASCRIPT_URL}?key=${getGoogleMapAPIKey()}&libraries=core,marker&callback=${callbackFnName}`; if (language) { url = url + `&language=${language}`; } return url; }, generateBaiduMapJavascriptUrl: (callbackFnName: string): string => { return `${BAIDU_MAP_JAVASCRIPT_URL}&ak=${getBaiduMapAK()}&callback=${callbackFnName}`; }, generateAmapJavascriptUrl: (callbackFnName: string): string => { return `${AMAP_JAVASCRIPT_URL}&key=${getAmapApplicationKey()}&plugin=AMap.ToolBar&callback=${callbackFnName}`; }, generateAmapApiInternalProxyUrl: (): string => { return `${window.location.origin}${getBasePath()}${BASE_AMAP_API_PROXY_URL_PATH}`; }, getInternalAvatarUrlWithToken(avatarUrl: string, disableBrowserCache?: boolean | string): string { if (!avatarUrl) { return avatarUrl; } const params: string[] = []; params.push('token=' + getCurrentToken()); if (disableBrowserCache) { if (isBoolean(disableBrowserCache)) { params.push('_nocache=' + generateRandomUUID()); } else { params.push('_nocache=' + disableBrowserCache); } } if (avatarUrl.indexOf('?') >= 0) { return avatarUrl + '&' + params.join('&'); } else { return avatarUrl + '?' + params.join('&'); } }, getTransactionPictureUrlWithToken(pictureUrl: string, disableBrowserCache?: boolean | string): string { if (!pictureUrl) { return pictureUrl; } const params: string[] = []; params.push('token=' + getCurrentToken()); if (disableBrowserCache) { if (isBoolean(disableBrowserCache)) { params.push('_nocache=' + generateRandomUUID()); } else { params.push('_nocache=' + disableBrowserCache); } } if (pictureUrl.indexOf('?') >= 0) { return pictureUrl + '&' + params.join('&'); } else { return pictureUrl + '?' + params.join('&'); } } };