domrobot-client
Version:
INWX Domrobot Node.JS Client
175 lines (147 loc) • 5.44 kB
text/typescript
import * as otplib from 'otplib';
export class ApiClient {
public static readonly CLIENT_VERSION = '3.0.2';
public static readonly API_URL_LIVE = 'https://api.domrobot.com/jsonrpc/';
public static readonly API_URL_OTE = 'https://api.ote.domrobot.com/jsonrpc/';
public static generateClientTransactionId(): string {
return 'DomRobot-' + Math.round(Math.random() * 1000000000);
}
private readonly apiUrl: string;
private language: string;
private debugMode: boolean;
private cookie: string;
private headers: Record<string, any>;
/**
* @param apiUrl url of the API.
* @param language default language for future API requests.
* @param debugMode whether requests and responses should be printed out.
* @param headers Optional HTTP headers to be included in all API requests.
*/
constructor(
apiUrl: string = ApiClient.API_URL_OTE,
language: string = Language.EN,
debugMode: boolean = false,
headers: Record<string, any> = {},
) {
this.apiUrl = apiUrl;
this.language = language;
this.debugMode = debugMode;
this.cookie = null;
this.headers = headers;
}
/**
* Makes an API call.
*
* @param apiMethod The name of the method called in the API.
* @param methodParams An object of parameters added to the request.
* @param clientTransactionId Id sent with every request to distinguish your api requests in case you need support.
* @param language Language for the API request. Default is value of field language.
* @param cacheOption HTTP cache options
* @param nextjsOptions Next.js options
*/
public async callApi(
apiMethod: string,
methodParams: any = {},
clientTransactionId: string = ApiClient.generateClientTransactionId(),
language: string = this.language,
cacheOption?: 'no-store' | 'reload' | 'no-cache' | 'force-cache' | 'only-if-cached' | 'default',
nextjsOptions?: object,
): Promise<any> {
if ('clTRID'! in methodParams && clientTransactionId !== null) {
methodParams.clTRID = clientTransactionId;
}
if ('lang'! in methodParams) {
methodParams.lang = language;
}
const requestBody = JSON.stringify({
method: apiMethod,
params: methodParams,
});
let fetchOption = {
method: 'POST',
headers: new Headers({
'Content-Type': 'application/json',
Cookie: this.cookie,
'User-Agent': `DomRobot/${ApiClient.CLIENT_VERSION} (Node ${process.version})`,
...(this.headers ?? {}),
}),
body: requestBody,
cache: cacheOption ?? 'default',
};
if (nextjsOptions !== undefined) {
fetchOption = { ...fetchOption, ...nextjsOptions };
}
const response = await fetch(this.apiUrl, fetchOption);
if (apiMethod === 'account.login') {
this.cookie = response.headers.get('set-cookie');
}
const data = await response.json();
if (this.debugMode) {
console.info(`Request (${apiMethod}): ${requestBody}`);
console.info(`Response (${apiMethod}): ${JSON.stringify(data)}`);
}
return data;
}
/**
* Performs a login at the API and saves the session for following API calls.
*
* @param username your username.
* @param password your password.
* @param sharedSecret
*/
public async login(username: string, password: string, sharedSecret: string = null): Promise<any> {
const loginResult = await this.callApi('account.login', { user: username, pass: password });
if (loginResult.code === 1000 && 'tfa' in loginResult.resData && loginResult.resData.tfa !== '0') {
if (sharedSecret === null) {
return Promise.reject('API requests two factor challenge but no shared secret is given. Aborting.');
}
const secretCode = otplib.authenticator.generate(sharedSecret);
const unlockResult = await this.callApi('account.unlock', { tan: secretCode });
if (unlockResult.code !== 1000) {
return unlockResult;
}
}
return loginResult;
}
/**
* Performs a logout at the API and destroys the current session.
*/
public async logout(): Promise<any> {
return await this.callApi('account.logout').then(() => (this.cookie = null));
}
public getApiUrl(): string {
return this.apiUrl;
}
public getLanguage(): string {
return this.language;
}
public setLanguage(value: string) {
this.language = value;
}
public isDebugMode(): boolean {
return this.debugMode;
}
public setDebugMode(value: boolean) {
this.debugMode = value;
}
public getCookie(): string {
return this.cookie;
}
public setCookie(cookie: string) {
this.cookie = cookie;
}
public setHeaders(headers: Record<string, any>) {
this.headers = headers;
}
public getHeaders(): Record<string, any> {
return this.headers;
}
}
/**
* The API response-message language.
*/
export enum Language {
EN = 'en',
DE = 'de',
ES = 'es',
}