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