feat(agrarmonitor): Cookies in den Einstellungen zurücksetzen
Build and Push Multi-Platform Images / build-and-push (push) Successful in 42s

Neuer Button „Cookies zurücksetzen“ neben „Status abrufen“. Er löscht
die gespeicherte Agrarmonitor-Session samt Cookie-Datei; beim nächsten
Zugriff meldet sich der Manager neu an.

Eine laufende Connector-Instanz wird über clearSession() zurückgesetzt
und verworfen, damit sie ihren alten Cookie-Jar nicht wieder in die
Datei schreibt. Während einer Lieferantennummern-Vergabe ist das
Zurücksetzen gesperrt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bjoernpoettkerandClaude Opus 5.5 committed 2026-09-28 09:53:47 +02:00
1 parent 549948913a
commit 0ff78707d1
5 files changed
+133 -18

No files matched your search

@@ -1,6 +1,7 @@
import {
BadRequestException,
Body,
ConflictException,
Controller,
Get,
HttpCode,
@@ -36,6 +37,18 @@ export class AgrarmonitorController {
return this.service.registerDevice(body.pcName, body.agrarmonitorId);
}
@Post('reset-cookies')
@HttpCode(200)
@RequirePermissions(Permission.MANAGE_SETTINGS)
async resetCookies() {
if (this.lieferantennummernService.getStatus().aktiv) {
throw new ConflictException(
'Während der Lieferantennummern-Vergabe können die Cookies nicht gelöscht werden.',
);
}
return this.service.resetCookies();
}
@Get('polling-config')
@RequirePermissions(Permission.MANAGE_SETTINGS)
async getPollingConfig() {
@@ -0,0 +1,44 @@
import type { ConfigService } from '@nestjs/config';
import type { AgrarmonitorConnectorResult } from 'agrarmonitor-connector';
import { AgrarmonitorService } from './agrarmonitor.service';
const storeClear = jest.fn(() => Promise.resolve());
// Der echte Connector lädt jsdom (ESM), das Jest nicht verarbeiten kann.
jest.mock('agrarmonitor-connector', () => ({
FileCookieStore: jest.fn(() => ({ clear: storeClear })),
AesGcmCookieEncryptor: jest.fn(),
createAgrarmonitorClient: jest.fn(),
}));
function service(): AgrarmonitorService {
const config = {
get: (_key: string, fallback?: string) => fallback,
} as unknown as ConfigService;
return new AgrarmonitorService(config);
}
describe('AgrarmonitorService.resetCookies', () => {
beforeEach(() => storeClear.mockClear());
it('setzt über die laufende Instanz zurück und verwirft sie', async () => {
const s = service();
const clearSession = jest.fn(() => Promise.resolve());
(s as unknown as { client: Partial<AgrarmonitorConnectorResult> }).client =
{ clearSession };
const result = await s.resetCookies();
expect(result.success).toBe(true);
expect(clearSession).toHaveBeenCalledTimes(1);
expect(storeClear).not.toHaveBeenCalled();
expect((s as unknown as { client: unknown }).client).toBeNull();
});
it('löscht ohne Instanz direkt die Cookie-Datei, ohne sich anzumelden', async () => {
const result = await service().resetCookies();
expect(result.success).toBe(true);
expect(storeClear).toHaveBeenCalledTimes(1);
});
});
@@ -46,21 +46,7 @@ export class AgrarmonitorService {
'https://api.agrarmonitor.de',
);
const apiToken = this.configService.get<string>('AGRARMONITOR_API_TOKEN');
const cookiePath = this.configService.get<string>(
'AGRARMONITOR_COOKIE_PATH',
'./data/agrarmonitor-cookies.json',
);
const encryptionKey = this.configService.get<string>(
'AGRARMONITOR_ENCRYPTION_KEY',
);
const encryptor = encryptionKey
? new AesGcmCookieEncryptor(encryptionKey)
: undefined;
const cookieStore = new FileCookieStore(cookiePath, {
encryptor,
logger: this.logger,
});
const cookieStore = this.createCookieStore();
this.client = await createAgrarmonitorClient({
baseUrl,
@@ -79,10 +65,45 @@ export class AgrarmonitorService {
return this.client;
}
private createCookieStore(): FileCookieStore {
const cookiePath = this.configService.get<string>(
'AGRARMONITOR_COOKIE_PATH',
'./data/agrarmonitor-cookies.json',
);
const encryptionKey = this.configService.get<string>(
'AGRARMONITOR_ENCRYPTION_KEY',
);
const encryptor = encryptionKey
? new AesGcmCookieEncryptor(encryptionKey)
: undefined;
return new FileCookieStore(cookiePath, {
encryptor,
logger: this.logger,
});
}
clearClient(): void {
this.client = null;
}
/**
* Löscht alle gespeicherten Agrarmonitor-Cookies (Session). Der nächste
* Zugriff meldet sich neu an.
*/
async resetCookies(): Promise<AgrarmonitorRegisterResultDto> {
const client = this.client;
this.client = null;
// Über die laufende Instanz zurücksetzen – sonst schreibt sie ihren alten
// Cookie-Jar beim nächsten Request wieder in die Datei.
if (client) await client.clearSession();
else await this.createCookieStore().clear();
this.logger.warn('Agrarmonitor-Cookies wurden zurückgesetzt');
return {
success: true,
message: 'Cookies gelöscht – beim nächsten Zugriff wird neu angemeldet.',
};
}
async getStatus(): Promise<AgrarmonitorStatusDto> {
try {
const client = await this.getClient();