feat(agrarmonitor): Cookies in den Einstellungen zurücksetzen
Build and Push Multi-Platform Images / build-and-push (push) Successful in 42s
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:
1 parent
549948913a
commit
0ff78707d1
5 files changed
+133
-18
No files matched your search
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
BadRequestException,
|
BadRequestException,
|
||||||
Body,
|
Body,
|
||||||
|
ConflictException,
|
||||||
Controller,
|
Controller,
|
||||||
Get,
|
Get,
|
||||||
HttpCode,
|
HttpCode,
|
||||||
@@ -36,6 +37,18 @@ export class AgrarmonitorController {
|
|||||||
return this.service.registerDevice(body.pcName, body.agrarmonitorId);
|
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')
|
@Get('polling-config')
|
||||||
@RequirePermissions(Permission.MANAGE_SETTINGS)
|
@RequirePermissions(Permission.MANAGE_SETTINGS)
|
||||||
async getPollingConfig() {
|
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',
|
'https://api.agrarmonitor.de',
|
||||||
);
|
);
|
||||||
const apiToken = this.configService.get<string>('AGRARMONITOR_API_TOKEN');
|
const apiToken = this.configService.get<string>('AGRARMONITOR_API_TOKEN');
|
||||||
const cookiePath = this.configService.get<string>(
|
const cookieStore = this.createCookieStore();
|
||||||
'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,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.client = await createAgrarmonitorClient({
|
this.client = await createAgrarmonitorClient({
|
||||||
baseUrl,
|
baseUrl,
|
||||||
@@ -79,10 +65,45 @@ export class AgrarmonitorService {
|
|||||||
return this.client;
|
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 {
|
clearClient(): void {
|
||||||
this.client = null;
|
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> {
|
async getStatus(): Promise<AgrarmonitorStatusDto> {
|
||||||
try {
|
try {
|
||||||
const client = await this.getClient();
|
const client = await this.getClient();
|
||||||
|
|||||||
@@ -317,6 +317,8 @@ export const agrarmonitorApi = {
|
|||||||
api.post<SyncCorrespondentsResult>('/api/agrarmonitor/sync-correspondents').then((r) => r.data),
|
api.post<SyncCorrespondentsResult>('/api/agrarmonitor/sync-correspondents').then((r) => r.data),
|
||||||
mergeCorrespondents: (keepId: number, deleteId: number) =>
|
mergeCorrespondents: (keepId: number, deleteId: number) =>
|
||||||
api.post<{ mergedDocuments: number }>('/api/agrarmonitor/merge-correspondents', { keepId, deleteId }).then((r) => r.data),
|
api.post<{ mergedDocuments: number }>('/api/agrarmonitor/merge-correspondents', { keepId, deleteId }).then((r) => r.data),
|
||||||
|
resetCookies: () =>
|
||||||
|
api.post<{ success: boolean; message: string }>('/api/agrarmonitor/reset-cookies').then((r) => r.data),
|
||||||
getLieferantennummernStatus: () =>
|
getLieferantennummernStatus: () =>
|
||||||
api.get<LieferantennummerStatus>('/api/agrarmonitor/lieferantennummern/status').then((r) => r.data),
|
api.get<LieferantennummerStatus>('/api/agrarmonitor/lieferantennummern/status').then((r) => r.data),
|
||||||
lieferantennummernVorschau: () =>
|
lieferantennummernVorschau: () =>
|
||||||
|
|||||||
@@ -2529,6 +2529,7 @@ function AgrarmonitorTab() {
|
|||||||
const [pollingForm] = Form.useForm();
|
const [pollingForm] = Form.useForm();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [registering, setRegistering] = useState(false);
|
const [registering, setRegistering] = useState(false);
|
||||||
|
const [cookiesResetting, setCookiesResetting] = useState(false);
|
||||||
const [pollingConfigLoading, setPollingConfigLoading] = useState(false);
|
const [pollingConfigLoading, setPollingConfigLoading] = useState(false);
|
||||||
const [pollingSaving, setPollingSaving] = useState(false);
|
const [pollingSaving, setPollingSaving] = useState(false);
|
||||||
const [pollingRunning, setPollingRunning] = useState(false);
|
const [pollingRunning, setPollingRunning] = useState(false);
|
||||||
@@ -2555,6 +2556,20 @@ function AgrarmonitorTab() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleResetCookies = async () => {
|
||||||
|
setCookiesResetting(true);
|
||||||
|
try {
|
||||||
|
const result = await agrarmonitorApi.resetCookies();
|
||||||
|
setStatus(null);
|
||||||
|
setRegisterResult(null);
|
||||||
|
message.success(result.message);
|
||||||
|
} catch (err) {
|
||||||
|
message.error(fehlermeldung(err, 'Cookies konnten nicht gelöscht werden'));
|
||||||
|
} finally {
|
||||||
|
setCookiesResetting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleRegister = async () => {
|
const handleRegister = async () => {
|
||||||
const values = await form.validateFields();
|
const values = await form.validateFields();
|
||||||
setRegistering(true);
|
setRegistering(true);
|
||||||
@@ -2646,9 +2661,29 @@ function AgrarmonitorTab() {
|
|||||||
|
|
||||||
<Space direction="vertical" style={{ width: '100%' }}>
|
<Space direction="vertical" style={{ width: '100%' }}>
|
||||||
<div>
|
<div>
|
||||||
<Button loading={loading} onClick={handleLoadStatus}>
|
<Space>
|
||||||
Status abrufen
|
<Button loading={loading} onClick={handleLoadStatus}>
|
||||||
</Button>
|
Status abrufen
|
||||||
|
</Button>
|
||||||
|
<Popconfirm
|
||||||
|
title="Agrarmonitor-Cookies löschen?"
|
||||||
|
description={
|
||||||
|
<div style={{ maxWidth: 320 }}>
|
||||||
|
Die gespeicherte Agrarmonitor-Session wird verworfen. Beim nächsten Zugriff meldet sich
|
||||||
|
der Manager neu an. Möglicherweise muss das Gerät danach neu registriert bzw.
|
||||||
|
freigeschaltet werden.
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
okText="Löschen"
|
||||||
|
okButtonProps={{ danger: true }}
|
||||||
|
cancelText="Abbrechen"
|
||||||
|
onConfirm={handleResetCookies}
|
||||||
|
>
|
||||||
|
<Button danger loading={cookiesResetting}>
|
||||||
|
Cookies zurücksetzen
|
||||||
|
</Button>
|
||||||
|
</Popconfirm>
|
||||||
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{status && (
|
{status && (
|
||||||
|
|||||||
Reference in new issue
Block a user