AxiosStrategy.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import axios, { AxiosRequestConfig } from "axios"
  2. import { v4 } from "uuid"
  3. import { pipe } from "fp-ts/function"
  4. import * as TE from "fp-ts/TaskEither"
  5. import { NetworkResponse, NetworkStrategy } from "../network"
  6. import { decodeB64StringToArrayBuffer } from "../utils/b64"
  7. import { settingsStore } from "~/newstore/settings"
  8. let cancelSource = axios.CancelToken.source()
  9. type ProxyHeaders = {
  10. "multipart-part-key"?: string
  11. }
  12. type ProxyPayloadType = FormData | (AxiosRequestConfig & { wantsBinary: true })
  13. export const cancelRunningAxiosRequest = () => {
  14. cancelSource.cancel()
  15. // Create a new cancel token
  16. cancelSource = axios.CancelToken.source()
  17. }
  18. const getProxyPayload = (
  19. req: AxiosRequestConfig,
  20. multipartKey: string | null
  21. ) => {
  22. let payload: ProxyPayloadType = {
  23. ...req,
  24. wantsBinary: true,
  25. }
  26. if (payload.data instanceof FormData) {
  27. const formData = payload.data
  28. payload.data = ""
  29. formData.append(multipartKey!, JSON.stringify(payload))
  30. payload = formData
  31. }
  32. return payload
  33. }
  34. const axiosWithProxy: NetworkStrategy = (req) =>
  35. pipe(
  36. TE.Do,
  37. // If the request has FormData, the proxy needs a key
  38. TE.bind("multipartKey", () =>
  39. TE.of(req.data instanceof FormData ? v4() : null)
  40. ),
  41. // Build headers to send
  42. TE.bind("headers", ({ multipartKey }) =>
  43. TE.of(
  44. req.data instanceof FormData
  45. ? <ProxyHeaders>{
  46. "multipart-part-key": `proxyRequestData-${multipartKey}`,
  47. }
  48. : <ProxyHeaders>{}
  49. )
  50. ),
  51. // Create payload
  52. TE.bind("payload", ({ multipartKey }) =>
  53. TE.of(getProxyPayload(req, multipartKey))
  54. ),
  55. // Run the proxy request
  56. TE.chain(({ payload, headers }) =>
  57. TE.tryCatch(
  58. () =>
  59. axios.post(
  60. settingsStore.value.PROXY_URL || "https://proxy.hoppscotch.io",
  61. payload,
  62. {
  63. headers,
  64. cancelToken: cancelSource.token,
  65. }
  66. ),
  67. (reason) =>
  68. axios.isCancel(reason)
  69. ? "cancellation" // Convert cancellation errors into cancellation strings
  70. : reason
  71. )
  72. ),
  73. // Check success predicate
  74. TE.chain(
  75. TE.fromPredicate(
  76. ({ data }) => data.success,
  77. ({ data }) => data.data.message || "Proxy Error"
  78. )
  79. ),
  80. // Process Base64
  81. TE.chain(({ data }) => {
  82. if (data.isBinary) {
  83. data.data = decodeB64StringToArrayBuffer(data.data)
  84. }
  85. return TE.of(data)
  86. })
  87. )
  88. const axiosWithoutProxy: NetworkStrategy = (req) =>
  89. pipe(
  90. TE.tryCatch(
  91. () =>
  92. axios({
  93. ...req,
  94. cancelToken: (cancelSource && cancelSource.token) || "",
  95. responseType: "arraybuffer",
  96. }),
  97. (e) => (axios.isCancel(e) ? "cancellation" : (e as any))
  98. ),
  99. TE.orElse((e) =>
  100. e !== "cancellation" && e.response
  101. ? TE.right(e.response as NetworkResponse)
  102. : TE.left(e)
  103. )
  104. )
  105. const axiosStrategy: NetworkStrategy = (req) =>
  106. pipe(
  107. req,
  108. settingsStore.value.PROXY_ENABLED ? axiosWithProxy : axiosWithoutProxy
  109. )
  110. export default axiosStrategy