diff --git a/src/index.ts b/src/index.ts index 70cc822..90e8c1c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,7 +32,15 @@ export { default as List } from './shared/List'; // Export specific functions: -export { useDebugServer, overrideApiOrigin, overrideAuthOrigin } from './util/Network'; +export { + useDebugServer, + overrideApiURL, + overrideAuthURL, + overrideUploadsURL, + getApiURL, + getAuthURL, + getUploadsURL, +} from './util/Network'; import LocalizedString from './internal/LocalizedString'; /** diff --git a/src/shared/Cover.ts b/src/shared/Cover.ts index 08f4c8f..8838393 100644 --- a/src/shared/Cover.ts +++ b/src/shared/Cover.ts @@ -6,6 +6,7 @@ import { fetchMDDataWithBody, fetchMDSearch, fetchMDWithFormData, + getUploadsURL, } from '../util/Network'; import Relationship from '../internal/Relationship'; @@ -85,7 +86,7 @@ export default class Cover extends IDObject implements CoverAttributesSchema { this.updatedAt = new Date(schem.attributes.updatedAt); const parentRelationship = Relationship.createSelfRelationship('cover_art', this); this.manga = Relationship.convertType('manga', schem.relationships, parentRelationship).pop()!; - this.url = `https://mangadex.org/covers/${this.manga.id}/${this.fileName}`; + this.url = `${getUploadsURL()}covers/${this.manga.id}/${this.fileName}`; this.uploader = Relationship.convertType('user', schem.relationships).pop() ?? null; } diff --git a/src/util/Network.ts b/src/util/Network.ts index c23199b..4627e92 100644 --- a/src/util/Network.ts +++ b/src/util/Network.ts @@ -23,18 +23,21 @@ type ListResponse = { data: { id: string }[]; limit: number; offset: number; tot type CustomRequestInit = Omit & { headers?: Record; noAuth?: boolean }; class NetworkStateManager { - static apiOriginOverride: string | undefined; - static authOriginOverride: string | undefined; + static apiURLOverride: string | undefined; + static authURLOverride: string | undefined; + static uploadsURLOverride: string | undefined; static activeClient: IAuthClient | undefined; - static get apiOrigin() { - const rawDomain = this.apiOriginOverride ?? 'https://api.mangadex.org/'; - return new URL(rawDomain).origin; + static get apiURL() { + return this.apiURLOverride ?? 'https://api.mangadex.org/'; } - static get authOrigin() { - const rawDomain = this.authOriginOverride ?? 'https://auth.mangadex.org/'; - return new URL(rawDomain).origin; + static get authURL() { + return this.authURLOverride ?? 'https://auth.mangadex.org/'; + } + + static get uploadsURL() { + return this.uploadsURLOverride ?? 'https://uploads.mangadex.org/'; } } @@ -46,32 +49,80 @@ export function useDebugServer(val: boolean) { const devApiDomain = 'https://api.mangadex.dev'; const devAuthDomain = 'https://auth.mangadex.dev'; if (val) { - NetworkStateManager.apiOriginOverride = devApiDomain; - NetworkStateManager.authOriginOverride = devAuthDomain; + NetworkStateManager.apiURLOverride = devApiDomain; + NetworkStateManager.authURLOverride = devAuthDomain; } else { - if (NetworkStateManager.apiOriginOverride === devApiDomain) { - NetworkStateManager.apiOriginOverride = undefined; + // is this necessary? + if (NetworkStateManager.apiURLOverride === devApiDomain) { + NetworkStateManager.apiURLOverride = undefined; } - if (NetworkStateManager.authOriginOverride === devAuthDomain) { - NetworkStateManager.authOriginOverride = undefined; + if (NetworkStateManager.authURLOverride === devAuthDomain) { + NetworkStateManager.authURLOverride = undefined; } } } +/** + * Returns the URL used for api calls. + * https://api.mangadex.org/ by default. + */ +export function getApiURL() { + return NetworkStateManager.apiURL; +} + +/** + * Returns the URL used for auth calls. + * https://auth.mangadex.org/ by default. + */ +export function getAuthURL() { + return NetworkStateManager.authURL; +} + +/** + * Returns the URL used for uploads calls. + * https://uploads.mangadex.org/ by default. + */ +export function getUploadsURL() { + return NetworkStateManager.uploadsURL; +} + /** * Changes the origin used by api calls to a custom one, or clears it if the passed value is undefined. - * @param domain - The new domain (e.g. https://example.com) + * @param url - The new URL (e.g. https://example.com) */ -export function overrideApiOrigin(domain: string | undefined) { - NetworkStateManager.apiOriginOverride = domain; +export function overrideApiURL(url: string | undefined) { + if (url) { + const newUrl = new URL(url).toString(); + NetworkStateManager.apiURLOverride = newUrl.endsWith('/') ? newUrl : newUrl + '/'; + } else { + NetworkStateManager.apiURLOverride = undefined; + } } /** * Changes the origin used by authentication calls to a custom one, or clears it if the passed value is undefined. - * @param domain - The new domain (e.g. https://example.com) + * @param url - The new URL (e.g. https://example.com) + */ +export function overrideAuthURL(url: string | undefined) { + if (url) { + const newUrl = new URL(url).toString(); + NetworkStateManager.authURLOverride = newUrl.endsWith('/') ? newUrl : newUrl + '/'; + } else { + NetworkStateManager.authURLOverride = undefined; + } +} + +/** + * Changes the origin used by uploads calls to a custom one, or clears it if the passed value is undefined. + * @param url - The new URL (e.g. https://example.com) */ -export function overrideAuthOrigin(domain: string | undefined) { - NetworkStateManager.authOriginOverride = domain; +export function overrideUploadsURL(url: string | undefined) { + if (url) { + const newUrl = new URL(url).toString(); + NetworkStateManager.uploadsURLOverride = newUrl.endsWith('/') ? newUrl : newUrl + '/'; + } else { + NetworkStateManager.uploadsURLOverride = undefined; + } } /** @@ -104,8 +155,9 @@ export async function fetchMD( params?: ParameterObj, requestInit: CustomRequestInit = {}, ): Promise { - const domain = NetworkStateManager.apiOrigin; - const url = buildURL(domain, endpoint, params); + const domain = NetworkStateManager.apiURL; + const normalizedEndpoint = endpoint.startsWith('/') ? endpoint.slice(1) : endpoint; + const url = buildURL(domain, normalizedEndpoint, params); if (NetworkStateManager.activeClient && !requestInit.noAuth) { const sessionToken = await NetworkStateManager.activeClient.getSessionToken(); @@ -265,7 +317,8 @@ export async function fetchMDDataWithBody( * Performs a POST fetch request to api.mangadex.network with a JSON body */ export async function postToMDNetwork(endpoint: string, body: object, params?: ParameterObj): Promise { - const url = buildURL('https://api.mangadex.network', endpoint, params); + const normalizedEndpoint = endpoint.startsWith('/') ? endpoint.slice(1) : endpoint; + const url = buildURL('https://api.mangadex.network', normalizedEndpoint, params); const res = await fetch(url, { body: JSON.stringify(body), method: 'POST', @@ -373,8 +426,9 @@ export async function performAuthCheck(sessionToken?: string): Promise export async function fetchMDAuth(endpoint: string, body: Record): Promise { const params = new URLSearchParams(); for (const [name, value] of Object.entries(body)) params.append(name, value); - const domain = NetworkStateManager.authOrigin; - const url = new URL(endpoint, domain); + const domain = NetworkStateManager.authURL; + const normalizedEndpoint = endpoint.startsWith('/') ? endpoint.slice(1) : endpoint; + const url = new URL(normalizedEndpoint, domain); const res = await fetch(url, { body: params, method: 'POST', diff --git a/tests/network.test.ts b/tests/network.test.ts index e3fd6de..d608e6c 100644 --- a/tests/network.test.ts +++ b/tests/network.test.ts @@ -1,4 +1,4 @@ -import { fetchMD, fetchMDAuth, overrideApiOrigin, overrideAuthOrigin, useDebugServer } from '../src/util/Network'; +import { fetchMD, fetchMDAuth, overrideApiURL, overrideAuthURL, useDebugServer } from '../src/util/Network'; function createFetchMock() { return jest.fn().mockResolvedValue({ @@ -26,7 +26,7 @@ test('Override Auth Origin', async () => { global.fetch = mockFetch; const testOrigin = 'http://localhost'; - overrideAuthOrigin(testOrigin); + overrideAuthURL(testOrigin); await fetchMDAuth('/test-endpoint', { param: 'value' }); @@ -41,7 +41,7 @@ test('Override Api Origin', async () => { global.fetch = mockFetch; const testOrigin = 'http://localhost'; - overrideApiOrigin(testOrigin); + overrideApiURL(testOrigin); await fetchMD('/test-endpoint', { param: 'value' });