Initial Agrarmonitor connector
This commit is contained in:
@@ -0,0 +1,392 @@
|
||||
import axios, { type AxiosInstance, type AxiosRequestConfig, type AxiosResponse } from 'axios';
|
||||
import { wrapper } from 'axios-cookiejar-support';
|
||||
import { JSDOM } from 'jsdom';
|
||||
import { CookieJar } from 'tough-cookie';
|
||||
import type {
|
||||
AgrarmonitorApiCustomer,
|
||||
AgrarmonitorConnectorOptions,
|
||||
AgrarmonitorConnectorResult,
|
||||
AgrarmonitorDeviceRegistrationOptions,
|
||||
AgrarmonitorDeviceRegistrationResult,
|
||||
AgrarmonitorFetchCustomersOptions,
|
||||
AgrarmonitorFreischaltungStatus,
|
||||
AgrarmonitorLoginStrategy,
|
||||
AgrarmonitorRegistrierungStatus,
|
||||
Logger,
|
||||
} from './types';
|
||||
|
||||
type RetryableAxiosRequestConfig = AxiosRequestConfig & {
|
||||
_agrarmonitorRetry?: boolean;
|
||||
};
|
||||
|
||||
export class AgrarmonitorConnector implements AgrarmonitorConnectorResult {
|
||||
public http!: AxiosInstance;
|
||||
|
||||
private readonly baseUrl: string;
|
||||
private readonly apiBaseUrl: string;
|
||||
private readonly timeoutMs: number;
|
||||
private readonly autoLogin: boolean;
|
||||
private readonly autoRetry: boolean;
|
||||
private readonly loginStrategy: AgrarmonitorLoginStrategy;
|
||||
private readonly logger?: Logger;
|
||||
private cookieJar!: CookieJar;
|
||||
private loginInProgress: Promise<void> | null = null;
|
||||
|
||||
constructor(private readonly options: AgrarmonitorConnectorOptions) {
|
||||
this.baseUrl = options.baseUrl ?? 'https://admin7.agrarmonitor.de';
|
||||
this.apiBaseUrl = options.apiBaseUrl ?? 'https://api.agrarmonitor.de';
|
||||
this.timeoutMs = options.timeoutMs ?? 15000;
|
||||
this.autoLogin = options.autoLogin ?? true;
|
||||
this.autoRetry = options.autoRetry ?? true;
|
||||
this.loginStrategy = options.loginStrategy ?? 'auto';
|
||||
this.logger = options.logger;
|
||||
}
|
||||
|
||||
async init(): Promise<this> {
|
||||
this.cookieJar = await this.options.cookieStore.load();
|
||||
this.http = this.createHttpClient();
|
||||
|
||||
if (this.autoLogin) {
|
||||
const valid = await this.isSessionValid();
|
||||
|
||||
if (!valid) {
|
||||
await this.login();
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
async login(): Promise<void> {
|
||||
if (this.loginInProgress) {
|
||||
return this.loginInProgress;
|
||||
}
|
||||
|
||||
this.loginInProgress = this.performLogin().finally(() => {
|
||||
this.loginInProgress = null;
|
||||
});
|
||||
|
||||
return this.loginInProgress;
|
||||
}
|
||||
|
||||
async clearSession(): Promise<void> {
|
||||
this.cookieJar = new CookieJar();
|
||||
await this.options.cookieStore.clear();
|
||||
this.http = this.createHttpClient();
|
||||
}
|
||||
|
||||
async saveSession(): Promise<void> {
|
||||
await this.options.cookieStore.save(this.cookieJar);
|
||||
}
|
||||
|
||||
async getCookieCount(url = this.baseUrl): Promise<number> {
|
||||
return this.cookieJar.getCookiesSync(url).length;
|
||||
}
|
||||
|
||||
async checkFreigeschaltet(): Promise<AgrarmonitorFreischaltungStatus> {
|
||||
const response = await this.http.get('/', {
|
||||
maxRedirects: 0,
|
||||
validateStatus: status => status >= 200 && status < 400,
|
||||
});
|
||||
|
||||
await this.saveSession();
|
||||
|
||||
const redirectLocation = this.getHeader(response, 'location');
|
||||
const redirected = response.status >= 300 && response.status < 400 && this.isFreischaltungUrl(redirectLocation);
|
||||
|
||||
return {
|
||||
freigeschaltet: !redirected,
|
||||
status: response.status,
|
||||
redirected,
|
||||
redirectLocation,
|
||||
timestamp: new Date().toISOString(),
|
||||
cookies: await this.getCookieCount(),
|
||||
};
|
||||
}
|
||||
|
||||
async checkRegistriert(): Promise<AgrarmonitorRegistrierungStatus> {
|
||||
const response = await this.http.get('/', {
|
||||
maxRedirects: 5,
|
||||
validateStatus: status => status >= 200 && status < 500,
|
||||
});
|
||||
|
||||
await this.saveSession();
|
||||
|
||||
const pageContent = typeof response.data === 'string' ? response.data : '';
|
||||
const hasRegistrationText = pageContent.includes('Neues Gerät registrieren');
|
||||
|
||||
return {
|
||||
registriert: !hasRegistrationText,
|
||||
status: response.status,
|
||||
hasRegistrationText,
|
||||
timestamp: new Date().toISOString(),
|
||||
cookies: await this.getCookieCount(),
|
||||
};
|
||||
}
|
||||
|
||||
async registerDevice(
|
||||
registration: AgrarmonitorDeviceRegistrationOptions
|
||||
): Promise<AgrarmonitorDeviceRegistrationResult> {
|
||||
const agrarmonitorId = registration.agrarmonitorId.trim();
|
||||
const pcName = registration.pcName.trim();
|
||||
|
||||
if (!agrarmonitorId || !pcName) {
|
||||
throw new Error('AgrarmonitorID und PC-Name sind erforderlich');
|
||||
}
|
||||
|
||||
const freischaltungResponse = await this.http.get('/freischaltung/');
|
||||
const responseContent = typeof freischaltungResponse.data === 'string' ? freischaltungResponse.data : '';
|
||||
const nonce = this.extractNonce(responseContent, '#nonce, input[name="nonce"]');
|
||||
|
||||
const registerResponse = await this.http.post(
|
||||
'/freischaltung/api/register.php',
|
||||
{
|
||||
firma: agrarmonitorId,
|
||||
name: pcName,
|
||||
nonce,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
validateStatus: status => status >= 200 && status < 500,
|
||||
}
|
||||
);
|
||||
|
||||
await this.saveSession();
|
||||
|
||||
const success = registerResponse.status >= 200 && registerResponse.status < 300;
|
||||
|
||||
return {
|
||||
success,
|
||||
status: registerResponse.status,
|
||||
message: success ? 'Registrierung erfolgreich' : 'Registrierung fehlgeschlagen',
|
||||
data: {
|
||||
agrarmonitorId,
|
||||
pcName,
|
||||
nonce: this.maskNonce(nonce),
|
||||
},
|
||||
timestamp: new Date().toISOString(),
|
||||
cookies: await this.getCookieCount(),
|
||||
};
|
||||
}
|
||||
|
||||
async fetchCustomers(options: AgrarmonitorFetchCustomersOptions = {}): Promise<AgrarmonitorApiCustomer[]> {
|
||||
const apiToken = options.apiToken ?? this.options.apiToken;
|
||||
|
||||
if (!apiToken) {
|
||||
throw new Error('Agrarmonitor API-Token nicht konfiguriert');
|
||||
}
|
||||
|
||||
const response = await this.http.get(`${this.apiBaseUrl}/v1/kunden`, {
|
||||
params: {
|
||||
per_page: options.perPage ?? 99999,
|
||||
api_token: apiToken,
|
||||
},
|
||||
});
|
||||
|
||||
await this.saveSession();
|
||||
|
||||
const responseData = response.data as { data?: unknown };
|
||||
|
||||
if (!responseData || !Array.isArray(responseData.data)) {
|
||||
throw new Error('Ungueltige Agrarmonitor API-Antwort');
|
||||
}
|
||||
|
||||
return responseData.data as AgrarmonitorApiCustomer[];
|
||||
}
|
||||
|
||||
private createHttpClient(): AxiosInstance {
|
||||
const client = wrapper(
|
||||
axios.create({
|
||||
baseURL: this.baseUrl,
|
||||
jar: this.cookieJar,
|
||||
withCredentials: true,
|
||||
timeout: this.timeoutMs,
|
||||
headers: {
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
|
||||
},
|
||||
maxRedirects: 5,
|
||||
validateStatus: status => status >= 200 && status < 400,
|
||||
})
|
||||
);
|
||||
|
||||
client.interceptors.response.use(
|
||||
async response => {
|
||||
await this.options.cookieStore.save(this.cookieJar);
|
||||
|
||||
if (this.autoRetry && this.isLoginRequiredResponse(response)) {
|
||||
return this.retryAfterLogin(response.config);
|
||||
}
|
||||
|
||||
return response;
|
||||
},
|
||||
async error => {
|
||||
const response = error.response as AxiosResponse | undefined;
|
||||
|
||||
if (this.autoRetry && response && this.isLoginRequiredResponse(response)) {
|
||||
return this.retryAfterLogin(error.config);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
private async performLogin(): Promise<void> {
|
||||
if (!this.options.username || !this.options.password) {
|
||||
throw new Error('Agrarmonitor-Credentials nicht konfiguriert');
|
||||
}
|
||||
|
||||
this.logger?.info?.('Fuehre Agrarmonitor-Login durch');
|
||||
|
||||
if (this.loginStrategy === 'auth') {
|
||||
await this.performAuthLogin();
|
||||
} else if (this.loginStrategy === 'legacy') {
|
||||
await this.performLegacyLogin();
|
||||
} else {
|
||||
await this.performAutoLogin();
|
||||
}
|
||||
|
||||
await this.options.cookieStore.save(this.cookieJar);
|
||||
this.logger?.info?.('Agrarmonitor-Login erfolgreich');
|
||||
}
|
||||
|
||||
private async performAutoLogin(): Promise<void> {
|
||||
try {
|
||||
await this.performAuthLogin();
|
||||
} catch (authError) {
|
||||
this.logger?.warn?.('Agrarmonitor-Login via /auth/login fehlgeschlagen, versuche Legacy-Login', authError);
|
||||
await this.performLegacyLogin();
|
||||
}
|
||||
}
|
||||
|
||||
private async performAuthLogin(): Promise<void> {
|
||||
await this.http.get('/auth/login');
|
||||
|
||||
const loginData = new URLSearchParams({
|
||||
email: this.options.username,
|
||||
password: this.options.password,
|
||||
remember: 'on',
|
||||
});
|
||||
|
||||
const response = await this.http.post('/auth/login', loginData, {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
});
|
||||
|
||||
const responseText = typeof response.data === 'string' ? response.data : '';
|
||||
|
||||
if (responseText.includes('Anmeldung fehlgeschlagen')) {
|
||||
throw new Error('Agrarmonitor-Login fehlgeschlagen');
|
||||
}
|
||||
}
|
||||
|
||||
private async performLegacyLogin(): Promise<void> {
|
||||
const loginPageResponse = await this.http.get('/');
|
||||
const loginPageText = typeof loginPageResponse.data === 'string' ? loginPageResponse.data : '';
|
||||
|
||||
if (!this.isLoginPageText(loginPageText)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nonce = this.extractNonce(loginPageText, 'input[name="nonce"]');
|
||||
const loginData = new URLSearchParams({
|
||||
username: this.options.username,
|
||||
passwort: this.options.password,
|
||||
nonce,
|
||||
});
|
||||
|
||||
const response = await this.http.post('/redirect.php?id=benutzerverwaltung&action=login', loginData, {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
});
|
||||
|
||||
const responseText = typeof response.data === 'string' ? response.data : '';
|
||||
|
||||
if (this.isLoginPageText(responseText)) {
|
||||
throw new Error('Agrarmonitor-Legacy-Login fehlgeschlagen');
|
||||
}
|
||||
}
|
||||
|
||||
private async isSessionValid(): Promise<boolean> {
|
||||
try {
|
||||
const response = await this.http.get('/');
|
||||
return !this.isLoginRequiredResponse(response);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private isLoginRequiredResponse(response: AxiosResponse): boolean {
|
||||
const responseUrl = this.getResponseUrl(response);
|
||||
const responseText = typeof response.data === 'string' ? response.data : '';
|
||||
|
||||
return (
|
||||
response.status === 401 ||
|
||||
response.status === 403 ||
|
||||
responseUrl.includes('/auth/login') ||
|
||||
responseText.includes('/auth/login') ||
|
||||
this.isLoginPageText(responseText) ||
|
||||
responseText.includes('Anmeldung') ||
|
||||
responseText.includes('Einloggen')
|
||||
);
|
||||
}
|
||||
|
||||
private async retryAfterLogin(config: RetryableAxiosRequestConfig): Promise<AxiosResponse> {
|
||||
if (config._agrarmonitorRetry) {
|
||||
throw new Error('Agrarmonitor-Request nach erneutem Login weiterhin nicht autorisiert');
|
||||
}
|
||||
|
||||
config._agrarmonitorRetry = true;
|
||||
|
||||
this.logger?.info?.('Agrarmonitor-Session abgelaufen, erneuter Login wird ausgefuehrt');
|
||||
await this.login();
|
||||
|
||||
return this.http.request(config);
|
||||
}
|
||||
|
||||
private getResponseUrl(response: AxiosResponse): string {
|
||||
const request = response.request as { res?: { responseUrl?: string } } | undefined;
|
||||
return request?.res?.responseUrl ?? '';
|
||||
}
|
||||
|
||||
private getHeader(response: AxiosResponse, header: string): string | null {
|
||||
const value = response.headers[header.toLowerCase()];
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return value[0] ?? null;
|
||||
}
|
||||
|
||||
return typeof value === 'string' ? value : null;
|
||||
}
|
||||
|
||||
private isFreischaltungUrl(value: string | null): boolean {
|
||||
return Boolean(value?.includes('freischaltung'));
|
||||
}
|
||||
|
||||
private isLoginPageText(responseText: string): boolean {
|
||||
return responseText.includes('Anmeldung - AGRARMONITOR');
|
||||
}
|
||||
|
||||
private extractNonce(html: string, selector: string): string {
|
||||
const dom = new JSDOM(html);
|
||||
const element = dom.window.document.querySelector<HTMLInputElement>(selector);
|
||||
const nonce = element?.getAttribute('value') ?? element?.value ?? '';
|
||||
|
||||
if (!nonce) {
|
||||
throw new Error('Nonce-Element nicht gefunden oder leer');
|
||||
}
|
||||
|
||||
return nonce;
|
||||
}
|
||||
|
||||
private maskNonce(nonce: string): string {
|
||||
return nonce.length <= 10 ? nonce : `${nonce.slice(0, 10)}...`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { Cookie, CookieJar } from 'tough-cookie';
|
||||
import type { CookieEncryptor, CookieStore, Logger } from '../types';
|
||||
|
||||
interface FileCookieStoreOptions {
|
||||
encryptor?: CookieEncryptor;
|
||||
logger?: Logger;
|
||||
}
|
||||
|
||||
type PlainCookieFile = ReturnType<CookieJar['toJSON']>;
|
||||
|
||||
type EncryptedCookieFile = {
|
||||
encrypted: true;
|
||||
algorithm: 'aes-256-gcm';
|
||||
data: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export class FileCookieStore implements CookieStore {
|
||||
private static readonly sharedJars = new Map<string, CookieJar>();
|
||||
|
||||
constructor(
|
||||
private readonly filePath: string,
|
||||
private readonly options: FileCookieStoreOptions = {}
|
||||
) {}
|
||||
|
||||
async load(): Promise<CookieJar> {
|
||||
const sharedJar = FileCookieStore.sharedJars.get(this.filePath);
|
||||
if (sharedJar) {
|
||||
return sharedJar;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!fs.existsSync(this.filePath)) {
|
||||
this.options.logger?.info?.('Neuer Cookie-Store wird erstellt');
|
||||
return this.remember(new CookieJar());
|
||||
}
|
||||
|
||||
const raw = fs.readFileSync(this.filePath, 'utf8');
|
||||
const parsed = JSON.parse(raw) as PlainCookieFile | EncryptedCookieFile;
|
||||
|
||||
if (this.isEncryptedCookieFile(parsed)) {
|
||||
if (!this.options.encryptor) {
|
||||
throw new Error('Cookie-Datei ist verschluesselt, aber kein Encryptor wurde konfiguriert');
|
||||
}
|
||||
|
||||
const decrypted = this.options.encryptor.decrypt(parsed.data);
|
||||
const cookieJson = JSON.parse(decrypted);
|
||||
return this.remember(this.cookieJarFromJson(cookieJson));
|
||||
}
|
||||
|
||||
return this.remember(this.cookieJarFromJson(parsed));
|
||||
} catch (error) {
|
||||
this.options.logger?.warn?.('Cookies konnten nicht geladen werden, neuer Cookie-Store wird erstellt', error);
|
||||
return this.remember(new CookieJar());
|
||||
}
|
||||
}
|
||||
|
||||
async save(cookieJar: CookieJar): Promise<void> {
|
||||
FileCookieStore.sharedJars.set(this.filePath, cookieJar);
|
||||
fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
|
||||
|
||||
const cookieJson = cookieJar.toJSON();
|
||||
|
||||
let fileContent: string;
|
||||
|
||||
if (this.options.encryptor) {
|
||||
fileContent = JSON.stringify(
|
||||
{
|
||||
encrypted: true,
|
||||
algorithm: 'aes-256-gcm',
|
||||
data: this.options.encryptor.encrypt(JSON.stringify(cookieJson)),
|
||||
updatedAt: new Date().toISOString(),
|
||||
} satisfies EncryptedCookieFile,
|
||||
null,
|
||||
2
|
||||
);
|
||||
} else {
|
||||
fileContent = JSON.stringify(cookieJson, null, 2);
|
||||
}
|
||||
|
||||
fs.writeFileSync(this.filePath, fileContent, {
|
||||
mode: 0o600,
|
||||
});
|
||||
}
|
||||
|
||||
async clear(): Promise<void> {
|
||||
FileCookieStore.sharedJars.set(this.filePath, new CookieJar());
|
||||
|
||||
if (fs.existsSync(this.filePath)) {
|
||||
fs.unlinkSync(this.filePath);
|
||||
}
|
||||
}
|
||||
|
||||
private isEncryptedCookieFile(value: unknown): value is EncryptedCookieFile {
|
||||
return (
|
||||
typeof value === 'object' &&
|
||||
value !== null &&
|
||||
(value as EncryptedCookieFile).encrypted === true &&
|
||||
(value as EncryptedCookieFile).algorithm === 'aes-256-gcm' &&
|
||||
typeof (value as EncryptedCookieFile).data === 'string'
|
||||
);
|
||||
}
|
||||
|
||||
private cookieJarFromJson(value: unknown): CookieJar {
|
||||
if (Array.isArray(value)) {
|
||||
const cookieJar = new CookieJar();
|
||||
|
||||
for (const cookieData of value) {
|
||||
const cookie = Cookie.fromJSON(cookieData);
|
||||
|
||||
if (cookie) {
|
||||
const domain = cookie.domain ?? 'admin7.agrarmonitor.de';
|
||||
const url = domain.startsWith('http') ? domain : `https://${domain}`;
|
||||
cookieJar.setCookieSync(cookie, url);
|
||||
}
|
||||
}
|
||||
|
||||
return cookieJar;
|
||||
}
|
||||
|
||||
return CookieJar.fromJSON(JSON.stringify(value));
|
||||
}
|
||||
|
||||
private remember(cookieJar: CookieJar): CookieJar {
|
||||
FileCookieStore.sharedJars.set(this.filePath, cookieJar);
|
||||
return cookieJar;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { CookieJar } from 'tough-cookie';
|
||||
import type { CookieStore } from '../types';
|
||||
|
||||
export class MemoryCookieStore implements CookieStore {
|
||||
private cookieJar = new CookieJar();
|
||||
|
||||
async load(): Promise<CookieJar> {
|
||||
return this.cookieJar;
|
||||
}
|
||||
|
||||
async save(cookieJar: CookieJar): Promise<void> {
|
||||
this.cookieJar = cookieJar;
|
||||
}
|
||||
|
||||
async clear(): Promise<void> {
|
||||
this.cookieJar = new CookieJar();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { AgrarmonitorConnector } from './AgrarmonitorConnector';
|
||||
import type { AgrarmonitorConnectorOptions, AgrarmonitorConnectorResult } from './types';
|
||||
|
||||
export async function createAgrarmonitorClient(
|
||||
options: AgrarmonitorConnectorOptions
|
||||
): Promise<AgrarmonitorConnectorResult> {
|
||||
const connector = new AgrarmonitorConnector(options);
|
||||
await connector.init();
|
||||
return connector;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import * as crypto from 'crypto';
|
||||
import type { CookieEncryptor } from '../types';
|
||||
|
||||
export class AesGcmCookieEncryptor implements CookieEncryptor {
|
||||
private readonly key: Buffer;
|
||||
|
||||
constructor(secret: string) {
|
||||
if (!secret || secret.trim().length < 16) {
|
||||
throw new Error('Cookie encryption secret muss mindestens 16 Zeichen lang sein');
|
||||
}
|
||||
|
||||
this.key = crypto.createHash('sha256').update(secret).digest();
|
||||
}
|
||||
|
||||
encrypt(text: string): string {
|
||||
const iv = crypto.randomBytes(12);
|
||||
const cipher = crypto.createCipheriv('aes-256-gcm', this.key, iv);
|
||||
|
||||
let encrypted = cipher.update(text, 'utf8', 'hex');
|
||||
encrypted += cipher.final('hex');
|
||||
|
||||
const authTag = cipher.getAuthTag();
|
||||
|
||||
return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`;
|
||||
}
|
||||
|
||||
decrypt(encryptedText: string): string {
|
||||
const [ivHex, authTagHex, encrypted] = encryptedText.split(':');
|
||||
|
||||
if (!ivHex || !authTagHex || !encrypted) {
|
||||
throw new Error('Ungueltiges verschluesseltes Cookie-Format');
|
||||
}
|
||||
|
||||
const iv = Buffer.from(ivHex, 'hex');
|
||||
const authTag = Buffer.from(authTagHex, 'hex');
|
||||
|
||||
const decipher = crypto.createDecipheriv('aes-256-gcm', this.key, iv);
|
||||
decipher.setAuthTag(authTag);
|
||||
|
||||
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
||||
decrypted += decipher.final('utf8');
|
||||
|
||||
return decrypted;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
export { AesGcmCookieEncryptor } from './crypto/AesGcmCookieEncryptor';
|
||||
export { FileCookieStore } from './cookie-store/FileCookieStore';
|
||||
export { MemoryCookieStore } from './cookie-store/MemoryCookieStore';
|
||||
export { AgrarmonitorConnector } from './AgrarmonitorConnector';
|
||||
export { createAgrarmonitorClient } from './createAgrarmonitorClient';
|
||||
export type {
|
||||
AgrarmonitorConnectorOptions,
|
||||
AgrarmonitorConnectorResult,
|
||||
CookieEncryptor,
|
||||
CookieStore,
|
||||
Logger,
|
||||
} from './types';
|
||||
@@ -0,0 +1,98 @@
|
||||
import type { AxiosInstance } from 'axios';
|
||||
import type { CookieJar } from 'tough-cookie';
|
||||
|
||||
export interface Logger {
|
||||
debug?(message: string, meta?: unknown): void;
|
||||
info?(message: string, meta?: unknown): void;
|
||||
warn?(message: string, meta?: unknown): void;
|
||||
error?(message: string, meta?: unknown): void;
|
||||
}
|
||||
|
||||
export interface CookieEncryptor {
|
||||
encrypt(text: string): string;
|
||||
decrypt(encryptedText: string): string;
|
||||
}
|
||||
|
||||
export interface CookieStore {
|
||||
load(): Promise<CookieJar>;
|
||||
save(cookieJar: CookieJar): Promise<void>;
|
||||
clear(): Promise<void>;
|
||||
}
|
||||
|
||||
export type AgrarmonitorLoginStrategy = 'auto' | 'auth' | 'legacy';
|
||||
|
||||
export interface AgrarmonitorConnectorOptions {
|
||||
baseUrl?: string;
|
||||
apiBaseUrl?: string;
|
||||
apiToken?: string;
|
||||
username: string;
|
||||
password: string;
|
||||
cookieStore: CookieStore;
|
||||
timeoutMs?: number;
|
||||
autoLogin?: boolean;
|
||||
autoRetry?: boolean;
|
||||
loginStrategy?: AgrarmonitorLoginStrategy;
|
||||
logger?: Logger;
|
||||
}
|
||||
|
||||
export interface AgrarmonitorConnectorResult {
|
||||
http: AxiosInstance;
|
||||
login(): Promise<void>;
|
||||
clearSession(): Promise<void>;
|
||||
saveSession(): Promise<void>;
|
||||
getCookieCount(url?: string): Promise<number>;
|
||||
checkFreigeschaltet(): Promise<AgrarmonitorFreischaltungStatus>;
|
||||
checkRegistriert(): Promise<AgrarmonitorRegistrierungStatus>;
|
||||
registerDevice(options: AgrarmonitorDeviceRegistrationOptions): Promise<AgrarmonitorDeviceRegistrationResult>;
|
||||
fetchCustomers(options?: AgrarmonitorFetchCustomersOptions): Promise<AgrarmonitorApiCustomer[]>;
|
||||
}
|
||||
|
||||
export interface AgrarmonitorFreischaltungStatus {
|
||||
freigeschaltet: boolean;
|
||||
status: number;
|
||||
redirected: boolean;
|
||||
redirectLocation: string | null;
|
||||
timestamp: string;
|
||||
cookies: number;
|
||||
}
|
||||
|
||||
export interface AgrarmonitorRegistrierungStatus {
|
||||
registriert: boolean;
|
||||
status: number;
|
||||
hasRegistrationText: boolean;
|
||||
timestamp: string;
|
||||
cookies: number;
|
||||
}
|
||||
|
||||
export interface AgrarmonitorDeviceRegistrationOptions {
|
||||
agrarmonitorId: string;
|
||||
pcName: string;
|
||||
}
|
||||
|
||||
export interface AgrarmonitorDeviceRegistrationResult {
|
||||
success: boolean;
|
||||
status: number;
|
||||
message: string;
|
||||
data: {
|
||||
agrarmonitorId: string;
|
||||
pcName: string;
|
||||
nonce: string;
|
||||
};
|
||||
timestamp: string;
|
||||
cookies: number;
|
||||
}
|
||||
|
||||
export interface AgrarmonitorFetchCustomersOptions {
|
||||
perPage?: number;
|
||||
apiToken?: string;
|
||||
}
|
||||
|
||||
export interface AgrarmonitorApiCustomer {
|
||||
id: string | number;
|
||||
vorname?: string;
|
||||
nachname?: string;
|
||||
firma?: string;
|
||||
ist_aktiv?: number | boolean;
|
||||
bearbeitet_am?: string | number;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
Reference in New Issue
Block a user