feat: add clickable links to daily digest emails via APP_URL
Build and Push Multi-Platform Images / build-and-push (push) Successful in 34s
Build and Push Multi-Platform Images / build-and-push (push) Successful in 34s
- Read APP_URL and AGRARMONITOR_BASE_URL from config - Render dashboard entries as clickable links in HTML digest email - Add APP_URL and DAILY_DIGEST_CRON to .env.example and docker-compose.yml Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user