aboutsummaryrefslogtreecommitdiff
path: root/packages/merchant-backoffice-ui/src/hooks/backend.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/merchant-backoffice-ui/src/hooks/backend.ts')
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/backend.ts139
1 files changed, 110 insertions, 29 deletions
diff --git a/packages/merchant-backoffice-ui/src/hooks/backend.ts b/packages/merchant-backoffice-ui/src/hooks/backend.ts
index ecd34df6d..fe4155788 100644
--- a/packages/merchant-backoffice-ui/src/hooks/backend.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/backend.ts
@@ -19,19 +19,21 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { useSWRConfig } from "swr";
-import { MerchantBackend } from "../declaration.js";
-import { useBackendContext } from "../context/backend.js";
-import { useCallback, useEffect, useState } from "preact/hooks";
-import { useInstanceContext } from "../context/instance.js";
+import { AbsoluteTime, HttpStatusCode } from "@gnu-taler/taler-util";
import {
ErrorType,
+ HttpError,
HttpResponse,
HttpResponseOk,
RequestError,
RequestOptions,
+ useApiContext,
} from "@gnu-taler/web-util/browser";
-import { useApiContext } from "@gnu-taler/web-util/browser";
+import { useCallback, useEffect, useState } from "preact/hooks";
+import { useSWRConfig } from "swr";
+import { useBackendContext } from "../context/backend.js";
+import { useInstanceContext } from "../context/instance.js";
+import { AccessToken, LoginToken, MerchantBackend, Timestamp } from "../declaration.js";
export function useMatchMutate(): (
@@ -85,6 +87,9 @@ export function useBackendInstancesTestForAdmin(): HttpResponse<
return result;
}
+const CHECK_CONFIG_INTERVAL_OK = 5 * 60 * 1000;
+const CHECK_CONFIG_INTERVAL_FAIL = 2 * 1000;
+
export function useBackendConfig(): HttpResponse<
MerchantBackend.VersionResponse,
RequestError<MerchantBackend.ErrorDetail>
@@ -92,18 +97,33 @@ export function useBackendConfig(): HttpResponse<
const { request } = useBackendBaseRequest();
type Type = MerchantBackend.VersionResponse;
-
- const [result, setResult] = useState<
- HttpResponse<Type, RequestError<MerchantBackend.ErrorDetail>>
- >({ loading: true });
+ type State = { data: HttpResponse<Type, RequestError<MerchantBackend.ErrorDetail>>, timer: number }
+ const [result, setResult] = useState<State>({ data: { loading: true }, timer: 0 });
useEffect(() => {
- request<Type>(`/config`)
- .then((data) => setResult(data))
- .catch((error) => setResult(error));
+ if (result.timer) {
+ clearTimeout(result.timer)
+ }
+ function tryConfig(): void {
+ request<Type>(`/config`)
+ .then((data) => {
+ const timer: any = setTimeout(() => {
+ tryConfig()
+ }, CHECK_CONFIG_INTERVAL_OK)
+ setResult({ data, timer })
+ })
+ .catch((error) => {
+ const timer: any = setTimeout(() => {
+ tryConfig()
+ }, CHECK_CONFIG_INTERVAL_FAIL)
+ const data = error.cause
+ setResult({ data, timer })
+ });
+ }
+ tryConfig()
}, [request]);
- return result;
+ return result.data;
}
interface useBackendInstanceRequestType {
@@ -149,32 +169,86 @@ interface useBackendBaseRequestType {
}
type YesOrNo = "yes" | "no";
+type LoginResult = {
+ valid: true;
+ token: string;
+ expiration: Timestamp;
+} | {
+ valid: false;
+ cause: HttpError<{}>;
+}
export function useCredentialsChecker() {
const { request } = useApiContext();
//check against instance details endpoint
//while merchant backend doesn't have a login endpoint
- async function testLogin(
- instance: string,
- token: string,
- ): Promise<{
- valid: boolean;
- cause?: ErrorType;
- }> {
+ async function requestNewLoginToken(
+ baseUrl: string,
+ token: AccessToken,
+ ): Promise<LoginResult> {
+ const data: MerchantBackend.Instances.LoginTokenRequest = {
+ scope: "write",
+ duration: {
+ d_us: "forever"
+ },
+ refreshable: true,
+ }
try {
- const response = await request(instance, `/private/`, {
+ const response = await request<MerchantBackend.Instances.LoginTokenSuccessResponse>(baseUrl, `/private/token`, {
+ method: "POST",
token,
+ data
});
- return { valid: true };
+ return { valid: true, token: response.data.token, expiration: response.data.expiration };
} catch (error) {
if (error instanceof RequestError) {
- return { valid: false, cause: error.cause.type };
+ return { valid: false, cause: error.cause };
}
- return { valid: false, cause: ErrorType.UNEXPECTED };
+ return {
+ valid: false, cause: {
+ type: ErrorType.UNEXPECTED,
+ loading: false,
+ info: {
+ hasToken: true,
+ status: 0,
+ options: {},
+ url: `/private/token`,
+ payload: {}
+ },
+ exception: error,
+ message: (error instanceof Error ? error.message : "unpexepected error")
+ }
+ };
}
};
- return testLogin
+
+ async function refreshLoginToken(
+ baseUrl: string,
+ token: LoginToken
+ ): Promise<LoginResult> {
+
+ if (AbsoluteTime.isExpired(AbsoluteTime.fromProtocolTimestamp(token.expiration))) {
+ return {
+ valid: false, cause: {
+ type: ErrorType.CLIENT,
+ status: HttpStatusCode.Unauthorized,
+ message: "login token expired, login again.",
+ info: {
+ hasToken: true,
+ status: 401,
+ options: {},
+ url: `/private/token`,
+ payload: {}
+ },
+ payload: {}
+ },
+ }
+ }
+
+ return requestNewLoginToken(baseUrl, token.token as AccessToken)
+ }
+ return { requestNewLoginToken, refreshLoginToken }
}
/**
@@ -183,15 +257,20 @@ export function useCredentialsChecker() {
* @returns request handler to
*/
export function useBackendBaseRequest(): useBackendBaseRequestType {
- const { url: backend, token } = useBackendContext();
+ const { url: backend, token: loginToken } = useBackendContext();
const { request: requestHandler } = useApiContext();
+ const token = loginToken?.token;
const request = useCallback(
function requestImpl<T>(
endpoint: string,
options: RequestOptions = {},
): Promise<HttpResponseOk<T>> {
- return requestHandler<T>(backend, endpoint, { token, ...options });
+ return requestHandler<T>(backend, endpoint, { token, ...options }).then(res => {
+ return res
+ }).catch(err => {
+ throw err
+ });
},
[backend, token],
);
@@ -204,10 +283,12 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
const { token: instanceToken, id, admin } = useInstanceContext();
const { request: requestHandler } = useApiContext();
- const { baseUrl, token } = !admin
+ const { baseUrl, token: loginToken } = !admin
? { baseUrl: rootBackendUrl, token: rootToken }
: { baseUrl: `${rootBackendUrl}/instances/${id}`, token: instanceToken };
+ const token = loginToken?.token;
+
const request = useCallback(
function requestImpl<T>(
endpoint: string,