[{"data":1,"prerenderedAt":1224},["ShallowReactive",2],{"navigation_docs_en":3,"-en-cli-add-mail":342,"-en-cli-add-mail-surround":1219},[4,45,61,96,132,166,196,217,241,265],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":44},"Getting Started","i-lucide-rocket","\u002Fen\u002Fgetting-started","en\u002F1.getting-started",[10,15,20,24,29,34,39],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fen\u002Fgetting-started\u002Fintroduction","en\u002F1.getting-started\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Coming from Laravel","\u002Fen\u002Fgetting-started\u002Fcoming-from-laravel","en\u002F1.getting-started\u002F2.coming-from-laravel","i-lucide-arrow-right-left",{"title":21,"path":22,"stem":23,"icon":19},"Coming from Symfony","\u002Fen\u002Fgetting-started\u002Fcoming-from-symfony","en\u002F1.getting-started\u002F3.coming-from-symfony",{"title":25,"path":26,"stem":27,"icon":28},"Installation","\u002Fen\u002Fgetting-started\u002Finstallation","en\u002F1.getting-started\u002F4.installation","i-lucide-download",{"title":30,"path":31,"stem":32,"icon":33},"Your first module","\u002Fen\u002Fgetting-started\u002Ffirst-module","en\u002F1.getting-started\u002F5.first-module","i-lucide-package-plus",{"title":35,"path":36,"stem":37,"icon":38},"Project structure","\u002Fen\u002Fgetting-started\u002Fproject-structure","en\u002F1.getting-started\u002F6.project-structure","i-lucide-folder-tree",{"title":40,"path":41,"stem":42,"icon":43},"The shop sample application","\u002Fen\u002Fgetting-started\u002Fsample-app","en\u002F1.getting-started\u002F7.sample-app","i-lucide-shopping-cart",false,{"title":46,"icon":47,"path":48,"stem":49,"children":50,"page":44},"Reference","i-lucide-book-open","\u002Fen\u002Freference","en\u002F10.reference",[51,56],{"title":52,"path":53,"stem":54,"icon":55},"Configuration","\u002Fen\u002Freference\u002Fconfiguration","en\u002F10.reference\u002F1.configuration","i-lucide-settings",{"title":57,"path":58,"stem":59,"icon":60},"External dependencies","\u002Fen\u002Freference\u002Fdependencies","en\u002F10.reference\u002F2.dependencies","i-lucide-package",{"title":62,"icon":63,"path":64,"stem":65,"children":66,"page":44},"Concepts","i-lucide-lightbulb","\u002Fen\u002Fconcepts","en\u002F2.concepts",[67,72,77,82,86,91],{"title":68,"path":69,"stem":70,"icon":71},"Architecture","\u002Fen\u002Fconcepts\u002Farchitecture","en\u002F2.concepts\u002F1.architecture","i-lucide-layers",{"title":73,"path":74,"stem":75,"icon":76},"Application lifecycle","\u002Fen\u002Fconcepts\u002Fapplication-lifecycle","en\u002F2.concepts\u002F2.application-lifecycle","i-lucide-power",{"title":78,"path":79,"stem":80,"icon":81},"Error model","\u002Fen\u002Fconcepts\u002Ferror-model","en\u002F2.concepts\u002F3.error-model","i-lucide-shield-alert",{"title":52,"path":83,"stem":84,"icon":85},"\u002Fen\u002Fconcepts\u002Fconfiguration","en\u002F2.concepts\u002F4.configuration","i-lucide-settings-2",{"title":87,"path":88,"stem":89,"icon":90},"Codegen pipeline","\u002Fen\u002Fconcepts\u002Fcodegen-pipeline","en\u002F2.concepts\u002F5.codegen-pipeline","i-lucide-file-json",{"title":92,"path":93,"stem":94,"icon":95},"Design patterns","\u002Fen\u002Fconcepts\u002Fdesign-patterns","en\u002F2.concepts\u002F6.design-patterns","i-lucide-puzzle",{"title":97,"icon":98,"path":99,"stem":100,"children":101,"page":44},"HTTP & Routing","i-lucide-globe","\u002Fen\u002Fhttp","en\u002F3.http",[102,107,112,117,122,127],{"title":103,"path":104,"stem":105,"icon":106},"The server core","\u002Fen\u002Fhttp\u002Fserver","en\u002F3.http\u002F1.server","i-lucide-server",{"title":108,"path":109,"stem":110,"icon":111},"The Fiber engine","\u002Fen\u002Fhttp\u002Ffiber","en\u002F3.http\u002F2.fiber","i-lucide-zap",{"title":113,"path":114,"stem":115,"icon":116},"The chi engine","\u002Fen\u002Fhttp\u002Fchi","en\u002F3.http\u002F3.chi","i-lucide-route",{"title":118,"path":119,"stem":120,"icon":121},"Error responses","\u002Fen\u002Fhttp\u002Ferror-responses","en\u002F3.http\u002F4.error-responses","i-lucide-octagon-alert",{"title":123,"path":124,"stem":125,"icon":126},"Validation","\u002Fen\u002Fhttp\u002Fvalidation","en\u002F3.http\u002F5.validation","i-lucide-badge-check",{"title":128,"path":129,"stem":130,"icon":131},"Pagination","\u002Fen\u002Fhttp\u002Fpagination","en\u002F3.http\u002F6.pagination","i-lucide-list-ordered",{"title":133,"icon":134,"path":135,"stem":136,"children":137,"page":44},"Database","i-lucide-database","\u002Fen\u002Fdatabase","en\u002F4.database",[138,142,147,151,156,161],{"title":139,"path":140,"stem":141,"icon":134},"Overview","\u002Fen\u002Fdatabase\u002Foverview","en\u002F4.database\u002F1.overview",{"title":143,"path":144,"stem":145,"icon":146},"pgx","\u002Fen\u002Fdatabase\u002Fpgx","en\u002F4.database\u002F2.pgx","i-lucide-plug",{"title":148,"path":149,"stem":150,"icon":71},"bun","\u002Fen\u002Fdatabase\u002Fbun","en\u002F4.database\u002F3.bun",{"title":152,"path":153,"stem":154,"icon":155},"Transactions","\u002Fen\u002Fdatabase\u002Ftransactions","en\u002F4.database\u002F4.transactions","i-lucide-git-merge",{"title":157,"path":158,"stem":159,"icon":160},"Migrations","\u002Fen\u002Fdatabase\u002Fmigrations","en\u002F4.database\u002F5.migrations","i-lucide-file-stack",{"title":162,"path":163,"stem":164,"icon":165},"Seeders","\u002Fen\u002Fdatabase\u002Fseeding","en\u002F4.database\u002F6.seeding","i-lucide-sprout",{"title":167,"icon":168,"path":169,"stem":170,"children":171,"page":44},"Async & Background","i-lucide-workflow","\u002Fen\u002Fasync","en\u002F5.async",[172,176,181,186,191],{"title":173,"path":174,"stem":175,"icon":131},"Queue","\u002Fen\u002Fasync\u002Fqueue","en\u002F5.async\u002F1.queue",{"title":177,"path":178,"stem":179,"icon":180},"Events","\u002Fen\u002Fasync\u002Fevents","en\u002F5.async\u002F2.events","i-lucide-radio",{"title":182,"path":183,"stem":184,"icon":185},"Outbox","\u002Fen\u002Fasync\u002Foutbox","en\u002F5.async\u002F3.outbox","i-lucide-inbox",{"title":187,"path":188,"stem":189,"icon":190},"Scheduling","\u002Fen\u002Fasync\u002Fscheduler","en\u002F5.async\u002F4.scheduler","i-lucide-calendar-clock",{"title":192,"path":193,"stem":194,"icon":195},"Worker","\u002Fen\u002Fasync\u002Fworker","en\u002F5.async\u002F5.worker","i-lucide-cog",{"title":197,"icon":198,"path":199,"stem":200,"children":201,"page":44},"Security","i-lucide-shield-check","\u002Fen\u002Fsecurity","en\u002F6.security",[202,207,212],{"title":203,"path":204,"stem":205,"icon":206},"Authentication","\u002Fen\u002Fsecurity\u002Fauthentication","en\u002F6.security\u002F1.authentication","i-lucide-key-round",{"title":208,"path":209,"stem":210,"icon":211},"RBAC","\u002Fen\u002Fsecurity\u002Frbac","en\u002F6.security\u002F2.rbac","i-lucide-users",{"title":213,"path":214,"stem":215,"icon":216},"Policies","\u002Fen\u002Fsecurity\u002Fpolicies","en\u002F6.security\u002F3.policies","i-lucide-gavel",{"title":218,"icon":219,"path":220,"stem":221,"children":222,"page":44},"Observability","i-lucide-activity","\u002Fen\u002Fobservability","en\u002F7.observability",[223,226,231,236],{"title":139,"path":224,"stem":225,"icon":219},"\u002Fen\u002Fobservability\u002Foverview","en\u002F7.observability\u002F1.overview",{"title":227,"path":228,"stem":229,"icon":230},"Logging","\u002Fen\u002Fobservability\u002Flogging","en\u002F7.observability\u002F2.logging","i-lucide-scroll-text",{"title":232,"path":233,"stem":234,"icon":235},"OpenTelemetry","\u002Fen\u002Fobservability\u002Fopentelemetry","en\u002F7.observability\u002F3.opentelemetry","i-lucide-radar",{"title":237,"path":238,"stem":239,"icon":240},"Sentry","\u002Fen\u002Fobservability\u002Fsentry","en\u002F7.observability\u002F4.sentry","i-lucide-bug",{"title":242,"icon":243,"path":244,"stem":245,"children":246,"page":44},"Storage & Mail","i-lucide-hard-drive","\u002Fen\u002Fstorage-mail","en\u002F8.storage-mail",[247,251,256,261],{"title":248,"path":249,"stem":250,"icon":243},"Storage","\u002Fen\u002Fstorage-mail\u002Fstorage","en\u002F8.storage-mail\u002F1.storage",{"title":252,"path":253,"stem":254,"icon":255},"Email","\u002Fen\u002Fstorage-mail\u002Fmail","en\u002F8.storage-mail\u002F2.mail","i-lucide-mail",{"title":257,"path":258,"stem":259,"icon":260},"Notifications","\u002Fen\u002Fstorage-mail\u002Fnotifications","en\u002F8.storage-mail\u002F3.notifications","i-lucide-bell",{"title":262,"path":263,"stem":264,"icon":180},"Realtime","\u002Fen\u002Fstorage-mail\u002Frealtime","en\u002F8.storage-mail\u002F4.realtime",{"title":266,"icon":267,"path":268,"stem":269,"children":270,"page":44},"CLI Reference","i-lucide-terminal","\u002Fen\u002Fcli","en\u002F9.cli",[271,274,279,284,289,294,299,303,308,313,318,323,328,333,337],{"title":139,"path":272,"stem":273,"icon":267},"\u002Fen\u002Fcli\u002Foverview","en\u002F9.cli\u002F1.overview",{"title":275,"path":276,"stem":277,"icon":278},"add db","\u002Fen\u002Fcli\u002Fadd-db","en\u002F9.cli\u002F10.add-db","i-lucide-database-zap",{"title":280,"path":281,"stem":282,"icon":283},"add compose \u002F add docker","\u002Fen\u002Fcli\u002Fadd-compose","en\u002F9.cli\u002F11.add-compose","i-lucide-container",{"title":285,"path":286,"stem":287,"icon":288},"add mail","\u002Fen\u002Fcli\u002Fadd-mail","en\u002F9.cli\u002F12.add-mail","i-lucide-mail-plus",{"title":290,"path":291,"stem":292,"icon":293},"add notification","\u002Fen\u002Fcli\u002Fadd-notification","en\u002F9.cli\u002F13.add-notification","i-lucide-bell-plus",{"title":295,"path":296,"stem":297,"icon":298},"add realtime","\u002Fen\u002Fcli\u002Fadd-realtime","en\u002F9.cli\u002F14.add-realtime","i-lucide-radio-tower",{"title":300,"path":301,"stem":302,"icon":165},"add seeder","\u002Fen\u002Fcli\u002Fadd-seeder","en\u002F9.cli\u002F15.add-seeder",{"title":304,"path":305,"stem":306,"icon":307},"new project","\u002Fen\u002Fcli\u002Fnew-project","en\u002F9.cli\u002F2.new-project","i-lucide-folder-plus",{"title":309,"path":310,"stem":311,"icon":312},"new module","\u002Fen\u002Fcli\u002Fnew-module","en\u002F9.cli\u002F3.new-module","i-lucide-blocks",{"title":314,"path":315,"stem":316,"icon":317},"add surface","\u002Fen\u002Fcli\u002Fadd-surface","en\u002F9.cli\u002F4.add-surface","i-lucide-layers-2",{"title":319,"path":320,"stem":321,"icon":322},"add core","\u002Fen\u002Fcli\u002Fadd-core","en\u002F9.cli\u002F5.add-core","i-lucide-box",{"title":324,"path":325,"stem":326,"icon":327},"add handler","\u002Fen\u002Fcli\u002Fadd-handler","en\u002F9.cli\u002F6.add-handler","i-lucide-webhook",{"title":329,"path":330,"stem":331,"icon":332},"new migration","\u002Fen\u002Fcli\u002Fnew-migration","en\u002F9.cli\u002F7.new-migration","i-lucide-file-plus",{"title":334,"path":335,"stem":336,"icon":195},"worker generators","\u002Fen\u002Fcli\u002Fworker-generators","en\u002F9.cli\u002F8.worker-generators",{"title":338,"path":339,"stem":340,"icon":341},"upgrade templates","\u002Fen\u002Fcli\u002Fupgrade-templates","en\u002F9.cli\u002F9.upgrade-templates","i-lucide-refresh-cw",{"id":343,"title":285,"body":344,"description":1212,"extension":1213,"links":1214,"meta":1215,"navigation":1216,"path":286,"seo":1217,"stem":287,"__hash__":1218},"docs_en\u002Fen\u002F9.cli\u002F12.add-mail.md",{"type":345,"value":346,"toc":1205},"minimark",[347,403,435,458,463,480,484,859,873,879,904,990,1033,1062,1066,1081,1152,1156,1185,1189,1201],[348,349,354],"pre",{"className":350,"code":351,"language":352,"meta":353,"style":353},"language-sh shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go tool gpsystem add mail \u003Cmodule> \u003Cname>\n","sh","",[355,356,357],"code",{"__ignoreMap":353},[358,359,362,366,370,373,376,379,383,386,390,393,395,398,400],"span",{"class":360,"line":361},"line",1,[358,363,365],{"class":364},"sBMFI","go",[358,367,369],{"class":368},"sfazB"," tool",[358,371,372],{"class":368}," gpsystem",[358,374,375],{"class":368}," add",[358,377,378],{"class":368}," mail",[358,380,382],{"class":381},"sMK4o"," \u003C",[358,384,385],{"class":368},"modul",[358,387,389],{"class":388},"sTEyZ","e",[358,391,392],{"class":381},">",[358,394,382],{"class":381},[358,396,397],{"class":368},"nam",[358,399,389],{"class":388},[358,401,402],{"class":381},">\n",[404,405,406,407,410,411,414,415,421,422,425,426,429,430,434],"p",{},"Creates a notifier in ",[355,408,409],{},"internal\u002Fmodules\u002F\u003Cmodule>\u002Fmail\u002F\u003Cname>.go",": a payload struct, a ",[355,412,413],{},"\u003CName>Notifier"," composing an injected ",[416,417,418],"a",{"href":253},[355,419,420],{},"mail.Mailer"," with this module's own templates, and its ",[355,423,424],{},"templates\u002F\u003Cname>.html.tmpl"," + ",[355,427,428],{},"templates\u002F\u003Cname>.txt.tmpl",". On a project's ",[431,432,433],"strong",{},"first"," mail notifier (in any module) it also wires the shared transport into every entry point. Idempotent: a second notifier, in the same or a different module, reuses the wiring.",[348,436,438],{"className":350,"code":437,"language":352,"meta":353,"style":353},"go tool gpsystem add mail contact contactNotification\n",[355,439,440],{"__ignoreMap":353},[358,441,442,444,446,448,450,452,455],{"class":360,"line":361},[358,443,365],{"class":364},[358,445,369],{"class":368},[358,447,372],{"class":368},[358,449,375],{"class":368},[358,451,378],{"class":368},[358,453,454],{"class":368}," contact",[358,456,457],{"class":368}," contactNotification\n",[459,460,462],"h2",{"id":461},"why-the-split","Why the split",[404,464,465,466,470,471,474,475,479],{},"Mail content (payload, templates, subject) is business content that belongs to the module that owns the use case; the SMTP transport is infrastructure, exactly like the database driver, so it's centralized once. This is the same ",[416,467,469],{"href":468},"\u002Fen\u002Fconcepts\u002Farchitecture#shared-code-a-decision-ladder","shared-code decision ladder"," the kit applies everywhere; see ",[416,472,35],{"href":473},"\u002Fen\u002Fgetting-started\u002Fproject-structure#what-can-live-inside-a-module"," for the full reasoning and the ",[416,476,478],{"href":477},"\u002Fen\u002Fstorage-mail\u002Fmail#worked-example-the-contact-email-flow","mail page's worked example",".",[459,481,483],{"id":482},"what-it-generates","What it generates",[348,485,489],{"className":486,"code":487,"filename":488,"language":365,"meta":353,"style":353},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","package mail\n\n\u002F\u002Fgo:embed templates\u002Fcontactnotification.html.tmpl templates\u002Fcontactnotification.txt.tmpl\nvar contactNotificationTemplatesFS embed.FS\n\ntype ContactNotification struct {\n}\n\ntype ContactNotificationNotifier struct{ mailer kitmail.Mailer }\n\nfunc NewContactNotificationNotifier(m kitmail.Mailer) *ContactNotificationNotifier {\n    return &ContactNotificationNotifier{mailer: m}\n}\n\nfunc (n *ContactNotificationNotifier) Notify(ctx context.Context, to string, p ContactNotification) error {\n    msg := kitmail.NewMessage().\n        WithTo(to).\n        WithSubject(\"TODO: subject for ContactNotification\").\n        WithBody(kitmail.Template(contactNotificationTemplatesFS, \"templates\u002Fcontactnotification.html.tmpl\", p).\n            WithTextTemplate(\"templates\u002Fcontactnotification.txt.tmpl\"))\n    return n.mailer.Send(ctx, msg)\n}\n","internal\u002Fmodules\u002Fcontact\u002Fmail\u002Fcontactnotification.go",[355,490,491,499,506,513,530,535,550,556,561,586,591,626,652,657,662,720,739,753,771,807,825,854],{"__ignoreMap":353},[358,492,493,496],{"class":360,"line":361},[358,494,495],{"class":381},"package",[358,497,498],{"class":364}," mail\n",[358,500,502],{"class":360,"line":501},2,[358,503,505],{"emptyLinePlaceholder":504},true,"\n",[358,507,509],{"class":360,"line":508},3,[358,510,512],{"class":511},"sHwdD","\u002F\u002Fgo:embed templates\u002Fcontactnotification.html.tmpl templates\u002Fcontactnotification.txt.tmpl\n",[358,514,516,519,522,525,527],{"class":360,"line":515},4,[358,517,518],{"class":381},"var",[358,520,521],{"class":388}," contactNotificationTemplatesFS ",[358,523,524],{"class":364},"embed",[358,526,479],{"class":381},[358,528,529],{"class":364},"FS\n",[358,531,533],{"class":360,"line":532},5,[358,534,505],{"emptyLinePlaceholder":504},[358,536,538,541,544,547],{"class":360,"line":537},6,[358,539,540],{"class":381},"type",[358,542,543],{"class":364}," ContactNotification",[358,545,546],{"class":381}," struct",[358,548,549],{"class":381}," {\n",[358,551,553],{"class":360,"line":552},7,[358,554,555],{"class":381},"}\n",[358,557,559],{"class":360,"line":558},8,[358,560,505],{"emptyLinePlaceholder":504},[358,562,564,566,569,572,575,578,580,583],{"class":360,"line":563},9,[358,565,540],{"class":381},[358,567,568],{"class":364}," ContactNotificationNotifier",[358,570,571],{"class":381}," struct{",[358,573,574],{"class":388}," mailer ",[358,576,577],{"class":364},"kitmail",[358,579,479],{"class":381},[358,581,582],{"class":364},"Mailer",[358,584,585],{"class":381}," }\n",[358,587,589],{"class":360,"line":588},10,[358,590,505],{"emptyLinePlaceholder":504},[358,592,594,597,601,604,608,611,613,615,618,621,624],{"class":360,"line":593},11,[358,595,596],{"class":381},"func",[358,598,600],{"class":599},"s2Zo4"," NewContactNotificationNotifier",[358,602,603],{"class":381},"(",[358,605,607],{"class":606},"sHdIc","m",[358,609,610],{"class":364}," kitmail",[358,612,479],{"class":381},[358,614,582],{"class":364},[358,616,617],{"class":381},")",[358,619,620],{"class":381}," *",[358,622,623],{"class":364},"ContactNotificationNotifier",[358,625,549],{"class":381},[358,627,629,633,636,638,641,644,647,650],{"class":360,"line":628},12,[358,630,632],{"class":631},"s7zQu","    return",[358,634,635],{"class":381}," &",[358,637,623],{"class":364},[358,639,640],{"class":381},"{",[358,642,643],{"class":388},"mailer",[358,645,646],{"class":381},":",[358,648,649],{"class":388}," m",[358,651,555],{"class":381},[358,653,655],{"class":360,"line":654},13,[358,656,555],{"class":381},[358,658,660],{"class":360,"line":659},14,[358,661,505],{"emptyLinePlaceholder":504},[358,663,665,667,670,673,676,678,680,683,685,688,691,693,696,699,702,706,708,711,713,715,718],{"class":360,"line":664},15,[358,666,596],{"class":381},[358,668,669],{"class":381}," (",[358,671,672],{"class":606},"n ",[358,674,675],{"class":381},"*",[358,677,623],{"class":364},[358,679,617],{"class":381},[358,681,682],{"class":599}," Notify",[358,684,603],{"class":381},[358,686,687],{"class":606},"ctx",[358,689,690],{"class":364}," context",[358,692,479],{"class":381},[358,694,695],{"class":364},"Context",[358,697,698],{"class":381},",",[358,700,701],{"class":606}," to",[358,703,705],{"class":704},"spNyl"," string",[358,707,698],{"class":381},[358,709,710],{"class":606}," p",[358,712,543],{"class":364},[358,714,617],{"class":381},[358,716,717],{"class":704}," error",[358,719,549],{"class":381},[358,721,723,726,729,731,733,736],{"class":360,"line":722},16,[358,724,725],{"class":388},"    msg ",[358,727,728],{"class":381},":=",[358,730,610],{"class":388},[358,732,479],{"class":381},[358,734,735],{"class":599},"NewMessage",[358,737,738],{"class":381},"().\n",[358,740,742,745,747,750],{"class":360,"line":741},17,[358,743,744],{"class":599},"        WithTo",[358,746,603],{"class":381},[358,748,749],{"class":388},"to",[358,751,752],{"class":381},").\n",[358,754,756,759,761,764,767,769],{"class":360,"line":755},18,[358,757,758],{"class":599},"        WithSubject",[358,760,603],{"class":381},[358,762,763],{"class":381},"\"",[358,765,766],{"class":368},"TODO: subject for ContactNotification",[358,768,763],{"class":381},[358,770,752],{"class":381},[358,772,774,777,779,781,783,786,788,791,793,796,799,801,803,805],{"class":360,"line":773},19,[358,775,776],{"class":599},"        WithBody",[358,778,603],{"class":381},[358,780,577],{"class":388},[358,782,479],{"class":381},[358,784,785],{"class":599},"Template",[358,787,603],{"class":381},[358,789,790],{"class":388},"contactNotificationTemplatesFS",[358,792,698],{"class":381},[358,794,795],{"class":381}," \"",[358,797,798],{"class":368},"templates\u002Fcontactnotification.html.tmpl",[358,800,763],{"class":381},[358,802,698],{"class":381},[358,804,710],{"class":388},[358,806,752],{"class":381},[358,808,810,813,815,817,820,822],{"class":360,"line":809},20,[358,811,812],{"class":599},"            WithTextTemplate",[358,814,603],{"class":381},[358,816,763],{"class":381},[358,818,819],{"class":368},"templates\u002Fcontactnotification.txt.tmpl",[358,821,763],{"class":381},[358,823,824],{"class":381},"))\n",[358,826,828,830,833,835,837,839,842,844,846,848,851],{"class":360,"line":827},21,[358,829,632],{"class":631},[358,831,832],{"class":388}," n",[358,834,479],{"class":381},[358,836,643],{"class":388},[358,838,479],{"class":381},[358,840,841],{"class":599},"Send",[358,843,603],{"class":381},[358,845,687],{"class":388},[358,847,698],{"class":381},[358,849,850],{"class":388}," msg",[358,852,853],{"class":381},")\n",[358,855,857],{"class":360,"line":856},22,[358,858,555],{"class":381},[404,860,861,862,865,866,872],{},"Plus a memory-mailer test (",[355,863,864],{},"contactnotification_test.go",") and two placeholder templates you fill in. The payload struct starts empty, the same way ",[416,867,869],{"href":868},"\u002Fen\u002Fcli\u002Fworker-generators#add-event",[355,870,871],{},"add event"," generates an empty event struct: add whatever fields your templates need.",[404,874,875,876,878],{},"On the ",[431,877,433],{}," notifier in the project:",[348,880,883],{"className":486,"code":881,"filename":882,"language":365,"meta":353,"style":353},"Mail smtp.Config `envPrefix:\"MAIL_\"`\n","internal\u002Fplatform\u002Fconfig\u002Fconfig.go",[355,884,885],{"__ignoreMap":353},[358,886,887,890,892,895,898,901],{"class":360,"line":361},[358,888,889],{"class":388},"Mail smtp",[358,891,479],{"class":381},[358,893,894],{"class":388},"Config ",[358,896,897],{"class":381},"`",[358,899,900],{"class":368},"envPrefix:\"MAIL_\"",[358,902,903],{"class":381},"`\n",[348,905,908],{"className":486,"code":906,"filename":907,"language":365,"meta":353,"style":353},"mailer := smtp.MustNewIfConfigured(ctx, cfg.Mail)\n\u002F\u002F ...\ndeps := contact.Dependencies{\n    \u002F\u002F ...\n    Mailer: mailer,\n    \u002F\u002F gpsystem:dependencies-init\n}\n","cmd\u002Fcontact\u002Fmain.go and cmd\u002Fworker\u002Fmain.go",[355,909,910,941,946,963,968,981,986],{"__ignoreMap":353},[358,911,912,915,917,920,922,925,927,929,931,934,936,939],{"class":360,"line":361},[358,913,914],{"class":388},"mailer ",[358,916,728],{"class":381},[358,918,919],{"class":388}," smtp",[358,921,479],{"class":381},[358,923,924],{"class":599},"MustNewIfConfigured",[358,926,603],{"class":381},[358,928,687],{"class":388},[358,930,698],{"class":381},[358,932,933],{"class":388}," cfg",[358,935,479],{"class":381},[358,937,938],{"class":388},"Mail",[358,940,853],{"class":381},[358,942,943],{"class":360,"line":501},[358,944,945],{"class":511},"\u002F\u002F ...\n",[358,947,948,951,953,955,957,960],{"class":360,"line":508},[358,949,950],{"class":388},"deps ",[358,952,728],{"class":381},[358,954,454],{"class":364},[358,956,479],{"class":381},[358,958,959],{"class":364},"Dependencies",[358,961,962],{"class":381},"{\n",[358,964,965],{"class":360,"line":515},[358,966,967],{"class":511},"    \u002F\u002F ...\n",[358,969,970,973,975,978],{"class":360,"line":532},[358,971,972],{"class":388},"    Mailer",[358,974,646],{"class":381},[358,976,977],{"class":388}," mailer",[358,979,980],{"class":381},",\n",[358,982,983],{"class":360,"line":537},[358,984,985],{"class":511},"    \u002F\u002F gpsystem:dependencies-init\n",[358,987,988],{"class":360,"line":552},[358,989,555],{"class":381},[348,991,994],{"className":486,"code":992,"filename":993,"language":365,"meta":353,"style":353},"type Dependencies struct {\n    \u002F\u002F ...\n    Mailer mail.Mailer\n    \u002F\u002F gpsystem:dependencies\n}\n","internal\u002Fmodules\u002Fcontact\u002Fregister.go",[355,995,996,1007,1011,1024,1029],{"__ignoreMap":353},[358,997,998,1000,1003,1005],{"class":360,"line":361},[358,999,540],{"class":381},[358,1001,1002],{"class":364}," Dependencies",[358,1004,546],{"class":381},[358,1006,549],{"class":381},[358,1008,1009],{"class":360,"line":501},[358,1010,967],{"class":511},[358,1012,1013,1016,1019,1021],{"class":360,"line":508},[358,1014,1015],{"class":388},"    Mailer ",[358,1017,1018],{"class":364},"mail",[358,1020,479],{"class":381},[358,1022,1023],{"class":364},"Mailer\n",[358,1025,1026],{"class":360,"line":515},[358,1027,1028],{"class":511},"    \u002F\u002F gpsystem:dependencies\n",[358,1030,1031],{"class":360,"line":532},[358,1032,555],{"class":381},[404,1034,1035,1037,1038,1041,1042,1045,1046,1049,1050,1053,1054,1057,1058,1061],{},[355,1036,643],{}," is constructed ",[431,1039,1040],{},"once"," per entry point and shared across every module that has a notifier, the same pattern as the shared database pool. ",[355,1043,1044],{},"smtp.MustNewIfConfigured"," builds a real SMTP client when ",[355,1047,1048],{},"MAIL_HOST"," is set, or ",[355,1051,1052],{},"mail.NewDiscard()"," when it's empty, so dev environments run without a mail server configured. See ",[416,1055,938],{"href":1056},"\u002Fen\u002Fstorage-mail\u002Fmail#smtp"," for the full ",[355,1059,1060],{},"MAIL_*"," reference.",[459,1063,1065],{"id":1064},"sending-it","Sending it",[404,1067,1068,1069,1073,1074,1080],{},"Nothing calls the notifier automatically: wire it from a service (synchronous mail, e.g. a password reset) or a listener (asynchronous, via ",[416,1070,1071],{"href":868},[355,1072,871],{}," \u002F ",[416,1075,1077],{"href":1076},"\u002Fen\u002Fcli\u002Fworker-generators#add-listener",[355,1078,1079],{},"add listener","):",[348,1082,1085],{"className":486,"code":1083,"filename":1084,"language":365,"meta":353,"style":353},"notifier := contactmail.NewContactNotificationNotifier(deps.Mailer)\nreturn notifier.Notify(ctx, recipientAddress, contactmail.ContactNotification{ \u002F* ... *\u002F })\n","internal\u002Fmodules\u002Fcontact\u002Flisteners\u002Fsendcontactemail.go",[355,1086,1087,1113],{"__ignoreMap":353},[358,1088,1089,1092,1094,1097,1099,1102,1104,1107,1109,1111],{"class":360,"line":361},[358,1090,1091],{"class":388},"notifier ",[358,1093,728],{"class":381},[358,1095,1096],{"class":388}," contactmail",[358,1098,479],{"class":381},[358,1100,1101],{"class":599},"NewContactNotificationNotifier",[358,1103,603],{"class":381},[358,1105,1106],{"class":388},"deps",[358,1108,479],{"class":381},[358,1110,582],{"class":388},[358,1112,853],{"class":381},[358,1114,1115,1118,1121,1123,1126,1128,1130,1132,1135,1137,1139,1141,1144,1146,1149],{"class":360,"line":501},[358,1116,1117],{"class":631},"return",[358,1119,1120],{"class":388}," notifier",[358,1122,479],{"class":381},[358,1124,1125],{"class":599},"Notify",[358,1127,603],{"class":381},[358,1129,687],{"class":388},[358,1131,698],{"class":381},[358,1133,1134],{"class":388}," recipientAddress",[358,1136,698],{"class":381},[358,1138,1096],{"class":364},[358,1140,479],{"class":381},[358,1142,1143],{"class":364},"ContactNotification",[358,1145,640],{"class":381},[358,1147,1148],{"class":511}," \u002F* ... *\u002F",[358,1150,1151],{"class":381}," })\n",[459,1153,1155],{"id":1154},"older-projects","Older projects",[404,1157,1158,1159,1163,1164,1166,1167,1163,1170,1173,1174,1163,1177,1180,1181,1184],{},"Like ",[416,1160,1161],{"href":276},[355,1162,275],{},", ",[355,1165,285],{}," writes at the ",[355,1168,1169],{},"gpsystem:pools",[355,1171,1172],{},"gpsystem:dependencies-init","\u002F",[355,1175,1176],{},"gpsystem:deps-init(\u003Cmodule>)",[355,1178,1179],{},"gpsystem:dependencies"," and ",[355,1182,1183],{},"gpsystem:config"," anchors; a project generated before those anchors existed needs them added by hand first (see a freshly generated project for placement).",[459,1186,1188],{"id":1187},"which-command-to-use","Which command to use",[404,1190,1191,1192,1196,1197,1200],{},"Content sent straight to an address, with no recipient concept (this page). Content addressed to a recipient, possibly on several channels at once (database inbox, live push): ",[416,1193,1194],{"href":291},[355,1195,290],{}," instead. The two compose: a notification's ",[355,1198,1199],{},"ToMail"," reuses the same builders shown above.",[1202,1203,1204],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}",{"title":353,"searchDepth":501,"depth":501,"links":1206},[1207,1208,1209,1210,1211],{"id":461,"depth":501,"text":462},{"id":482,"depth":501,"text":483},{"id":1064,"depth":501,"text":1065},{"id":1154,"depth":501,"text":1155},{"id":1187,"depth":501,"text":1188},"Generate a module-owned mail notifier: payload, templates, and the shared transport wiring.","md",null,{},{"icon":288},{"title":285,"description":1212},"SiO5zGSmEMveCffQtvJeOEc7nOnv9uSRs-rAuzxMqUE",[1220,1222],{"title":280,"path":281,"stem":282,"description":1221,"icon":283,"children":-1},"Regenerate compose.yml, or just the Dockerfile\u002F.dockerignore, standalone into an existing project.",{"title":290,"path":291,"stem":292,"description":1223,"icon":293,"children":-1},"Generate a module-owned, multi-channel notification: payload, Via, per-channel content, and the shared Hub wiring.",1784668711267]