111 lines
2.9 KiB
TypeScript
111 lines
2.9 KiB
TypeScript
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<Document> {
|
|
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<Response> {
|
|
const postUrl = `${url}${path}`;
|
|
return fetch(postUrl, {
|
|
...requestInit,
|
|
...{
|
|
headers: {
|
|
...requestInit.headers,
|
|
...{
|
|
Host: domain,
|
|
Referer: postUrl,
|
|
},
|
|
},
|
|
},
|
|
method: 'post',
|
|
});
|
|
}
|
|
|
|
function getLoginMeta(): Promise<ILoginMeta> {
|
|
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<boolean> {
|
|
return fetch(`${url}${paths.favorites}`, { redirect: 'manual' }).then((res: Response) => res.status === HttpCode.OK);
|
|
}
|
|
|
|
export function login(name: string, password: string): Promise<void> {
|
|
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)));
|
|
}
|