Files
ssh-access-tui/outlook-bulk-sender/BulkArchiveMailer.Core/SendJob.cs
T
redlineoandClaude Sonnet 5 804b9610ca Generalize mail templates from a fixed pair to an operator-managed list
ArchiveKind was a hardcoded two-value enum (ssh_access/config_update),
wired through FilenameParser's regex, MailTemplateSet's two fixed
slots, and SettingsForm's two fixed tabs. Operators wanted arbitrary
custom templates (own file-name suffix, subject, body) without a code
change per new archive kind ssh-access-tui might start producing.

Replaced with MailTemplateDefinition (DisplayName/FileSuffix/Subject/
BodyHtml) in an open-ended list: FilenameParser now accepts any
"{login}_{suffix}.7z" and ArchiveScanner matches the suffix against
the configured list; SendJob.Kind/SendLogEntry.Kind became plain
strings; SettingsForm gained a template list (+/- buttons, editor
panel) replacing the two static tabs. ssh_access/config_update are now
just the two default entries in that list, not special-cased.

Handles the two backward-compat edges that matter for a tool already
in real use: old config.json's object-shaped "templates" migrates to
the new list shape transparently on first load (DataContractJsonSerializer
turned out to silently produce an empty list rather than throw on the
old shape, so detection is structural, not exception-based), and old
send-log.csv rows with PascalCase Kind values (SshAccess/ConfigUpdate)
still dedupe correctly against the new snake_case suffixes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 11:26:31 +03:00

70 lines
3.0 KiB
C#

using System.Collections.Generic;
using System.IO;
namespace BulkArchiveMailer.Core
{
/// <summary>Status of one preview row / send attempt.</summary>
public enum SendStatus
{
/// <summary>Exactly one archive found for this login, nothing sent for it yet - safe to send.</summary>
Ready,
/// <summary>A prior log entry shows this exact archive (by SHA-256) was already sent to this address.</summary>
AlreadySent,
/// <summary>Blocking data problem (e.g. both archive kinds present for one login) - never sendable.</summary>
Error,
/// <summary>File in the folder did not match the expected naming pattern - listed for visibility, not sendable.</summary>
Skipped
}
/// <summary>
/// One row of the send preview: everything needed to either display the
/// row in the grid or hand it to MailSender.
/// </summary>
public sealed class SendJob
{
public string Login { get; set; } = "";
public string Email { get; set; } = "";
/// <summary>File suffix ("ssh_access", "config_update", or any other configured template's FileSuffix). Null for Error rows caused by a login/kind conflict, and for Skipped (unrecognized file) rows.</summary>
public string Kind { get; set; }
/// <summary>Null for conflict Error rows (see <see cref="ConflictingArchivePaths"/>) - otherwise the single archive to attach.</summary>
public string ArchivePath { get; set; }
/// <summary>Populated only for the "both kinds present" Error case: every archive path found for that login.</summary>
public IReadOnlyList<string> ConflictingArchivePaths { get; set; }
public string Subject { get; set; } = "";
public string BodyHtml { get; set; } = "";
public SendStatus Status { get; set; } = SendStatus.Ready;
/// <summary>Human-readable explanation, set for Error/Skipped rows and for AlreadySent rows (when it was sent).</summary>
public string ErrorMessage { get; set; }
/// <summary>SHA-256 of ArchivePath's current bytes, computed when a SendLogStore is available. Used for the AlreadySent dedup key.</summary>
public string FileSha256 { get; set; }
/// <summary>Whether this row is even eligible to be checked/sent in the preview grid.</summary>
public bool IsSelectable => Status == SendStatus.Ready || Status == SendStatus.AlreadySent;
public string FileName => string.IsNullOrEmpty(ArchivePath) ? "" : Path.GetFileName(ArchivePath);
/// <summary>
/// Human-readable label for the matched template (its
/// DisplayName), explicitly set by ArchiveScanner when it renders
/// this job - "-" for conflict Error rows and Skipped rows, which
/// never had a matched template. No longer computed from a fixed
/// enum switch, since the set of templates is now open-ended.
/// </summary>
public string KindDisplay { get; set; } = "-";
public override string ToString() => $"{Login} <{Email}> [{Status}]";
}
}