Freigabe #4

Merged
bjoernpoettker merged 23 commits from Freigabe into main 2026-06-16 14:49:23 +00:00
3 changed files with 34 additions and 16 deletions
Showing only changes of commit 184ac3f5cc - Show all commits
+6
View File
@@ -61,3 +61,9 @@ AGRARMONITOR_COOKIE_PATH=./data/agrarmonitor-cookies.json
AGRARMONITOR_ENCRYPTION_KEY= # optional, 16+ Zeichen für Cookie-Verschlüsselung
AGRARMONITOR_POLLING_CRON=0 */30 * * * * # Polling-Intervall (Standard: alle 30 Minuten); leer lassen zum Deaktivieren
AGRARMONITOR_UPLOAD_CHECK_CRON=0 * * * * * # Upload-Check-Intervall (Standard: einmal pro Minute); leer lassen zum Deaktivieren
# --- Täglicher Digest ---
# Basis-URL der App für klickbare Links in Digest-E-Mails (z.B. https://paperless.example.com)
# Leer lassen: E-Mails werden ohne Links versendet
APP_URL=
DAILY_DIGEST_CRON= # Standard: 0 7 * * * (täglich 07:00 Uhr)
+2
View File
@@ -47,6 +47,8 @@ services:
- AGRARMONITOR_ENCRYPTION_KEY=${AGRARMONITOR_ENCRYPTION_KEY:-}
- AGRARMONITOR_POLLING_CRON=${AGRARMONITOR_POLLING_CRON:-}
- AGRARMONITOR_UPLOAD_CHECK_CRON=${AGRARMONITOR_UPLOAD_CHECK_CRON:-}
- APP_URL=${APP_URL:-}
- DAILY_DIGEST_CRON=${DAILY_DIGEST_CRON:-}
volumes:
- /mnt/scans:/mnt/scans
- /mnt/paperlessmanager:/mnt/data
@@ -1,4 +1,5 @@
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Cron } from '@nestjs/schedule';
import { StatsService, DashboardCounts } from '../stats/stats.service';
import { UserSettingsService } from '../user-settings/user-settings.service';
@@ -7,17 +8,23 @@ import { MailService } from '../postprocessing/mail.service';
@Injectable()
export class DailyDigestService {
private readonly logger = new Logger(DailyDigestService.name);
private readonly appUrl: string;
private readonly agrarmonitorBaseUrl: string;
constructor(
private readonly statsService: StatsService,
private readonly userSettingsService: UserSettingsService,
private readonly mailService: MailService,
) {}
private readonly configService: ConfigService,
) {
this.appUrl = this.configService.get<string>('APP_URL', '');
this.agrarmonitorBaseUrl = this.configService.get<string>('AGRARMONITOR_BASE_URL', '');
}
async sendDigestForUser(userId: string, email: string, preferredUsername?: string) {
const today = new Date().toLocaleDateString('de-DE', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
const counts = await this.statsService.getDashboardCounts(preferredUsername);
const html = buildDigestHtml(counts, today);
const html = buildDigestHtml(counts, today, this.appUrl, this.agrarmonitorBaseUrl);
const plainText = buildDigestPlainText(counts, today);
await this.mailService.sendMail({
to: email,
@@ -43,7 +50,7 @@ export class DailyDigestService {
for (const sub of subscribers) {
try {
const counts = await this.statsService.getDashboardCounts(sub.UserPreferredUsername ?? undefined);
const html = buildDigestHtml(counts, today);
const html = buildDigestHtml(counts, today, this.appUrl, this.agrarmonitorBaseUrl);
const plainText = buildDigestPlainText(counts, today);
await this.mailService.sendMail({
to: sub.UserEmail!,
@@ -65,23 +72,26 @@ function countColor(n: number): string {
return '#dc2626';
}
function buildDigestHtml(counts: DashboardCounts, today: string): string {
const rows: { label: string; count: number }[] = [
{ label: 'Eingangsbox (Scanner)', count: counts.inbox },
{ label: 'Posteingang', count: counts.posteingang },
{ label: 'Manuell bearbeiten', count: counts.manuell },
{ label: 'Mailpostfach', count: counts.mailpostfach },
{ label: 'In Agrarmonitor', count: counts.agrarmonitor },
function buildDigestHtml(counts: DashboardCounts, today: string, appUrl: string, agrarmonitorBaseUrl: string): string {
const rows: { label: string; count: number; url: string }[] = [
{ label: 'Eingangsbox (Scanner)', count: counts.inbox, url: appUrl ? `${appUrl}/inbox` : '' },
{ label: 'Posteingang', count: counts.posteingang, url: appUrl ? `${appUrl}/posteingang` : '' },
{ label: 'Manuell bearbeiten', count: counts.manuell, url: appUrl ? `${appUrl}/manuell` : '' },
{ label: 'Mailpostfach', count: counts.mailpostfach, url: appUrl ? `${appUrl}/mailpostfach` : '' },
{ label: 'In Agrarmonitor', count: counts.agrarmonitor, url: agrarmonitorBaseUrl ? `${agrarmonitorBaseUrl}/dateien/eingang#dateien` : '' },
];
const tableRows = rows
.map(
r => `
.map(r => {
const labelCell = r.url
? `<a href="${r.url}" style="color:#1d4ed8;text-decoration:none;">${r.label}</a>`
: r.label;
return `
<tr>
<td style="padding:10px 16px;border-bottom:1px solid #e5e7eb;font-family:sans-serif;font-size:14px;color:#374151;">${r.label}</td>
<td style="padding:10px 16px;border-bottom:1px solid #e5e7eb;text-align:center;font-family:sans-serif;font-size:16px;font-weight:bold;color:${countColor(r.count)};">${r.count}</td>
</tr>`,
)
<td style="padding:10px 16px;border-bottom:1px solid #e5e7eb;font-family:sans-serif;font-size:14px;color:#374151;">${labelCell}</td>
<td style="padding:10px 16px;border-bottom:1px solid #e5e7eb;text-align:center;font-family:sans-serif;font-size:16px;font-weight:bold;color:${countColor(r.count)};">${r.url ? `<a href="${r.url}" style="color:${countColor(r.count)};text-decoration:none;">${r.count}</a>` : r.count}</td>
</tr>`;
})
.join('');
return `<!DOCTYPE html>