/** * Copyright (c) 2023-present Plane Software, Inc. and contributors * SPDX-License-Identifier: AGPL-3.0-only * See the LICENSE file for details. */ /* eslint-disable @typescript-eslint/no-explicit-any */ import type { AxiosInstance, AxiosRequestConfig } from "axios"; import axios from "axios"; export abstract class APIService { protected baseURL: string; private axiosInstance: AxiosInstance; constructor(baseURL: string) { this.baseURL = baseURL; this.axiosInstance = axios.create({ baseURL, withCredentials: true, }); this.setupInterceptors(); } private setupInterceptors() { this.axiosInstance.interceptors.response.use( (response) => response, (error) => { if (error.response && error.response.status === 401) { // binarybeachio fork — re-auth must miss the browser HTTP cache. // Vanilla Plane navigated to `/?next_path=` which is the // SPA root route; the browser served the cached SPA bundle, the SPA // re-fetched the same /api endpoint, the call 401'd again, and the // page looped without ever hitting the network at the document // level. Plane's Traefik plane-signin-redirect router (priority 200, // matching `^/(sign-in|...)`) catches `/sign-in/` regardless of // cookie state and 302s to the bridge handoff — but only if the // browser actually fetches it. Append a ts param so the URL is // never in cache, and target /sign-in/ so the priority-200 router // wins. Vanilla Plane handles /sign-in/ in its SPA too (the SPA // bounces it to /), so the patch is also benign in non-platform // deployments. See binarybeachio/docs/conventions/per-app-edge- // identity-validation.md and feedback_plane_spa_cached_loop. window.location.replace(`/sign-in/?_bb_reauth=${Date.now()}`); } return Promise.reject(error); } ); } get(url: string, params = {}, config: AxiosRequestConfig = {}) { return this.axiosInstance.get(url, { ...params, ...config, }); } post(url: string, data = {}, config: AxiosRequestConfig = {}) { return this.axiosInstance.post(url, data, config); } put(url: string, data = {}, config: AxiosRequestConfig = {}) { return this.axiosInstance.put(url, data, config); } patch(url: string, data = {}, config: AxiosRequestConfig = {}) { return this.axiosInstance.patch(url, data, config); } delete(url: string, data?: any, config: AxiosRequestConfig = {}) { return this.axiosInstance.delete(url, { data, ...config }); } request(config = {}) { return this.axiosInstance(config); } }