import { JSDOM } from 'jsdom'; import { RequestInit, Response } from 'node-fetch'; import { Errors, RenaiError } from '../../types/error'; import { fetch } from './web-crawler'; const domain = 'nhentai.net'; const url = `https://${domain}/`; const paths = { books: 'g/', login: 'login/', favorites: 'favorites/', }; const usernameInput = 'username_or_email'; const passwordInput = 'password'; interface ILoginMeta { [key: string]: string; } interface ILoginAuth { [usernameInput]: string; [passwordInput]: string; } interface ILoginParams extends ILoginMeta, ILoginAuth {} function getNHentai(path: string): Promise { return fetch(`${url}${path}`) .then((res: Response) => res.text()) .then((text: string) => { const { document } = new JSDOM(text).window; return document; }); } function postNHentai(path: string, requestInit: RequestInit = {}): Promise { const postUrl = `${url}${path}`; return fetch(postUrl, { ...requestInit, ...{ headers: { ...requestInit.headers, ...{ Host: domain, Referer: postUrl, }, }, }, method: 'post', }); } function getLoginMeta(): Promise { return getNHentai(paths.login).then((document: Document) => { // eslint-disable-next-line @typescript-eslint/prefer-for-of for (let i = 0; i < document.forms.length; i++) { const form: HTMLFormElement = document.forms[i]; const valueStore: ILoginMeta = {}; let isLoginForm = false; // eslint-disable-next-line @typescript-eslint/prefer-for-of for (let j = 0; j < form.elements.length; j++) { const input = form.elements[j]; const name = input.getAttribute('name'); if (name === usernameInput || name === passwordInput) { isLoginForm = true; } else if (name) { valueStore[name] = input.getAttribute('value'); } } if (isLoginForm) { return valueStore; } } return Promise.reject(new RenaiError(Errors.ENOLOGIN)); }); } export function isLoggedIn(): Promise { return fetch(`${url}${paths.favorites}`, { redirect: 'manual' }).then((res: Response) => res.status === HttpCode.OK); } export function login(name: string, password: string): Promise { return getLoginMeta() .then((meta: ILoginMeta) => { const loginParams: ILoginParams = { ...meta, ...{ [usernameInput]: name, [passwordInput]: password, }, }; return postNHentai(paths.login, { body: encodeURI( Object.keys(loginParams) .map((key: keyof ILoginParams) => `${key}=${loginParams[key]}`) .join('&') ), headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, redirect: 'manual', }); }) .then(() => {}) .catch(() => Promise.reject(new RenaiError(Errors.ELOGINFAIL))); }