feat(07-02): InboxProvider interface + config/vehicle/history DTOs
- inbox-provider.interface.ts: InboxProvider contract with fetchPdfAttachments + testConnection; re-exports InboxConfig/InboxEmail/InboxAttachment with export type (isolatedModules) - dkv-config.dto.ts: DkvConfigDto with @IsEmail() senderFilter/exportRecipient, @Min(5) pollIntervalMin, @IsIn() protocol/encryption (T-07-04) - dkv-vehicle.dto.ts: CreateVehicleDto (all required @IsNotEmpty) + UpdateVehicleDto (all optional) - dkv-history.dto.ts: DkvHistoryQueryDto with @IsInt @Min(1) page/limit pagination (T-07-06) - Fix: export type re-exports required for isolatedModules TypeScript setting
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import {
|
||||
IsBoolean,
|
||||
IsEmail,
|
||||
IsIn,
|
||||
IsInt,
|
||||
IsOptional,
|
||||
IsString,
|
||||
Max,
|
||||
Min,
|
||||
} from 'class-validator';
|
||||
|
||||
/**
|
||||
* DTO for creating or updating DKV module configuration.
|
||||
*
|
||||
* Security:
|
||||
* - T-07-04: senderFilter validated as email address (injection mitigation)
|
||||
* - T-07-04: port constrained to 1–65535 (invalid port rejection)
|
||||
* - T-07-04: protocol and encryption constrained with @IsIn (enum injection mitigation)
|
||||
* - T-07-05: pollIntervalMin minimum 5 (DoS mitigation — no sub-5-minute polling)
|
||||
*/
|
||||
export class DkvConfigDto {
|
||||
/**
|
||||
* Inbox protocol — 'imap' for IMAP, 'exchange' for Exchange (EWS).
|
||||
*/
|
||||
@IsIn(['imap', 'exchange'])
|
||||
protocol!: string;
|
||||
|
||||
/**
|
||||
* Mail server hostname or IP address (IMAP: IMAP server, Exchange: EWS endpoint host).
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
host?: string;
|
||||
|
||||
/**
|
||||
* TCP port. Standard values: 993 (IMAP SSL/TLS), 143 (IMAP STARTTLS), 443 (EWS).
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Max(65535)
|
||||
port?: number;
|
||||
|
||||
/**
|
||||
* TLS mode: 'none' = plain TCP, 'starttls' = STARTTLS upgrade, 'ssl-tls' = implicit TLS.
|
||||
*/
|
||||
@IsIn(['none', 'starttls', 'ssl-tls'])
|
||||
encryption!: string;
|
||||
|
||||
/**
|
||||
* IMAP folder to monitor (e.g. "INBOX", "DKV"). Exchange always uses Inbox.
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
folder?: string;
|
||||
|
||||
/**
|
||||
* Sender email address to filter by. Only emails from this sender are processed.
|
||||
* Validated as email address to prevent header injection (T-07-04 / Research V5).
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsEmail()
|
||||
senderFilter?: string;
|
||||
|
||||
/**
|
||||
* Email address to which the generated Excel export is sent.
|
||||
* Validated as email address (T-07-04).
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsEmail()
|
||||
exportRecipient?: string;
|
||||
|
||||
/**
|
||||
* Polling interval in minutes. Minimum 5 minutes to prevent DoS via excessive polling
|
||||
* (T-07-05, UI-SPEC minimum = 5).
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Min(5)
|
||||
pollIntervalMin?: number;
|
||||
|
||||
/**
|
||||
* Whether the automatic polling cron job is active.
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
isActive?: boolean;
|
||||
|
||||
/**
|
||||
* Format string for the Fahrzeug column in the Excel export.
|
||||
* Placeholders: {Marke}, {Modell}, {Kennzeichen}, {Fahrer}.
|
||||
* Default: "{Marke}/{Modell}/{Kennzeichen}".
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
vehicleFormatString?: string;
|
||||
|
||||
/**
|
||||
* Inbox username (stored encrypted; cleared after save).
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
username?: string;
|
||||
|
||||
/**
|
||||
* Inbox password (stored encrypted; cleared after save).
|
||||
* T-07-03: never returned to the frontend in responses.
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
password?: string;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { IsInt, IsOptional, Min } from 'class-validator';
|
||||
|
||||
/**
|
||||
* Query DTO for paginating the DKV invoice processing history.
|
||||
*
|
||||
* Security:
|
||||
* - T-07-06: Pagination limits prevent unbounded history accumulation DoS
|
||||
* (Research Security Domain: "Excessive history accumulation" pattern)
|
||||
*
|
||||
* Used for: GET /dkv/history?page=1&limit=20
|
||||
*/
|
||||
export class DkvHistoryQueryDto {
|
||||
/**
|
||||
* Page number (1-based). Defaults to 1 when omitted.
|
||||
* T-07-06: bounded integer prevents negative-page or non-integer injection.
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
page?: number;
|
||||
|
||||
/**
|
||||
* Number of records per page. Defaults to 20 when omitted.
|
||||
* T-07-06: bounded integer mitigates oversized result-set DoS.
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
limit?: number;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
/**
|
||||
* DTO for creating a vehicle master entry.
|
||||
*
|
||||
* All four fields represent the vehicle identity and driver mapping.
|
||||
* Used for: POST /dkv/vehicles
|
||||
*/
|
||||
export class CreateVehicleDto {
|
||||
/**
|
||||
* License plate (Kennzeichen), e.g. "GP-JL 728E".
|
||||
* Unique per tenant — used to map DKV invoice vehicle blocks to master data.
|
||||
*/
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
kennzeichen!: string;
|
||||
|
||||
/**
|
||||
* Vehicle make (Marke), e.g. "Mercedes".
|
||||
*/
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
marke!: string;
|
||||
|
||||
/**
|
||||
* Vehicle model (Modell), e.g. "GLC 300 de 4MATIC".
|
||||
*/
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
modell!: string;
|
||||
|
||||
/**
|
||||
* Driver name in "Vorname Nachname" format, e.g. "Max Mustermann".
|
||||
* Mapped to the Fahrer column in the Excel export (D-13).
|
||||
*/
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
fahrer!: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* DTO for updating a vehicle master entry (all fields optional).
|
||||
*
|
||||
* Used for: PATCH /dkv/vehicles/:id
|
||||
*/
|
||||
export class UpdateVehicleDto {
|
||||
/**
|
||||
* New license plate. Updating changes the lookup key — use with care.
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
kennzeichen?: string;
|
||||
|
||||
/**
|
||||
* New vehicle make.
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
marke?: string;
|
||||
|
||||
/**
|
||||
* New vehicle model.
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
modell?: string;
|
||||
|
||||
/**
|
||||
* New driver name.
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
fahrer?: string;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { InboxAttachment, InboxConfig, InboxEmail } from '../dkv.types';
|
||||
|
||||
// Re-export types for downstream consumers that import from this module
|
||||
export type { InboxAttachment, InboxConfig, InboxEmail };
|
||||
|
||||
/**
|
||||
* Abstract inbox provider contract — implemented by ImapProvider and
|
||||
* ExchangeInboxProvider. Consumers (DkvService, DkvSchedulerService) depend
|
||||
* only on this interface, not on the concrete implementations.
|
||||
*
|
||||
* Security:
|
||||
* - T-07-03: Implementations MUST NOT log credential values
|
||||
* - InboxConfig carries decrypted credentials — never persist, never serialize
|
||||
*/
|
||||
export interface InboxProvider {
|
||||
/**
|
||||
* Connects to the configured inbox, searches for emails from the configured
|
||||
* sender filter, downloads PDF attachments, and returns all matching emails.
|
||||
*
|
||||
* @param config Decrypted inbox connection parameters
|
||||
* @returns Array of emails with their PDF attachments as Buffers
|
||||
*/
|
||||
fetchPdfAttachments(config: InboxConfig): Promise<InboxEmail[]>;
|
||||
|
||||
/**
|
||||
* Tests whether the inbox connection can be established.
|
||||
*
|
||||
* @param config Decrypted inbox connection parameters
|
||||
* @returns true if connection succeeded, false on any auth/network error
|
||||
*/
|
||||
testConnection(config: InboxConfig): Promise<boolean>;
|
||||
}
|
||||
Reference in New Issue
Block a user