diff options
Diffstat (limited to 'packages/bank-ui/src/context/config.ts')
-rw-r--r-- | packages/bank-ui/src/context/config.ts | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/packages/bank-ui/src/context/config.ts b/packages/bank-ui/src/context/config.ts new file mode 100644 index 000000000..39d12be86 --- /dev/null +++ b/packages/bank-ui/src/context/config.ts @@ -0,0 +1,263 @@ +/* + This file is part of GNU Taler + (C) 2022-2024 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +import { + assertUnreachable, + LibtoolVersion, + TalerBankConversionCacheEviction, + TalerBankConversionHttpClient, + TalerCorebankApi, + TalerAuthenticationHttpClient, + TalerCoreBankCacheEviction, + TalerCoreBankHttpClient, + TalerError +} from "@gnu-taler/taler-util"; +import { + BrowserHttpLib, + ErrorLoading, + useTranslationContext, +} from "@gnu-taler/web-util/browser"; +import { + ComponentChildren, + createContext, + FunctionComponent, + h, + VNode, +} from "preact"; +import { useContext, useEffect, useState } from "preact/hooks"; +import { + revalidateAccountDetails, + revalidatePublicAccounts, + revalidateTransactions, +} from "../hooks/account.js"; +import { + revalidateBusinessAccounts, + revalidateCashouts, + revalidateConversionInfo, +} from "../hooks/regional.js"; + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +export type Type = { + url: URL; + config: TalerCorebankApi.Config; + bank: TalerCoreBankHttpClient; + conversion: TalerBankConversionHttpClient; + authenticator: (user: string) => TalerAuthenticationHttpClient; + hints: VersionHint[]; +}; + +// FIXME: below +// @ts-expect-error default value to undefined, should it be another thing? +const Context = createContext<Type>(undefined); + +export const useBankCoreApiContext = (): Type => useContext(Context); + +export enum VersionHint { + /** + * when this flag is on, server is running an old version with cashout before implementing 2fa API + */ + CASHOUT_BEFORE_2FA, +} + +export type ConfigResult = + | undefined + | { type: "ok"; config: TalerCorebankApi.Config; hints: VersionHint[] } + | { type: "incompatible"; result: TalerCorebankApi.Config; supported: string } + | { type: "error"; error: TalerError }; + +export const BankCoreApiProvider = ({ + baseUrl, + children, + frameOnError, +}: { + baseUrl: string; + children: ComponentChildren; + frameOnError: FunctionComponent<{ children: ComponentChildren }>; +}): VNode => { + const [checked, setChecked] = useState<ConfigResult>(); + const { i18n } = useTranslationContext(); + + const { bankClient, conversionClient, authClient } = buildApiClient(new URL(baseUrl)) + + useEffect(() => { + bankClient + .getConfig() + .then((resp) => { + if (bankClient.isCompatible(resp.body.version)) { + setChecked({ type: "ok", config: resp.body, hints: [] }); + } else { + // this API supports version 3.0.3 + const compare = LibtoolVersion.compare("3:0:3", resp.body.version); + if (compare?.compatible ?? false) { + setChecked({ + type: "ok", + config: resp.body, + hints: [VersionHint.CASHOUT_BEFORE_2FA], + }); + } else { + setChecked({ + type: "incompatible", + result: resp.body, + supported: bankClient.PROTOCOL_VERSION, + }); + } + } + }) + .catch((error: unknown) => { + if (error instanceof TalerError) { + setChecked({ type: "error", error }); + } + }); + }, []); + + if (checked === undefined) { + return h(frameOnError, { children: h("div", {}, "loading...") }); + } + if (checked.type === "error") { + return h(frameOnError, { + children: h(ErrorLoading, { error: checked.error, showDetail: true }), + }); + } + if (checked.type === "incompatible") { + return h(frameOnError, { + children: h( + "div", + {}, + i18n.str`The bank backend is not supported. Supported version "${checked.supported}", server version "${checked.result.version}"`, + ), + }); + } + const value: Type = { + url: new URL(bankClient.baseUrl), + config: checked.config, + bank: bankClient, + conversion: conversionClient, + authenticator: authClient, + hints: checked.hints, + }; + return h(Context.Provider, { + value, + children, + }); +}; + +/** + * build http client with cache breaker due to SWR + * @param url + * @returns + */ +function buildApiClient(url: URL) { + const httpLib = new BrowserHttpLib(); + + const bankClient = new TalerCoreBankHttpClient(url.href, httpLib, { + async notifySuccess(op) { + switch (op) { + case TalerCoreBankCacheEviction.DELELE_ACCOUNT: { + await Promise.all([ + revalidatePublicAccounts(), + revalidateBusinessAccounts(), + ]); + return + } + case TalerCoreBankCacheEviction.CREATE_ACCOUNT: { + // admin balance change on new account + await Promise.all([ + revalidateAccountDetails(), + revalidateTransactions(), + revalidatePublicAccounts(), + revalidateBusinessAccounts(), + ]) + return; + } + case TalerCoreBankCacheEviction.UPDATE_ACCOUNT: { + await Promise.all([ + revalidateAccountDetails(), + ]) + return; + } + case TalerCoreBankCacheEviction.CREATE_TRANSACTION: { + await Promise.all([ + revalidateAccountDetails(), + revalidateTransactions(), + ]) + return; + } + case TalerCoreBankCacheEviction.CONFIRM_WITHDRAWAL: { + await Promise.all([ + revalidateAccountDetails(), + revalidateTransactions(), + ]) + return; + } + case TalerCoreBankCacheEviction.CREATE_CASHOUT: { + await Promise.all([ + revalidateAccountDetails(), + revalidateCashouts(), + revalidateTransactions(), + ]) + return; + } + case TalerCoreBankCacheEviction.UPDATE_PASSWORD: + case TalerCoreBankCacheEviction.ABORT_WITHDRAWAL: + case TalerCoreBankCacheEviction.CREATE_WITHDRAWAL: + return; + default: + assertUnreachable(op) + } + } + }); + const conversionClient = new TalerBankConversionHttpClient(bankClient.getConversionInfoAPI(), httpLib, { + async notifySuccess(op) { + switch (op) { + case TalerBankConversionCacheEviction.UPDATE_RATE: { + await revalidateConversionInfo(); + return + } + default: + assertUnreachable(op) + } + } + }); + const authClient = (user: string) => new TalerAuthenticationHttpClient(bankClient.getAuthenticationAPI(user), user, httpLib); + return { bankClient, conversionClient, authClient } +} + +export const BankCoreApiProviderTesting = ({ + children, + state, + url, +}: { + children: ComponentChildren; + state: TalerCorebankApi.Config; + url: string; +}): VNode => { + const value: Type = { + url: new URL(url), + config: state, + // @ts-expect-error this API is not being used, not really needed + bank: undefined, + hints: [], + }; + + return h(Context.Provider, { + value, + children, + }); +}; |