import axios, { AxiosRequestConfig } from "axios" import { v4 } from "uuid" import { pipe } from "fp-ts/function" import * as TE from "fp-ts/TaskEither" import { NetworkResponse, NetworkStrategy } from "../network" import { decodeB64StringToArrayBuffer } from "../utils/b64" import { settingsStore } from "~/newstore/settings" let cancelSource = axios.CancelToken.source() type ProxyHeaders = { "multipart-part-key"?: string } type ProxyPayloadType = FormData | (AxiosRequestConfig & { wantsBinary: true }) export const cancelRunningAxiosRequest = () => { cancelSource.cancel() // Create a new cancel token cancelSource = axios.CancelToken.source() } const getProxyPayload = ( req: AxiosRequestConfig, multipartKey: string | null ) => { let payload: ProxyPayloadType = { ...req, wantsBinary: true, } if (payload.data instanceof FormData) { const formData = payload.data payload.data = "" formData.append(multipartKey!, JSON.stringify(payload)) payload = formData } return payload } const axiosWithProxy: NetworkStrategy = (req) => pipe( TE.Do, // If the request has FormData, the proxy needs a key TE.bind("multipartKey", () => TE.of(req.data instanceof FormData ? v4() : null) ), // Build headers to send TE.bind("headers", ({ multipartKey }) => TE.of( req.data instanceof FormData ? <ProxyHeaders>{ "multipart-part-key": `proxyRequestData-${multipartKey}`, } : <ProxyHeaders>{} ) ), // Create payload TE.bind("payload", ({ multipartKey }) => TE.of(getProxyPayload(req, multipartKey)) ), // Run the proxy request TE.chain(({ payload, headers }) => TE.tryCatch( () => axios.post( settingsStore.value.PROXY_URL || "https://proxy.hoppscotch.io", payload, { headers, cancelToken: cancelSource.token, } ), (reason) => axios.isCancel(reason) ? "cancellation" // Convert cancellation errors into cancellation strings : reason ) ), // Check success predicate TE.chain( TE.fromPredicate( ({ data }) => data.success, ({ data }) => data.data.message || "Proxy Error" ) ), // Process Base64 TE.chain(({ data }) => { if (data.isBinary) { data.data = decodeB64StringToArrayBuffer(data.data) } return TE.of(data) }) ) const axiosWithoutProxy: NetworkStrategy = (req) => pipe( TE.tryCatch( () => axios({ ...req, cancelToken: (cancelSource && cancelSource.token) || "", responseType: "arraybuffer", }), (e) => (axios.isCancel(e) ? "cancellation" : (e as any)) ), TE.orElse((e) => e !== "cancellation" && e.response ? TE.right(e.response as NetworkResponse) : TE.left(e) ) ) const axiosStrategy: NetworkStrategy = (req) => pipe( req, settingsStore.value.PROXY_ENABLED ? axiosWithProxy : axiosWithoutProxy ) export default axiosStrategy