Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

9.9KB

MassMailer architecture (draft)

Component map

flowchart LR
  O[Owner] --> L[Launcher and database preflight]
  L --> U[32-bit HTA UI]
  U --> C[Campaign and contact services]
  U --> D[In-app dispatch timer]
  C --> M[(Access MDB)]
  D --> M
  D --> E[Eligibility and pacing policy]
  E --> A[SMTP adapter]
  A --> P[SMTP server]

The HTA owns campaign editing, preview, approval, pause, reporting, and dispatch. Sending is possible only while the HTA is open and the owner has explicitly started or resumed a campaign. Closing the window starts no further sends. On reopening, all campaigns remain paused until the owner explicitly resumes them. The timer is only a wake-up mechanism; durable database state enforces pacing. A single-instance guard prevents two HTAs from dispatching at once. Keep business rules separate from UI DOM code so they can be tested with a fake clock and SMTP adapter.

Launch and provider check

The launcher reads DB_PATH, DB_PROVIDER, and DB_BITNESS from the INI, validates them, and starts the configured process architecture. On the current PC, the 32-bit host with Microsoft.ACE.OLEDB.12.0 passed the temporary .mdb create/write/read probe; 64-bit hosts failed. The deployment launcher must therefore run the 32-bit mshta.exe on this PC. It must open the configured .mdb using the configured provider from the same architecture that will host the HTA, read AppMeta.schemaVersion, and fail with a clear error if the provider, file, permissions, or schema is wrong. The temporary diagnostic is useful on another PC but is not a substitute for this startup check. Do not silently fall back to another provider or create a new production database when opening fails.

Campaign and dispatch flow

  1. Import CSV contacts with source reference and collection date. Normalize addresses for case-insensitive matching while retaining the original value. Keep a global suppression list that re-import cannot clear.
  2. Import a reusable HTML template and local images. Approval freezes the template, image bytes, recipient set, sender and reply identity, postal address, and subject in a campaign version. Create one Delivery per eligible address.
  3. The owner explicitly starts or resumes a campaign. While the HTA remains open, the timer checks the global pause flag, campaign start instant, nextAllowedAttemptUtc, and pending deliveries. It attempts at most one recipient at a time. Recheck suppression immediately before contacting SMTP.
  4. In a transaction, mark an Attempt as Started before the provider call. Record a definitive Accepted or Failed response afterward. If the outcome is unknown after a timeout or crash, mark it Uncertain on recovery and require manual reconciliation. SMTP acceptance does not prove inbox delivery.
  5. Reserve the next account-wide slot after every attempt, including failures and retries: nextAllowedAttemptUtc = max(now + interval, retryBackoffUntil). Missed slots never accumulate. On app close or restart, no new send begins and no automatic resume occurs.
  6. Show the next eligible time, outcomes, errors, and opt-out review reminder. The owner checks the monitored reply mailbox daily and records opt-outs; the next dispatch checks suppression again.

A synchronous COM SMTP call may briefly block the HTA. The feasibility spike must measure that behavior, provide a timeout and visible sending state, and determine whether a small helper process is needed for responsiveness. Any helper must remain bound to the open HTA session and must stop accepting new work when it closes.

Access MDB data model

Entity Essential fields
AppMeta schemaVersion, createdAt, migratedAt
Contact id, emailOriginal, emailKey, businessName, sourceReference, acquiredAt, country, eligibilityBasis
Suppression emailKey, reason, recordedAt, evidenceReference
TemplateVersion id, templateName, version, htmlContent, contentHash, importedAt, validationStatus
TemplateImage id, templateVersionId, originalRelativePath, contentType, contentHash, snapshotBytes, contentId
Campaign id, name, senderKey, fromSnapshot, replyToSnapshot, postalAddressSnapshot, subject, templateVersionId, status, startAtUtc, approvedAt
Delivery id, campaignId, contactId, emailKey, status, attemptCount, lastAttemptAt, providerMessageId, lastErrorCode
Attempt id, deliveryId, startedAtUtc, endedAtUtc, outcome, providerRequestId, sanitizedError
DispatchState senderKey, lastAttemptAtUtc, nextAllowedAttemptUtc
OperationalState id, globalPause, lastOptOutReviewAtUtc, lastConfigHash
ProtectedSecret id, purpose, dpapiCiphertext, createdAt, rotatedAt
AuditEvent id, actor, action, entityId, timestampUtc, detail

Use ADO parameterized commands for values and transactions for state transitions. Create unique indexes for Contact.emailKey, Suppression.emailKey, and (Delivery.campaignId, Delivery.emailKey). Index due deliveries and attempt history. Use one local MDB and one active HTA instance; do not put the database on a network share. Restrict file permissions to the operator. Back up the MDB, INI, and template sources together while the app is closed, then test restore. The MDB, lock files, backups, and live configuration are excluded from Git.

HTML templates with local images

The owner selects a local .html file. Image references must be relative to that file's folder, such as images/postcard.jpg. On import, resolve each path within that folder, reject traversal and absolute, remote, or data URLs, validate PNG/JPEG/GIF content and size, and snapshot bytes into TemplateImage. The preview uses the copied assets in a restricted context so template scripts cannot run in the privileged HTA. The mail adapter replaces each image reference with a cid: URL and builds an HTML multipart/related message with matching inline image parts. Recipient HTML must contain no file:// or drive paths. General attachments are outside v1.

The SMTP feasibility spike must prove mandatory STARTTLS, password authentication, and inline-image MIME on the supplied server, then inspect the received message. Do not commit to CDO until its transport and MIME behavior pass this test; a small transport helper is an acceptable implementation if required.

Configuration and credentials

The validated config.ini is the source of truth for system-wide settings described in config.example.ini. The MDB is the source of truth for campaign data and runtime state. The owner may edit INI values directly or through the HTA; the app validates changes before dispatch, records a configuration audit event, and never starts or resumes sending because of a config edit. Do not duplicate an editable setting in both places.

INI setting group Meaning MDB counterpart
SMTP_*, SMTP_SECRET_ID Endpoint, auth identity, required TLS/STARTTLS, from and reply addresses, secret lookup ProtectedSecret stores DPAPI ciphertext and rotation metadata.
DB_PATH, DB_PROVIDER, DB_BITNESS MDB location, OLE DB provider, and launch architecture AppMeta stores schema version.
QUEUE_INTERVAL, SEND_MODE, DISPLAY_TIME_ZONE Account-wide minimum gap, allowed sending hours, display time DispatchState stores pacing; OperationalState stores pause and review state. SEND_MODE=always means any hour while the HTA is open.
TEMPLATE_ROOT, TEMPLATE_IMAGE_*, TEMPLATE_MESSAGE_MAX_BYTES Source folder, inline image mode, supported types, size limits Template and image versions store approved snapshots.
OPT_OUT_REVIEW_HOURS, BUSINESS_POSTAL_ADDRESS Review reminder and required footer for new campaigns Review timestamp and campaign footer snapshot.

SMTP_TLS=on means encryption is mandatory; SMTP_STARTTLS=on means the client must negotiate STARTTLS before authentication or message data. Port 587 is configurable. Invalid combinations, handshake failure, or invalid certificates block sending. QUEUE_INTERVAL=300 means at least 300 seconds between attempts from this account across all campaigns. On interval change, use max(persistedNextAllowedUtc, lastAttemptAtUtc + currentInterval) so a new value cannot shorten the reserved gap.

The password setup/rotation action accepts the password without writing it to a command line, file, or log, protects it with Windows DPAPI CurrentUser, and writes only ciphertext to ProtectedSecret. The HTA may show whether a secret exists; plaintext should exist only for the brief SMTP authentication call. A backup restored under another Windows identity requires password re-entry. This design needs a tested implementation because script hosts do not expose DPAPI directly; a small local helper may be required.

Open technical decisions

Area Decision to prove in Sprint 0
SMTP Verify actual server authentication, STARTTLS certificate validation, inline MIME, size caps, and marketing policy.
MDB Choose migration and backup procedure; verify the real startup gate with the 32-bit HTA and configured file.
UI responsiveness Measure synchronous send duration and choose a bounded helper if the HTA blocks too long.
DPAPI Implement and test CurrentUser protection from the chosen script/helper boundary without exposing plaintext in logs or process arguments.

Technical source notes

Powered by TurnKey Linux.