aboutsummaryrefslogtreecommitdiffstats
path: root/code/app/src/utilities/_fetch.ts
blob: f884653656bbe660f92f69ae55063382d9ae23c3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import { Temporal } from "temporal-polyfill";
import { redirect } from "@sveltejs/kit";
import { browser } from "$app/environment";
import { goto } from "$app/navigation";
import { SignInPageMessage, signInPageMessageQueryKey } from "$routes/(main)/(public)/sign-in";
import { AccountService } from "$services/account-service";


export async function http_post_async(url: string, body?: object | string, timeout = -1, skip_401_check = false, abort_signal?: AbortSignal): Promise<Response> {
    const init = make_request_init("post", body, abort_signal);
    const response = await internal_fetch_async({ url, init, timeout });
    if (!skip_401_check && await redirect_if_401_async(response)) throw new Error("Server returned 401");
    return response;
}

export async function http_get_async(url: string, timeout = -1, skip_401_check = false, abort_signal?: AbortSignal): Promise<Response> {
    const init = make_request_init("get", undefined, abort_signal);
    const response = await internal_fetch_async({ url, init, timeout });
    if (!skip_401_check && await redirect_if_401_async(response)) throw new Error("Server returned 401");
    return response;
}

export async function http_delete_async(url: string, body?: object | string, timeout = -1, skip_401_check = false, abort_signal?: AbortSignal): Promise<Response> {
    const init = make_request_init("delete", body, abort_signal);
    const response = await internal_fetch_async({ url, init, timeout });
    if (!skip_401_check && await redirect_if_401_async(response)) throw new Error("Server returned 401");
    return response;
}

async function internal_fetch_async(request: InternalFetchRequest): Promise<Response> {
    if (!request.init) throw new Error("request.init is required");
    const fetch_request = new Request(request.url, request.init);
    let response: any;

    try {
        if (request.timeout && request.timeout > 500) {
            response = await Promise.race([
                fetch(fetch_request),
                new Promise((_, reject) => setTimeout(() => reject(new Error("Timeout")), request.timeout)),
            ]);
        } else {
            response = await fetch(fetch_request);
        }
    } catch (error: any) {
        if (error.message === "Timeout") {
            console.error("Request timed out", error);
        } else if (error.message === "Network request failed") {
            console.error("No internet connection", error);
        } else {
            throw error;
        }
    }

    return response;
}

async function redirect_if_401_async(response: Response): Promise<boolean> {
    if (response.status === 401) {
        const redirectUrl = `/sign-in?${signInPageMessageQueryKey}=${SignInPageMessage.LOGGED_OUT}`;
        await AccountService.resolve().end_session_async();
        if (browser) {
            await goto(redirectUrl);
        } else {
            throw redirect(307, redirectUrl);
        }
    }
    return false;
}

function make_request_init(method: string, body?: any, signal?: AbortSignal): RequestInit {
    const init = {
        method,
        credentials: "include",
        signal,
        headers: {
            "X-TimeZone": Temporal.Now.timeZone().id,
        },
    } as RequestInit;

    if (body) {
        init.body = JSON.stringify(body);
        init.headers["Content-Type"] = "application/json;charset=UTF-8";
    }

    return init;
}

export type InternalFetchRequest = {
    url: string,
    init: RequestInit,
    timeout?: number
    retry_count?: number,
}