import axios, { type AxiosError } from "axios" import type { AddCategoryRequest, AdminSaveUserRequest, AuthenticationError, Category, CategoryModificationRequest, CollapseRequest, Entries, FeedInfo, FeedInfoRequest, FeedModificationRequest, GetEntriesPaginatedRequest, IDRequest, LoginRequest, MarkRequest, Metrics, MultipleMarkRequest, PasswordResetRequest, ProfileModificationRequest, RegistrationRequest, ServerInfo, Settings, StarRequest, SubscribeRequest, Subscription, TagRequest, UserModel, } from "./types" const axiosInstance = axios.create({ baseURL: "./rest", withCredentials: true }) axiosInstance.interceptors.response.use( response => response, error => { if (isAuthenticationError(error)) { const data = error.response?.data window.location.hash = data?.allowRegistrations ? "/welcome" : "/login" } throw error } ) function isAuthenticationError(error: unknown): error is AxiosError { return axios.isAxiosError(error) && !!error.response && [401, 403].includes(error.response.status) } export const client = { category: { getRoot: async () => await axiosInstance.get("category/get"), modify: async (req: CategoryModificationRequest) => await axiosInstance.post("category/modify", req), collapse: async (req: CollapseRequest) => await axiosInstance.post("category/collapse", req), getEntries: async (req: GetEntriesPaginatedRequest) => await axiosInstance.get("category/entries", { params: req }), markEntries: async (req: MarkRequest) => await axiosInstance.post("category/mark", req), add: async (req: AddCategoryRequest) => await axiosInstance.post("category/add", req), delete: async (req: IDRequest) => await axiosInstance.post("category/delete", req), }, entry: { mark: async (req: MarkRequest) => await axiosInstance.post("entry/mark", req), markMultiple: async (req: MultipleMarkRequest) => await axiosInstance.post("entry/markMultiple", req), star: async (req: StarRequest) => await axiosInstance.post("entry/star", req), getTags: async () => await axiosInstance.get("entry/tags"), tag: async (req: TagRequest) => await axiosInstance.post("entry/tag", req), }, feed: { get: async (id: string) => await axiosInstance.get(`feed/get/${id}`), modify: async (req: FeedModificationRequest) => await axiosInstance.post("feed/modify", req), getEntries: async (req: GetEntriesPaginatedRequest) => await axiosInstance.get("feed/entries", { params: req }), markEntries: async (req: MarkRequest) => await axiosInstance.post("feed/mark", req), fetchFeed: async (req: FeedInfoRequest) => await axiosInstance.post("feed/fetch", req), refreshAll: async () => await axiosInstance.get("feed/refreshAll"), subscribe: async (req: SubscribeRequest) => await axiosInstance.post("feed/subscribe", req), unsubscribe: async (req: IDRequest) => await axiosInstance.post("feed/unsubscribe", req), importOpml: async (req: File) => { const formData = new FormData() formData.append("file", req) return await axiosInstance.post("feed/import", formData, { headers: { "Content-Type": "multipart/form-data", }, }) }, }, user: { login: async (req: LoginRequest) => await axiosInstance.post("user/login", req), register: async (req: RegistrationRequest) => await axiosInstance.post("user/register", req), passwordReset: async (req: PasswordResetRequest) => await axiosInstance.post("user/passwordReset", req), getSettings: async () => await axiosInstance.get("user/settings"), saveSettings: async (settings: Settings) => await axiosInstance.post("user/settings", settings), getProfile: async () => await axiosInstance.get("user/profile"), saveProfile: async (req: ProfileModificationRequest) => await axiosInstance.post("user/profile", req), deleteProfile: async () => await axiosInstance.post("user/profile/deleteAccount"), }, server: { getServerInfos: async () => await axiosInstance.get("server/get"), }, admin: { getAllUsers: async () => await axiosInstance.get("admin/user/getAll"), saveUser: async (req: AdminSaveUserRequest) => await axiosInstance.post("admin/user/save", req), deleteUser: async (req: IDRequest) => await axiosInstance.post("admin/user/delete", req), getMetrics: async () => await axiosInstance.get("admin/metrics"), }, } /** * transform an error object to an array of strings that can be displayed to the user * @param err an error object (e.g. from axios) * @returns an array of messages to show the user */ export const errorToStrings = (err: unknown) => { let strings: string[] = [] if (axios.isAxiosError(err) && err.response) { if (typeof err.response.data === "string") strings.push(err.response.data) if (isMessageError(err)) strings.push(err.response.data.message) if (isMessageArrayError(err)) strings = [...strings, ...err.response.data.errors] } return strings } function isMessageError(err: AxiosError): err is AxiosError<{ message: string }> { return !!err.response && !!err.response.data && typeof err.response.data === "object" && "message" in err.response.data } function isMessageArrayError(err: AxiosError): err is AxiosError<{ errors: string[] }> { return !!err.response && !!err.response.data && typeof err.response.data === "object" && "errors" in err.response.data }