Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
/**
Expand Down
3 changes: 2 additions & 1 deletion src/shared/Cover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
fetchMDDataWithBody,
fetchMDSearch,
fetchMDWithFormData,
getUploadsURL,
} from '../util/Network';
import Relationship from '../internal/Relationship';

Expand Down Expand Up @@ -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>('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>('user', schem.relationships).pop() ?? null;
}

Expand Down
104 changes: 79 additions & 25 deletions src/util/Network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,21 @@ type ListResponse = { data: { id: string }[]; limit: number; offset: number; tot
type CustomRequestInit = Omit<RequestInit, 'headers'> & { headers?: Record<string, string>; 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/';
}
}

Expand All @@ -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;
}
}

/**
Expand Down Expand Up @@ -104,8 +155,9 @@ export async function fetchMD<T extends object>(
params?: ParameterObj,
requestInit: CustomRequestInit = {},
): Promise<T> {
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();
Expand Down Expand Up @@ -265,7 +317,8 @@ export async function fetchMDDataWithBody<T extends { data: unknown }>(
* Performs a POST fetch request to api.mangadex.network with a JSON body
*/
export async function postToMDNetwork(endpoint: string, body: object, params?: ParameterObj): Promise<void> {
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',
Expand Down Expand Up @@ -373,8 +426,9 @@ export async function performAuthCheck(sessionToken?: string): Promise<boolean>
export async function fetchMDAuth<T extends object>(endpoint: string, body: Record<string, string>): Promise<T> {
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',
Expand Down
6 changes: 3 additions & 3 deletions tests/network.test.ts
Original file line number Diff line number Diff line change
@@ -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({
Expand Down Expand Up @@ -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' });

Expand All @@ -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' });

Expand Down