import { baseUrl, sseBaseUrl } from "../utils"; import { AuthContext } from "../utils/Context"; interface ConfigType { body?: BodyInit | Record | unknown; headers?: Record; } type PostBody = BodyInit | Record | unknown; export async function HttpClient( endpoint: string, method: string, { body, ...customConfig }: ConfigType = {} ): Promise { const config = { method: method, body: body ? JSON.stringify(body) : undefined, headers: { "Content-Type": "application/json" }, // NOTE: customConfig can override the above defined settings ...customConfig } as RequestInit; return window.fetch(`${baseUrl()}${endpoint}`, config) .then(async response => { if (!response.ok) { // if 401 consider the session expired and force logout if (response.status === 401) { // Remove auth info from localStorage AuthContext.reset(); // Show an error toast to notify the user what occurred return Promise.resolve(response); } return Promise.reject(new Error(await response.text())); } // Resolve immediately since 204 contains no data if (response.status === 204) return Promise.resolve(response); return await response.json(); }); } const appClient = { Get: (endpoint: string) => HttpClient(endpoint, "GET"), Post: (endpoint: string, data: PostBody = undefined) => HttpClient(endpoint, "POST", { body: data }), Put: (endpoint: string, data: PostBody) => HttpClient(endpoint, "PUT", { body: data }), Patch: (endpoint: string, data: PostBody = undefined) => HttpClient(endpoint, "PATCH", { body: data }), Delete: (endpoint: string) => HttpClient(endpoint, "DELETE") }; export const APIClient = { auth: { login: (username: string, password: string) => appClient.Post("api/auth/login", { username: username, password: password }), logout: () => appClient.Post("api/auth/logout"), validate: () => appClient.Get("api/auth/validate"), onboard: (username: string, password: string) => appClient.Post("api/auth/onboard", { username: username, password: password }), canOnboard: () => appClient.Get("api/auth/onboard") }, actions: { create: (action: Action) => appClient.Post("api/actions", action), update: (action: Action) => appClient.Put(`api/actions/${action.id}`, action), delete: (id: number) => appClient.Delete(`api/actions/${id}`), toggleEnable: (id: number) => appClient.Patch(`api/actions/${id}/toggleEnabled`) }, apikeys: { getAll: () => appClient.Get("api/keys"), create: (key: APIKey) => appClient.Post("api/keys", key), delete: (key: string) => appClient.Delete(`api/keys/${key}`) }, config: { get: () => appClient.Get("api/config") }, download_clients: { getAll: () => appClient.Get("api/download_clients"), create: (dc: DownloadClient) => appClient.Post("api/download_clients", dc), update: (dc: DownloadClient) => appClient.Put("api/download_clients", dc), delete: (id: number) => appClient.Delete(`api/download_clients/${id}`), test: (dc: DownloadClient) => appClient.Post("api/download_clients/test", dc) }, filters: { getAll: () => appClient.Get("api/filters"), find: (indexers: string[], sortOrder: string) => { const params = new URLSearchParams(); if (sortOrder.length > 0) { params.append("sort", sortOrder); } indexers?.forEach((i) => { if (i !== undefined || i !== "") { params.append("indexer", i); } }); const p = params.toString(); const q = p ? `?${p}` : ""; return appClient.Get(`api/filters${q}`); }, getByID: (id: number) => appClient.Get(`api/filters/${id}`), create: (filter: Filter) => appClient.Post("api/filters", filter), update: (filter: Filter) => appClient.Put(`api/filters/${filter.id}`, filter), duplicate: (id: number) => appClient.Get(`api/filters/${id}/duplicate`), toggleEnable: (id: number, enabled: boolean) => appClient.Put(`api/filters/${id}/enabled`, { enabled }), delete: (id: number) => appClient.Delete(`api/filters/${id}`) }, feeds: { find: () => appClient.Get("api/feeds"), create: (feed: FeedCreate) => appClient.Post("api/feeds", feed), toggleEnable: (id: number, enabled: boolean) => appClient.Patch(`api/feeds/${id}/enabled`, { enabled }), update: (feed: Feed) => appClient.Put(`api/feeds/${feed.id}`, feed), delete: (id: number) => appClient.Delete(`api/feeds/${id}`), test: (feed: Feed) => appClient.Post("api/feeds/test", feed) }, indexers: { // returns indexer options for all currently present/enabled indexers getOptions: () => appClient.Get("api/indexer/options"), // returns indexer definitions for all currently present/enabled indexers getAll: () => appClient.Get("api/indexer"), // returns all possible indexer definitions getSchema: () => appClient.Get("api/indexer/schema"), create: (indexer: Indexer) => appClient.Post("api/indexer", indexer), update: (indexer: Indexer) => appClient.Put("api/indexer", indexer), delete: (id: number) => appClient.Delete(`api/indexer/${id}`) }, irc: { getNetworks: () => appClient.Get("api/irc"), createNetwork: (network: IrcNetworkCreate) => appClient.Post("api/irc", network), updateNetwork: (network: IrcNetwork) => appClient.Put(`api/irc/network/${network.id}`, network), deleteNetwork: (id: number) => appClient.Delete(`api/irc/network/${id}`), restartNetwork: (id: number) => appClient.Get(`api/irc/network/${id}/restart`) }, events: { logs: () => new EventSource(`${sseBaseUrl()}api/events?stream=logs`, { withCredentials: true }) }, notifications: { getAll: () => appClient.Get("api/notification"), create: (notification: Notification) => appClient.Post("api/notification", notification), update: (notification: Notification) => appClient.Put(`api/notification/${notification.id}`, notification), delete: (id: number) => appClient.Delete(`api/notification/${id}`), test: (n: Notification) => appClient.Post("api/notification/test", n) }, release: { find: (query?: string) => appClient.Get(`api/release${query}`), findRecent: () => appClient.Get("api/release/recent"), findQuery: (offset?: number, limit?: number, filters?: Array) => { const params = new URLSearchParams(); if (offset !== undefined && offset > 0) params.append("offset", offset.toString()); if (limit !== undefined) params.append("limit", limit.toString()); filters?.forEach((filter) => { if (!filter.value) return; if (filter.id == "indexer") params.append("indexer", filter.value); else if (filter.id === "action_status") params.append("push_status", filter.value); else if (filter.id == "torrent_name") params.append("q", filter.value); }); return appClient.Get(`api/release?${params.toString()}`); }, indexerOptions: () => appClient.Get("api/release/indexers"), stats: () => appClient.Get("api/release/stats"), delete: () => appClient.Delete("api/release/all") } };