diff --git a/apps/api/src/dkv/dto/dkv-config.dto.ts b/apps/api/src/dkv/dto/dkv-config.dto.ts new file mode 100644 index 0000000..06eb381 --- /dev/null +++ b/apps/api/src/dkv/dto/dkv-config.dto.ts @@ -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; +} diff --git a/apps/api/src/dkv/dto/dkv-history.dto.ts b/apps/api/src/dkv/dto/dkv-history.dto.ts new file mode 100644 index 0000000..d441f93 --- /dev/null +++ b/apps/api/src/dkv/dto/dkv-history.dto.ts @@ -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; +} diff --git a/apps/api/src/dkv/dto/dkv-vehicle.dto.ts b/apps/api/src/dkv/dto/dkv-vehicle.dto.ts new file mode 100644 index 0000000..15468a1 --- /dev/null +++ b/apps/api/src/dkv/dto/dkv-vehicle.dto.ts @@ -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; +} diff --git a/apps/api/src/dkv/providers/inbox-provider.interface.ts b/apps/api/src/dkv/providers/inbox-provider.interface.ts new file mode 100644 index 0000000..4a14a5c --- /dev/null +++ b/apps/api/src/dkv/providers/inbox-provider.interface.ts @@ -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; + + /** + * 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; +}