feat: add debug logging for SMTP configuration and detailed mail delivery status
Build and Push Multi-Platform Images / build-and-push (push) Failing after 34s
Build and Push Multi-Platform Images / build-and-push (push) Failing after 34s
This commit is contained in:
@@ -16,6 +16,7 @@ import { KontonummernModule } from './kontonummern/kontonummern.module';
|
||||
import { StatsModule } from './stats/stats.module';
|
||||
import { BarcodeModule } from './barcode/barcode.module';
|
||||
import { InboxPostprocessorModule } from './inbox-postprocessor/inbox-postprocessor.module';
|
||||
import { UserSettingsModule } from './user-settings/user-settings.module';
|
||||
import * as path from 'path';
|
||||
|
||||
@Module({
|
||||
@@ -43,6 +44,7 @@ import * as path from 'path';
|
||||
StatsModule,
|
||||
BarcodeModule,
|
||||
InboxPostprocessorModule,
|
||||
UserSettingsModule,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
InboxDocument,
|
||||
InboxPostprocessingAction,
|
||||
CorrespondentEmailMapping,
|
||||
UserSettings,
|
||||
} from './entities';
|
||||
|
||||
const entities = [
|
||||
@@ -47,6 +48,7 @@ const entities = [
|
||||
InboxDocument,
|
||||
InboxPostprocessingAction,
|
||||
CorrespondentEmailMapping,
|
||||
UserSettings,
|
||||
];
|
||||
|
||||
@Module({
|
||||
|
||||
@@ -19,3 +19,4 @@ export { BarcodeTemplate } from './barcode-template.entity';
|
||||
export { InboxDocument } from './inbox-document.entity';
|
||||
export { InboxPostprocessingAction } from './inbox-postprocessing-action.entity';
|
||||
export { CorrespondentEmailMapping } from './correspondent-email-mapping.entity';
|
||||
export { UserSettings } from './user-settings.entity';
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Entity, PrimaryColumn, Column } from 'typeorm';
|
||||
|
||||
@Entity('user_settings')
|
||||
export class UserSettings {
|
||||
@PrimaryColumn({ type: 'varchar', length: 255 })
|
||||
UserId!: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: true })
|
||||
SmtpHost!: string | null;
|
||||
|
||||
@Column({ type: 'int', nullable: true })
|
||||
SmtpPort!: number | null;
|
||||
|
||||
@Column({ type: 'boolean', default: false })
|
||||
SmtpSecure!: boolean;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: true })
|
||||
SmtpUser!: string | null;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: true })
|
||||
SmtpPass!: string | null;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: true })
|
||||
SmtpFrom!: string | null;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
MailSignatureHtml!: string | null;
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import { createReadStream } from 'fs';
|
||||
import { InboxService } from './inbox.service';
|
||||
import { InboxPostprocessorService } from '../inbox-postprocessor/inbox-postprocessor.service';
|
||||
import { BarcodeScannerService } from '../barcode/barcode-scanner.service';
|
||||
import { UserSettingsService } from '../user-settings/user-settings.service';
|
||||
import { RequirePermissions } from '../auth/permissions.decorator';
|
||||
import { Permission } from '../auth/permissions.enum';
|
||||
|
||||
@@ -28,6 +29,7 @@ export class InboxController {
|
||||
private readonly inboxService: InboxService,
|
||||
private readonly postprocessor: InboxPostprocessorService,
|
||||
private readonly barcodeScanner: BarcodeScannerService,
|
||||
private readonly userSettingsService: UserSettingsService,
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
@@ -205,6 +207,10 @@ export class InboxController {
|
||||
@Request() req: any,
|
||||
): Promise<void> {
|
||||
const preferredUsername: string | null = req.user?.preferredUsername ?? null;
|
||||
await this.inboxService.sendAsEmail(id, preferredUsername, body);
|
||||
const smtpOverride = await this.userSettingsService.getSmtpConfig(req.user.userId);
|
||||
await this.inboxService.sendAsEmail(id, preferredUsername, {
|
||||
...body,
|
||||
smtpOverride: smtpOverride ?? undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { InboxMigrationService } from './inbox-migration.service';
|
||||
import { BarcodeModule } from '../barcode/barcode.module';
|
||||
import { InboxPostprocessorModule } from '../inbox-postprocessor/inbox-postprocessor.module';
|
||||
import { PostprocessingModule } from '../postprocessing/postprocessing.module';
|
||||
import { UserSettingsModule } from '../user-settings/user-settings.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -17,6 +18,7 @@ import { PostprocessingModule } from '../postprocessing/postprocessing.module';
|
||||
BarcodeModule,
|
||||
InboxPostprocessorModule,
|
||||
PostprocessingModule,
|
||||
UserSettingsModule,
|
||||
],
|
||||
controllers: [InboxController, ClientsController],
|
||||
providers: [InboxService, InboxMigrationService],
|
||||
|
||||
@@ -274,6 +274,7 @@ export class InboxService {
|
||||
body: string;
|
||||
html?: string;
|
||||
segments: { pages: number[]; filename: string }[];
|
||||
smtpOverride?: { host: string; port: number; secure: boolean; user: string; pass: string; from: string };
|
||||
},
|
||||
): Promise<void> {
|
||||
const { doc, pdfPath } = await this.resolveDocument(id, preferredUsername);
|
||||
@@ -294,6 +295,7 @@ export class InboxService {
|
||||
body: opts.body,
|
||||
html: opts.html,
|
||||
attachments,
|
||||
smtpOverride: opts.smtpOverride,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,10 +25,27 @@ export class MailService {
|
||||
body: string;
|
||||
html?: string;
|
||||
attachments?: { filename: string; content: Buffer }[];
|
||||
smtpOverride?: { host: string; port: number; secure: boolean; user: string; pass: string; from: string };
|
||||
}): Promise<void> {
|
||||
const from = this.configService.get<string>('SMTP_FROM', 'paperless@localhost');
|
||||
let transporter = this.transporter;
|
||||
let from = this.configService.get<string>('SMTP_FROM', 'paperless@localhost');
|
||||
|
||||
await this.transporter.sendMail({
|
||||
if (options.smtpOverride) {
|
||||
const o = options.smtpOverride;
|
||||
transporter = nodemailer.createTransport({
|
||||
host: o.host,
|
||||
port: o.port,
|
||||
secure: o.secure,
|
||||
auth: { user: o.user, pass: o.pass },
|
||||
});
|
||||
from = o.from || from;
|
||||
} else {
|
||||
this.logger.debug(
|
||||
`SMTP config — host: ${this.configService.get('SMTP_HOST')} port: ${this.configService.get('SMTP_PORT')} secure: ${this.configService.get('SMTP_SECURE')} user: ${this.configService.get('SMTP_USER')} from: ${from}`,
|
||||
);
|
||||
}
|
||||
|
||||
const info = await transporter.sendMail({
|
||||
from,
|
||||
to: options.to,
|
||||
subject: options.subject,
|
||||
@@ -40,6 +57,8 @@ export class MailService {
|
||||
})),
|
||||
});
|
||||
|
||||
this.logger.log(`Mail gesendet an ${options.to}: "${options.subject}"`);
|
||||
this.logger.log(
|
||||
`Mail gesendet an ${options.to}: "${options.subject}" — messageId: ${info.messageId} accepted: ${JSON.stringify(info.accepted)} rejected: ${JSON.stringify(info.rejected)} response: ${info.response}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Body, Controller, Get, HttpCode, Post, Put, Request } from '@nestjs/common';
|
||||
import { UserSettingsService } from './user-settings.service';
|
||||
|
||||
@Controller('api/user-settings')
|
||||
export class UserSettingsController {
|
||||
constructor(private readonly userSettingsService: UserSettingsService) {}
|
||||
|
||||
@Get()
|
||||
async getSettings(@Request() req: any) {
|
||||
return this.userSettingsService.getSettings(req.user.userId);
|
||||
}
|
||||
|
||||
@Put()
|
||||
async updateSettings(@Request() req: any, @Body() body: any) {
|
||||
return this.userSettingsService.updateSettings(req.user.userId, body);
|
||||
}
|
||||
|
||||
@Post('test-smtp')
|
||||
@HttpCode(200)
|
||||
async testSmtp(@Body() body: { host: string; port: number; secure: boolean; user: string; pass: string }) {
|
||||
return this.userSettingsService.testSmtp(body);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { UserSettings } from '../database/entities/user-settings.entity';
|
||||
import { UserSettingsService } from './user-settings.service';
|
||||
import { UserSettingsController } from './user-settings.controller';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([UserSettings])],
|
||||
providers: [UserSettingsService],
|
||||
controllers: [UserSettingsController],
|
||||
exports: [UserSettingsService],
|
||||
})
|
||||
export class UserSettingsModule {}
|
||||
@@ -0,0 +1,107 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import * as nodemailer from 'nodemailer';
|
||||
import { UserSettings } from '../database/entities/user-settings.entity';
|
||||
|
||||
export interface UserSettingsDto {
|
||||
smtpHost: string | null;
|
||||
smtpPort: number | null;
|
||||
smtpSecure: boolean;
|
||||
smtpUser: string | null;
|
||||
smtpPassSet: boolean;
|
||||
smtpFrom: string | null;
|
||||
mailSignatureHtml: string | null;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UserSettingsService {
|
||||
constructor(
|
||||
@InjectRepository(UserSettings)
|
||||
private readonly repo: Repository<UserSettings>,
|
||||
) {}
|
||||
|
||||
async getSettings(userId: string): Promise<UserSettingsDto> {
|
||||
const entity = await this.repo.findOne({ where: { UserId: userId } });
|
||||
return this.toDto(entity);
|
||||
}
|
||||
|
||||
async updateSettings(
|
||||
userId: string,
|
||||
data: {
|
||||
smtpHost?: string | null;
|
||||
smtpPort?: number | null;
|
||||
smtpSecure?: boolean;
|
||||
smtpUser?: string | null;
|
||||
smtpPass?: string | null;
|
||||
smtpFrom?: string | null;
|
||||
mailSignatureHtml?: string | null;
|
||||
},
|
||||
): Promise<UserSettingsDto> {
|
||||
let entity = await this.repo.findOne({ where: { UserId: userId } });
|
||||
if (!entity) {
|
||||
entity = this.repo.create({ UserId: userId });
|
||||
}
|
||||
|
||||
if (data.smtpHost !== undefined) entity.SmtpHost = data.smtpHost;
|
||||
if (data.smtpPort !== undefined) entity.SmtpPort = data.smtpPort;
|
||||
if (data.smtpSecure !== undefined) entity.SmtpSecure = data.smtpSecure;
|
||||
if (data.smtpUser !== undefined) entity.SmtpUser = data.smtpUser;
|
||||
if (data.smtpPass !== undefined && data.smtpPass !== null && data.smtpPass !== '') {
|
||||
entity.SmtpPass = data.smtpPass;
|
||||
}
|
||||
if (data.smtpFrom !== undefined) entity.SmtpFrom = data.smtpFrom;
|
||||
if (data.mailSignatureHtml !== undefined) entity.MailSignatureHtml = data.mailSignatureHtml;
|
||||
|
||||
await this.repo.save(entity);
|
||||
return this.toDto(entity);
|
||||
}
|
||||
|
||||
async testSmtp(config: {
|
||||
host: string;
|
||||
port: number;
|
||||
secure: boolean;
|
||||
user: string;
|
||||
pass: string;
|
||||
}): Promise<{ ok: boolean; error?: string }> {
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: config.host,
|
||||
port: config.port,
|
||||
secure: config.secure,
|
||||
auth: { user: config.user, pass: config.pass },
|
||||
});
|
||||
try {
|
||||
await transporter.verify();
|
||||
return { ok: true };
|
||||
} catch (err: any) {
|
||||
return { ok: false, error: err.message };
|
||||
}
|
||||
}
|
||||
|
||||
async getSmtpConfig(userId: string): Promise<{
|
||||
host: string; port: number; secure: boolean; user: string; pass: string; from: string;
|
||||
} | null> {
|
||||
const entity = await this.repo.findOne({ where: { UserId: userId } });
|
||||
if (!entity?.SmtpHost || !entity?.SmtpPass) return null;
|
||||
return {
|
||||
host: entity.SmtpHost,
|
||||
port: entity.SmtpPort ?? 587,
|
||||
secure: entity.SmtpSecure,
|
||||
user: entity.SmtpUser ?? '',
|
||||
pass: entity.SmtpPass,
|
||||
from: entity.SmtpFrom ?? entity.SmtpUser ?? '',
|
||||
};
|
||||
}
|
||||
|
||||
private toDto(entity: UserSettings | null): UserSettingsDto {
|
||||
return {
|
||||
smtpHost: entity?.SmtpHost ?? null,
|
||||
smtpPort: entity?.SmtpPort ?? null,
|
||||
smtpSecure: entity?.SmtpSecure ?? false,
|
||||
smtpUser: entity?.SmtpUser ?? null,
|
||||
smtpPassSet: !!(entity?.SmtpPass),
|
||||
smtpFrom: entity?.SmtpFrom ?? null,
|
||||
mailSignatureHtml: entity?.MailSignatureHtml ?? null,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user