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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
import { Temporal } from "temporal-polyfill";
import { clear_session_data } from "$lib/session";
import type { Result } from "rustic";
import { Err, Ok } from "rustic";
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 { log_error } from "$lib/logger";
export async function http_post_async<T>(url: string, body?: object | string, timeout = -1, skip_401_check = false, abort_signal?: AbortSignal): Promise<InternalFetchResponse<T>> {
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)) return Err("Server returned 401");
return make_response_async(response);
}
export async function http_get_async<T>(url: string, timeout = -1, skip_401_check = false, abort_signal?: AbortSignal): Promise<Result<InternalFetchResponse<T>, string>> {
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)) return Err("Server returned 401");
return make_response_async(response);
}
export async function http_delete_async<T>(url: string, body?: object | string, timeout = -1, skip_401_check = false, abort_signal?: AbortSignal): Promise<Result<InternalFetchResponse<T>, string>> {
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)) return Err("Server returned 401");
return make_response_async(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) {
log_error(error);
if (error.message === "Timeout") {
console.error("Request timed out");
} else if (error.message === "Network request failed") {
console.error("No internet connection");
} 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}`;
clear_session_data();
if (browser) {
await goto(redirectUrl)
} else {
throw redirect(307, redirectUrl);
}
}
return false;
}
async function make_response_async<T>(response: Response): Promise<Result<InternalFetchResponse<T>, string>> {
const result = {
ok: response.ok,
status: response.status,
http_response: response,
} as InternalFetchResponse<T>;
if (response.status !== 204) {
try {
result.data = await response.json() as T;
} catch (error) {
log_error("", { error, result })
return Err("Deserialisation threw");
}
}
return Ok(result);
}
function make_request_init(method: string, body?: any, signal?: AbortSignal): RequestInit {
const init = {
method,
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,
}
export type InternalFetchResponse<T> = {
ok: boolean,
status: number,
data: T | undefined,
http_response: Response
}
|