Add Gmail-DWD send path for the digest mailer (v0.1.0:76)
The box's existing service-account domain-wide-delegation grant already includes gmail.compose, which authorizes users.messages.send — verified 2026-06-15 by a token-mint probe and a live messages.send to grant. So CRM-originated mail can send through the account that already powers email capture: no SMTP account, no app password, no admin change. - backend/email_integration/gmail_send.py: send_via_gmail() impersonates a domain user and POSTs users.messages.send (reuses credentials.py + the compose scope; mirrors compose.py's REST pattern). - backend/digest_mailer.py: send_digest() prefers Gmail DWD when enabled, falls back to smtp_send otherwise. Sender = CRM_DIGEST_SENDER else first active admin. - server.py: the admin test endpoint now routes through digest_mailer (so the Settings button sends via DWD on the box with zero SMTP config). Recipient restriction to the admin set and no-leak error handling preserved. - test_gmail_send.py: build/send + transport routing (provider + urlopen faked). 19/19 backend green; s9pk typechecks. SMTP (v75) stays as the fallback transport. Send-path decision + scope finding recorded in ROADMAP.md and AGENTS.md.
This commit is contained in:
@@ -36,8 +36,9 @@ import { v_0_1_0_72 } from './v0.1.0.72'
|
||||
import { v_0_1_0_73 } from './v0.1.0.73'
|
||||
import { v_0_1_0_74 } from './v0.1.0.74'
|
||||
import { v_0_1_0_75 } from './v0.1.0.75'
|
||||
import { v_0_1_0_76 } from './v0.1.0.76'
|
||||
|
||||
export const versionGraph = VersionGraph.of({
|
||||
current: v_0_1_0_75,
|
||||
other: [v_0_1_0_39, v_0_1_0_40, v_0_1_0_41, v_0_1_0_42, v_0_1_0_43, v_0_1_0_44, v_0_1_0_45, v_0_1_0_46, v_0_1_0_47, v_0_1_0_48, v_0_1_0_49, v_0_1_0_50, v_0_1_0_51, v_0_1_0_52, v_0_1_0_53, v_0_1_0_54, v_0_1_0_55, v_0_1_0_56, v_0_1_0_57, v_0_1_0_58, v_0_1_0_59, v_0_1_0_60, v_0_1_0_61, v_0_1_0_62, v_0_1_0_63, v_0_1_0_64, v_0_1_0_65, v_0_1_0_66, v_0_1_0_67, v_0_1_0_68, v_0_1_0_69, v_0_1_0_70, v_0_1_0_71, v_0_1_0_72, v_0_1_0_73, v_0_1_0_74],
|
||||
current: v_0_1_0_76,
|
||||
other: [v_0_1_0_39, v_0_1_0_40, v_0_1_0_41, v_0_1_0_42, v_0_1_0_43, v_0_1_0_44, v_0_1_0_45, v_0_1_0_46, v_0_1_0_47, v_0_1_0_48, v_0_1_0_49, v_0_1_0_50, v_0_1_0_51, v_0_1_0_52, v_0_1_0_53, v_0_1_0_54, v_0_1_0_55, v_0_1_0_56, v_0_1_0_57, v_0_1_0_58, v_0_1_0_59, v_0_1_0_60, v_0_1_0_61, v_0_1_0_62, v_0_1_0_63, v_0_1_0_64, v_0_1_0_65, v_0_1_0_66, v_0_1_0_67, v_0_1_0_68, v_0_1_0_69, v_0_1_0_70, v_0_1_0_71, v_0_1_0_72, v_0_1_0_73, v_0_1_0_74, v_0_1_0_75],
|
||||
})
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { VersionInfo } from '@start9labs/start-sdk'
|
||||
|
||||
// Digest send path via Gmail domain-wide delegation. Code-only, no schema change
|
||||
// (migrations are no-ops):
|
||||
// * The DWD grant on this deployment already includes the gmail.compose scope
|
||||
// (verified 2026-06-15: token mint + a live messages.send both succeed), and
|
||||
// gmail.compose authorizes users.messages.send. So CRM-originated mail can go
|
||||
// through the existing service account — no SMTP account / app password.
|
||||
// * backend/email_integration/gmail_send.py: send_via_gmail() impersonates a
|
||||
// domain user and POSTs users.messages.send (mirrors compose.py's REST path).
|
||||
// * backend/digest_mailer.py: send_digest() prefers Gmail DWD when enabled and
|
||||
// falls back to smtp_send when not. The admin POST /api/admin/digest/test-email
|
||||
// and the Settings "Send Test Digest Email" button route through it.
|
||||
// * Sender for the DWD path is CRM_DIGEST_SENDER, else the first active admin.
|
||||
export const v_0_1_0_76 = VersionInfo.of({
|
||||
version: '0.1.0:76',
|
||||
releaseNotes: {
|
||||
en_US: [
|
||||
'The daily-digest mailer can now send through the Gmail account the CRM already uses for email',
|
||||
'capture (domain-wide delegation) — no separate SMTP account or app password needed. SMTP stays',
|
||||
'available as a fallback.',
|
||||
].join(' '),
|
||||
},
|
||||
migrations: { up: async () => {}, down: async () => {} },
|
||||
})
|
||||
Reference in New Issue
Block a user