feat: add Agrarmonitor integration module
- New backend module (agrarmonitor) with status check and device registration - Frontend settings tab with connection status display and registration form - Environment variables for base URLs, credentials, cookie path and encryption key - Docker Compose env passthrough for agrarmonitor config Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import { Body, Controller, Get, HttpCode, Post } from '@nestjs/common';
|
||||
import { AgrarmonitorService } from './agrarmonitor.service';
|
||||
import { RequirePermissions } from '../auth/permissions.decorator';
|
||||
import { Permission } from '../auth/permissions.enum';
|
||||
|
||||
@Controller('api/agrarmonitor')
|
||||
export class AgrarmonitorController {
|
||||
constructor(private readonly service: AgrarmonitorService) {}
|
||||
|
||||
@Get('status')
|
||||
@RequirePermissions(Permission.MANAGE_SETTINGS)
|
||||
async getStatus() {
|
||||
return this.service.getStatus();
|
||||
}
|
||||
|
||||
@Post('register')
|
||||
@HttpCode(200)
|
||||
@RequirePermissions(Permission.MANAGE_SETTINGS)
|
||||
async registerDevice(@Body() body: { pcName: string; agrarmonitorId: string }) {
|
||||
return this.service.registerDevice(body.pcName, body.agrarmonitorId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AgrarmonitorService } from './agrarmonitor.service';
|
||||
import { AgrarmonitorController } from './agrarmonitor.controller';
|
||||
|
||||
@Module({
|
||||
providers: [AgrarmonitorService],
|
||||
controllers: [AgrarmonitorController],
|
||||
exports: [AgrarmonitorService],
|
||||
})
|
||||
export class AgrarmonitorModule {}
|
||||
@@ -0,0 +1,86 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import {
|
||||
createAgrarmonitorClient,
|
||||
FileCookieStore,
|
||||
AesGcmCookieEncryptor,
|
||||
type AgrarmonitorConnectorResult,
|
||||
} from 'agrarmonitor-connector';
|
||||
|
||||
export interface AgrarmonitorStatusDto {
|
||||
connected: boolean;
|
||||
registriert: boolean | null;
|
||||
freigeschaltet: boolean | null;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface AgrarmonitorRegisterResultDto {
|
||||
success: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AgrarmonitorService {
|
||||
private readonly logger = new Logger(AgrarmonitorService.name);
|
||||
private client: AgrarmonitorConnectorResult | null = null;
|
||||
|
||||
constructor(private readonly configService: ConfigService) {}
|
||||
|
||||
async getClient(): Promise<AgrarmonitorConnectorResult> {
|
||||
if (this.client) return this.client;
|
||||
|
||||
const username = this.configService.get<string>('AGRARMONITOR_USERNAME', '');
|
||||
const password = this.configService.get<string>('AGRARMONITOR_PASSWORD', '');
|
||||
const baseUrl = this.configService.get<string>('AGRARMONITOR_BASE_URL', 'https://admin7.agrarmonitor.de');
|
||||
const apiBaseUrl = this.configService.get<string>('AGRARMONITOR_API_BASE_URL', '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 });
|
||||
|
||||
this.client = await createAgrarmonitorClient({
|
||||
baseUrl,
|
||||
apiBaseUrl,
|
||||
apiToken,
|
||||
username,
|
||||
password,
|
||||
cookieStore,
|
||||
autoLogin: true,
|
||||
autoRetry: true,
|
||||
logger: this.logger,
|
||||
});
|
||||
|
||||
return this.client;
|
||||
}
|
||||
|
||||
async getStatus(): Promise<AgrarmonitorStatusDto> {
|
||||
try {
|
||||
const client = await this.getClient();
|
||||
const [registrierungStatus, freigeschaltetStatus] = await Promise.all([
|
||||
client.checkRegistriert(),
|
||||
client.checkFreigeschaltet(),
|
||||
]);
|
||||
return {
|
||||
connected: true,
|
||||
registriert: registrierungStatus.registriert,
|
||||
freigeschaltet: freigeschaltetStatus.freigeschaltet,
|
||||
};
|
||||
} catch (err: any) {
|
||||
this.client = null;
|
||||
return {
|
||||
connected: false,
|
||||
registriert: null,
|
||||
freigeschaltet: null,
|
||||
error: err?.message ?? 'Verbindung fehlgeschlagen',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async registerDevice(pcName: string, agrarmonitorId: string): Promise<AgrarmonitorRegisterResultDto> {
|
||||
const client = await this.getClient();
|
||||
const result = await client.registerDevice({ agrarmonitorId, pcName });
|
||||
return { success: result.success, message: result.message };
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import { BarcodeModule } from './barcode/barcode.module';
|
||||
import { InboxPostprocessorModule } from './inbox-postprocessor/inbox-postprocessor.module';
|
||||
import { UserSettingsModule } from './user-settings/user-settings.module';
|
||||
import { LabelPrintAgentModule } from './label-print-agent/label-print-agent.module';
|
||||
import { AgrarmonitorModule } from './agrarmonitor/agrarmonitor.module';
|
||||
import * as path from 'path';
|
||||
|
||||
@Module({
|
||||
@@ -47,6 +48,7 @@ import * as path from 'path';
|
||||
InboxPostprocessorModule,
|
||||
UserSettingsModule,
|
||||
LabelPrintAgentModule,
|
||||
AgrarmonitorModule,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
Reference in New Issue
Block a user