Initial Agrarmonitor connector
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
data/
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
.env
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# Agrarmonitor Connector
|
||||||
|
|
||||||
|
TypeScript MVP connector for Agrarmonitor with shared cookie persistence, optional AES-GCM cookie encryption, automatic login, retry after expired sessions, device registration checks, and customer detail extraction.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import {
|
||||||
|
AesGcmCookieEncryptor,
|
||||||
|
FileCookieStore,
|
||||||
|
createAgrarmonitorClient,
|
||||||
|
} from 'agrarmonitor-connector';
|
||||||
|
|
||||||
|
const agrarmonitor = await createAgrarmonitorClient({
|
||||||
|
baseUrl: 'https://admin7.agrarmonitor.de',
|
||||||
|
apiToken: process.env.AGRARMONITOR_API_TOKEN,
|
||||||
|
username: process.env.AGRARMONITOR_USERNAME ?? '',
|
||||||
|
password: process.env.AGRARMONITOR_PASSWORD ?? '',
|
||||||
|
cookieStore: new FileCookieStore(
|
||||||
|
process.env.AGRARMONITOR_COOKIE_PATH ?? './data/agrarmonitor-cookies.json',
|
||||||
|
{
|
||||||
|
encryptor: process.env.AGRARMONITOR_ENCRYPTION_KEY
|
||||||
|
? new AesGcmCookieEncryptor(process.env.AGRARMONITOR_ENCRYPTION_KEY)
|
||||||
|
: undefined,
|
||||||
|
logger: console,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
logger: console,
|
||||||
|
});
|
||||||
|
|
||||||
|
const freischaltung = await agrarmonitor.checkFreigeschaltet();
|
||||||
|
console.log(freischaltung.freigeschaltet);
|
||||||
|
|
||||||
|
const registrierung = await agrarmonitor.checkRegistriert();
|
||||||
|
console.log(registrierung.registriert);
|
||||||
|
|
||||||
|
const response = await agrarmonitor.http.get('/kunden/detail/123');
|
||||||
|
console.log(response.status);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cookie Persistence
|
||||||
|
|
||||||
|
`FileCookieStore` keeps one shared `CookieJar` per file path inside the Node process. Multiple connector instances that use the same cookie file therefore reuse the same session and every successful request saves the latest cookies back to disk.
|
||||||
|
|
||||||
|
The store can read both the connector format and the older Telefonbuch cookie-array format.
|
||||||
|
|
||||||
|
## Useful Methods
|
||||||
|
|
||||||
|
- `checkFreigeschaltet()` checks whether Agrarmonitor redirects to `/freischaltung/`.
|
||||||
|
- `checkRegistriert()` checks whether the page still contains `Neues Gerät registrieren`.
|
||||||
|
- `registerDevice({ agrarmonitorId, pcName })` loads `/freischaltung/`, extracts the nonce, and posts the registration request.
|
||||||
|
- `fetchCustomers()` loads customers from `https://api.agrarmonitor.de/v1/kunden`.
|
||||||
|
- `saveSession()` explicitly persists the current cookie jar.
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import {
|
||||||
|
AesGcmCookieEncryptor,
|
||||||
|
FileCookieStore,
|
||||||
|
createAgrarmonitorClient,
|
||||||
|
} from '../src';
|
||||||
|
|
||||||
|
async function main(): Promise<void> {
|
||||||
|
const agrarmonitor = await createAgrarmonitorClient({
|
||||||
|
baseUrl: 'https://admin7.agrarmonitor.de',
|
||||||
|
apiToken: process.env.AGRARMONITOR_API_TOKEN,
|
||||||
|
username: process.env.AGRARMONITOR_USERNAME ?? '',
|
||||||
|
password: process.env.AGRARMONITOR_PASSWORD ?? '',
|
||||||
|
cookieStore: new FileCookieStore(
|
||||||
|
process.env.AGRARMONITOR_COOKIE_PATH ?? './data/agrarmonitor-cookies.json',
|
||||||
|
{
|
||||||
|
encryptor: process.env.AGRARMONITOR_ENCRYPTION_KEY
|
||||||
|
? new AesGcmCookieEncryptor(process.env.AGRARMONITOR_ENCRYPTION_KEY)
|
||||||
|
: undefined,
|
||||||
|
logger: console,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
logger: console,
|
||||||
|
});
|
||||||
|
|
||||||
|
const freischaltung = await agrarmonitor.checkFreigeschaltet();
|
||||||
|
console.log('Freigeschaltet:', freischaltung.freigeschaltet);
|
||||||
|
|
||||||
|
const registrierung = await agrarmonitor.checkRegistriert();
|
||||||
|
console.log('Registriert:', registrierung.registriert);
|
||||||
|
|
||||||
|
const response = await agrarmonitor.http.get('/kunden/detail/123');
|
||||||
|
console.log(response.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main();
|
||||||
Generated
+989
@@ -0,0 +1,989 @@
|
|||||||
|
{
|
||||||
|
"name": "agrarmonitor-connector",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "agrarmonitor-connector",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.7.9",
|
||||||
|
"axios-cookiejar-support": "^5.0.5",
|
||||||
|
"jsdom": "^29.1.1",
|
||||||
|
"tough-cookie": "^4.1.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/jsdom": "^28.0.3",
|
||||||
|
"@types/node": "^22.10.2",
|
||||||
|
"@types/tough-cookie": "^4.0.5",
|
||||||
|
"typescript": "^5.7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@asamuzakjp/css-color": {
|
||||||
|
"version": "5.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz",
|
||||||
|
"integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@asamuzakjp/generational-cache": "^1.0.1",
|
||||||
|
"@csstools/css-calc": "^3.2.0",
|
||||||
|
"@csstools/css-color-parser": "^4.1.0",
|
||||||
|
"@csstools/css-parser-algorithms": "^4.0.0",
|
||||||
|
"@csstools/css-tokenizer": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@asamuzakjp/dom-selector": {
|
||||||
|
"version": "7.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz",
|
||||||
|
"integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@asamuzakjp/generational-cache": "^1.0.1",
|
||||||
|
"@asamuzakjp/nwsapi": "^2.3.9",
|
||||||
|
"bidi-js": "^1.0.3",
|
||||||
|
"css-tree": "^3.2.1",
|
||||||
|
"is-potential-custom-element-name": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@asamuzakjp/generational-cache": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@asamuzakjp/nwsapi": {
|
||||||
|
"version": "2.3.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz",
|
||||||
|
"integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@bramus/specificity": {
|
||||||
|
"version": "2.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz",
|
||||||
|
"integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"css-tree": "^3.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"specificity": "bin/cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@csstools/color-helpers": {
|
||||||
|
"version": "6.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/csstools"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/csstools"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT-0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.19.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@csstools/css-calc": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/csstools"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/csstools"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.19.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@csstools/css-parser-algorithms": "^4.0.0",
|
||||||
|
"@csstools/css-tokenizer": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@csstools/css-color-parser": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/csstools"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/csstools"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@csstools/color-helpers": "^6.0.2",
|
||||||
|
"@csstools/css-calc": "^3.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.19.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@csstools/css-parser-algorithms": "^4.0.0",
|
||||||
|
"@csstools/css-tokenizer": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@csstools/css-parser-algorithms": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/csstools"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/csstools"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.19.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@csstools/css-tokenizer": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@csstools/css-syntax-patches-for-csstree": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-wgsqt92b7C7tQhIdPNxj0n9zuUbQlvAuI1exyzeNrOKOi62SD7ren8zqszmpVREjAOqg8cD2FqYhQfAuKjk4sw==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/csstools"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/csstools"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT-0",
|
||||||
|
"peerDependencies": {
|
||||||
|
"css-tree": "^3.2.1"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"css-tree": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@csstools/css-tokenizer": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/csstools"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/csstools"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.19.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@exodus/bytes": {
|
||||||
|
"version": "1.15.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz",
|
||||||
|
"integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@noble/hashes": "^1.8.0 || ^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@noble/hashes": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/jsdom": {
|
||||||
|
"version": "28.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-28.0.3.tgz",
|
||||||
|
"integrity": "sha512-/HQ2uFoetFTXuye8vzIcHw2z6Fwi7Hi/qcgC+RoS9NCyewiqxhVGqlG+ViGB6lkax481R6dmhf1I7lIGlzJStQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"@types/tough-cookie": "*",
|
||||||
|
"parse5": "^8.0.0",
|
||||||
|
"undici-types": "^7.21.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/jsdom/node_modules/undici-types": {
|
||||||
|
"version": "7.25.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.25.0.tgz",
|
||||||
|
"integrity": "sha512-AXNgS1Byr27fTI+2bsPEkV9CxkT8H6xNyRI68b3TatlZo3RkzlqQBLL+w7SmGPVpokjHbcuNVQUWE7FRTg+LRA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "22.19.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.18.tgz",
|
||||||
|
"integrity": "sha512-9v00a+dn2yWVsYDEunWC4g/TcRKVq3r8N5FuZp7u0SGrPvdN9c2yXI9bBuf5Fl0hNCb+QTIePTn5pJs2pwBOQQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~6.21.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/tough-cookie": {
|
||||||
|
"version": "4.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",
|
||||||
|
"integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/agent-base": {
|
||||||
|
"version": "7.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
|
||||||
|
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/asynckit": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/axios": {
|
||||||
|
"version": "1.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz",
|
||||||
|
"integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.16.0",
|
||||||
|
"form-data": "^4.0.5",
|
||||||
|
"proxy-from-env": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/axios-cookiejar-support": {
|
||||||
|
"version": "5.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios-cookiejar-support/-/axios-cookiejar-support-5.0.5.tgz",
|
||||||
|
"integrity": "sha512-jJG+p7JnOYxkVrYkCDKBrLqUmcpwHZTNQrEcIEKr5qe7YVTyPAD9nCsi1cO5LDmQpQApfS430czO+oceI3g/3g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"http-cookie-agent": "^6.0.8"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/3846masa"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"axios": ">=0.20.0",
|
||||||
|
"tough-cookie": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/bidi-js": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"require-from-string": "^2.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/call-bind-apply-helpers": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"function-bind": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/combined-stream": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"delayed-stream": "~1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/css-tree": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mdn-data": "2.27.1",
|
||||||
|
"source-map-js": "^1.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/data-urls": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-mimetype": "^5.0.0",
|
||||||
|
"whatwg-url": "^16.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/decimal.js": {
|
||||||
|
"version": "10.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
|
||||||
|
"integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/delayed-stream": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dunder-proto": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind-apply-helpers": "^1.0.1",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"gopd": "^1.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/entities": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.19.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/es-define-property": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/es-errors": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/es-object-atoms": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/es-set-tostringtag": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"get-intrinsic": "^1.2.6",
|
||||||
|
"has-tostringtag": "^1.0.2",
|
||||||
|
"hasown": "^2.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/follow-redirects": {
|
||||||
|
"version": "1.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
|
||||||
|
"integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"debug": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/form-data": {
|
||||||
|
"version": "4.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
|
||||||
|
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"es-set-tostringtag": "^2.1.0",
|
||||||
|
"hasown": "^2.0.2",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/function-bind": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-intrinsic": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind-apply-helpers": "^1.0.2",
|
||||||
|
"es-define-property": "^1.0.1",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"es-object-atoms": "^1.1.1",
|
||||||
|
"function-bind": "^1.1.2",
|
||||||
|
"get-proto": "^1.0.1",
|
||||||
|
"gopd": "^1.2.0",
|
||||||
|
"has-symbols": "^1.1.0",
|
||||||
|
"hasown": "^2.0.2",
|
||||||
|
"math-intrinsics": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-proto": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"dunder-proto": "^1.0.1",
|
||||||
|
"es-object-atoms": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/gopd": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-symbols": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-tostringtag": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"has-symbols": "^1.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/hasown": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"function-bind": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/html-encoding-sniffer": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@exodus/bytes": "^1.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/http-cookie-agent": {
|
||||||
|
"version": "6.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-cookie-agent/-/http-cookie-agent-6.0.8.tgz",
|
||||||
|
"integrity": "sha512-qnYh3yLSr2jBsTYkw11elq+T361uKAJaZ2dR4cfYZChw1dt9uL5t3zSUwehoqqVb4oldk1BpkXKm2oat8zV+oA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"agent-base": "^7.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/3846masa"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"tough-cookie": "^4.0.0 || ^5.0.0",
|
||||||
|
"undici": "^5.11.0 || ^6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"undici": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-potential-custom-element-name": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/jsdom": {
|
||||||
|
"version": "29.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz",
|
||||||
|
"integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@asamuzakjp/css-color": "^5.1.11",
|
||||||
|
"@asamuzakjp/dom-selector": "^7.1.1",
|
||||||
|
"@bramus/specificity": "^2.4.2",
|
||||||
|
"@csstools/css-syntax-patches-for-csstree": "^1.1.3",
|
||||||
|
"@exodus/bytes": "^1.15.0",
|
||||||
|
"css-tree": "^3.2.1",
|
||||||
|
"data-urls": "^7.0.0",
|
||||||
|
"decimal.js": "^10.6.0",
|
||||||
|
"html-encoding-sniffer": "^6.0.0",
|
||||||
|
"is-potential-custom-element-name": "^1.0.1",
|
||||||
|
"lru-cache": "^11.3.5",
|
||||||
|
"parse5": "^8.0.1",
|
||||||
|
"saxes": "^6.0.0",
|
||||||
|
"symbol-tree": "^3.2.4",
|
||||||
|
"tough-cookie": "^6.0.1",
|
||||||
|
"undici": "^7.25.0",
|
||||||
|
"w3c-xmlserializer": "^5.0.0",
|
||||||
|
"webidl-conversions": "^8.0.1",
|
||||||
|
"whatwg-mimetype": "^5.0.0",
|
||||||
|
"whatwg-url": "^16.0.1",
|
||||||
|
"xml-name-validator": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || ^22.13.0 || >=24.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"canvas": "^3.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"canvas": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jsdom/node_modules/tough-cookie": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"tldts": "^7.0.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jsdom/node_modules/undici": {
|
||||||
|
"version": "7.25.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz",
|
||||||
|
"integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.18.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lru-cache": {
|
||||||
|
"version": "11.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.0.tgz",
|
||||||
|
"integrity": "sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==",
|
||||||
|
"license": "BlueOak-1.0.0",
|
||||||
|
"engines": {
|
||||||
|
"node": "20 || >=22"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/math-intrinsics": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mdn-data": {
|
||||||
|
"version": "2.27.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz",
|
||||||
|
"integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==",
|
||||||
|
"license": "CC0-1.0"
|
||||||
|
},
|
||||||
|
"node_modules/mime-db": {
|
||||||
|
"version": "1.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-types": {
|
||||||
|
"version": "2.1.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "1.52.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/parse5": {
|
||||||
|
"version": "8.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz",
|
||||||
|
"integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"entities": "^8.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/proxy-from-env": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/psl": {
|
||||||
|
"version": "1.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
|
||||||
|
"integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": "^2.3.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/lupomontero"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/punycode": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/querystringify": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/require-from-string": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/requires-port": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/saxes": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"xmlchars": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=v12.22.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/source-map-js": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/symbol-tree": {
|
||||||
|
"version": "3.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
||||||
|
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/tldts": {
|
||||||
|
"version": "7.0.30",
|
||||||
|
"resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.30.tgz",
|
||||||
|
"integrity": "sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tldts-core": "^7.0.30"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"tldts": "bin/cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tldts-core": {
|
||||||
|
"version": "7.0.30",
|
||||||
|
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.30.tgz",
|
||||||
|
"integrity": "sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/tough-cookie": {
|
||||||
|
"version": "4.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
|
||||||
|
"integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"psl": "^1.1.33",
|
||||||
|
"punycode": "^2.1.1",
|
||||||
|
"universalify": "^0.2.0",
|
||||||
|
"url-parse": "^1.5.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": "^2.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "5.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici": {
|
||||||
|
"version": "6.25.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici/-/undici-6.25.0.tgz",
|
||||||
|
"integrity": "sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "6.21.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||||
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/universalify": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/url-parse": {
|
||||||
|
"version": "1.5.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||||
|
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"querystringify": "^2.1.1",
|
||||||
|
"requires-port": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/w3c-xmlserializer": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"xml-name-validator": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/webidl-conversions": {
|
||||||
|
"version": "8.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz",
|
||||||
|
"integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-mimetype": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "16.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz",
|
||||||
|
"integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@exodus/bytes": "^1.11.0",
|
||||||
|
"tr46": "^6.0.0",
|
||||||
|
"webidl-conversions": "^8.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xml-name-validator": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xmlchars": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "agrarmonitor-connector",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "TypeScript connector MVP for Agrarmonitor with cookie persistence.",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc -p tsconfig.json",
|
||||||
|
"clean": "rm -rf dist",
|
||||||
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"agrarmonitor",
|
||||||
|
"connector",
|
||||||
|
"typescript"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.7.9",
|
||||||
|
"axios-cookiejar-support": "^5.0.5",
|
||||||
|
"jsdom": "^29.1.1",
|
||||||
|
"tough-cookie": "^4.1.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/jsdom": "^28.0.3",
|
||||||
|
"@types/node": "^22.10.2",
|
||||||
|
"@types/tough-cookie": "^4.0.5",
|
||||||
|
"typescript": "^5.7.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "CommonJS",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"lib": [
|
||||||
|
"ES2022",
|
||||||
|
"DOM"
|
||||||
|
],
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"dist",
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user