ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

OpenCloud notifications 服务详解:邮件模板、分组邮件发送与翻译机制

OpenCloud notifications 服务详解:邮件模板、分组邮件发送与翻译机制 OpenCloud notifications 服务详解邮件模板、分组邮件发送与翻译机制【免费下载链接】opencloud️ OpenCloud is the open source platform for file management, sharing and collaboration. Simple and sovereign.项目地址: https://gitcode.com/GitHub_Trending/op/opencloudOpenCloud 的notifications服务负责向用户发送邮件告知其账号上发生的分享、空间变更等事件。它挂接到 OpenCloud 的事件系统event system上监听特定事件并决定立即发信或按“每日/每周”分组聚合后发信。读完本文你将掌握该服务的邮件模板体系内置与自定义模板、分组邮件的存储与触发机制opencloud notifications send-email命令、可用的持久化后端及其 TTL 行为以及内置/自定义翻译与语言回退规则从而能够完整配置一个多语言、可水平扩展的邮件通知链路。服务定位挂载在事件总线上的通知网关服务说明开篇即定义notification service 负责“向用户发送邮件告知其发生了哪些事件”其实现方式是挂接到事件系统并监听需要通知用户的事件。从源码结构看该服务通过 NATS 事件总线接收ShareCreated、SpaceShared等事件job.go 中可见对这些事件类型的逐个处理再通过 channels 定义的邮件消息结构经 SMTP 发出。服务自身的默认配置定义在 defaultconfig.go 中几个值得注意的默认值Store.Store默认nats-js-kvDatabase为notificationsTTL为336h两周SMTP.Encryption默认noneWebUIURL默认https://localhost:9200用于在邮件中生成指向 Web UI 的链接事件端点默认127.0.0.1:9233NATS。完整的可配置环境变量含 SMTP 主机、端口、认证方式、事件总线 TLS 等均可在 config.go 中通过env标签查证例如NOTIFICATIONS_SMTP_HOST、NOTIFICATIONS_SMTP_AUTHENTICATION取值login/plain/crammd5/none/auto、NOTIFICATIONS_SMTP_ENCRYPTION取值starttls/ssltls/none等。邮件通知模板内置模板与三个占位符notifications服务内置了纯文本text和 HTML 两种邮件正文模板通过//go:embed templates嵌入到二进制中见 email.go 中的templatesFS因此“内置模板对所有部署场景都可用”。模板内提供三个占位符发送时它们会被替换为对应目的地的翻译字符串占位符含义{{ .Greeting }}问候语如“Hello {ShareGrantee},”{{ .MessageBody }}邮件正文主体{{ .CallToAction }}行动号召通常包含链接邮件**主题Subject**同样来自翻译字符串但它是邮件的必备组成部分因此不包含占位符。README 中用一个 ASCII 图描述了这层解析链这里原样保留其含义template placeholders translated strings -- source strings -- purpose final output即最终输出 模板 ← 占位符 ← 翻译后的字符串 ← 源字符串 ← 用途purpose。每个用途purpose都有各自独立的可翻译字符串最终通过占位符注入模板。从 templates.go 可以看到当前定义的全部用途每个用途对应一个MessageTemplate结构含Subject、Greeting、MessageBody、CallToAction四个字段分享类ShareCreated、ShareRemoved、ShareExpired空间类SharedSpace、UnsharedSpace、MembershipExpired联邦协作类ScienceMeshInviteTokenGenerated、ScienceMeshInviteTokenGeneratedWithoutShareLink提及类Mention分组邮件Grouped其MessageBody为空字符串运行时由多个子模板拼接生成见下文。翻译源字符串中还嵌入了更细粒度的变量如{ShareSharer}、{ShareFolder}、{SpaceName}、{ShareLink}、{ExpiredAt}等。这些{Var}记法与 Go 模板语法不同是翻译友好的中间层composer.go 中的replacePlaceholders会将它们批量替换为{{ .Var }}形式映射关系见 templates.go 的_placeholders表再由composeMessage用text/template解析执行实现“翻译 → 变量注入”的两段式渲染。自定义模板除了内置模板服务还支持自定义邮件模板且自定义模板优先于内置模板——只要存在自定义模板内置模板即被完全绕过而不是部分覆盖。配置方式为将NOTIFICATIONS_EMAIL_TEMPLATE_PATH环境变量指向一个基础文件夹该路径必须对所有 notifications 服务实例可见README 建议放置在共享存储上。目录结构必须严格遵循{NOTIFICATIONS_EMAIL_TEMPLATE_PATH}/templates/text/email.text.tmpl {NOTIFICATIONS_EMAIL_TEMPLATE_PATH}/templates/html/email.html.tmpl {NOTIFICATIONS_EMAIL_TEMPLATE_PATH}/templates/html/img/对应的子目录层级README 原文结构图templates │ └───html │ │ email.html.tmpl │ │ │ └───img │ │ logo-mail.gif │ └───text │ email.text.tmpl自定义模板必须位于templates/text和templates/html子目录中且文件名必须与内置模板一致。OpenCloud 提供的可作为派生起点的源模板位于 templates 基础目录包括text/email.text.tmplhtml/email.html.tmpl关于 HTML 模板中的图片README 给出两种方式外链托管图片用标准 HTML 代码引用如img srchttps://example.com/logo-mail.gif altlogo-mail/CID 内嵌图片写成img srccid:logo-mail.gif altlogo-mail/此时图片文件必须放在templates/html/img子目录下支持的内嵌图片类型为 png、jpeg 和 gif。注意通过 CID 资源内嵌图片在部分邮件客户端中可能不被完全支持。这一 CID 机制在源码中有精确对应RenderEmailTemplate在 email.go 中仅当emailTemplatePath ! 时调用readImages读取templates/html/img目录且validateMime通过魔数\xff\xd8\xff为 jpeg、\x89PNG\r\n\x1a\n为 png、GIF87a/GIF89a为 gif验证文件确实是这三种图片类型之一验证不通过的文件会被跳过并作为AttachInline内联资源附加到邮件中。另外值得注意的是安全细节HTML 渲染前所有变量会经html.EscapeString转义escapeStringMap而CallToAction中的{ShareLink}/{ResourceLink}会被callToActionToHTML转换为a href锚点后再注入模板保证链接可点击且不破坏转义策略。发送分组邮件Grouped Emailsdaily / weekly 分组机制notifications服务支持基于事件存储中积累的事件发起分组邮件发送事件被归入daily或weekly两个桶。用户可以在 Web UI 的个人设置中配置“每日”或“每周”邮件通知如果用户没有定义任何分组通知周期则对应事件不会被存储——即没有设置分组的用户只会收到即时邮件。分组事件的保留时间由OC_PERSISTENT_STORE_TTL定义也可通过NOTIFICATIONS_STORE_TTL单独为 notifications 服务配置源码默认值为 336h见 defaultconfig.go。超过 TTL 的分组事件会被自动清除既不另行通知也不发送这一点在运维排障时尤其重要。从源码可以还原出完整的存取链路拆分用户splitter.go 中的intervalSplitter.execute通过 settings 服务的ValueService查询每个用户的邮件发送周期设置SettingUUIDProfileEmailSendingInterval把用户分成instant、daily、weekly三组查询失败时保守地归入instant立即发送。持久化事件 IDpersistence.go 中的userEventStore.persist以interval 用户OpaqueId作为键如dailyu123...值为包含用户信息与事件 ID 列表的 JSON注意源码注释明确提示该读写过程非线程安全高并发下可能丢失个别事件。触发与消费job.go 中sendGroupedEmailsJob按 interval 前缀listKeys随后为每个键并发启动createGroupedMailpop会取出记录、通过 eventhistory 服务的GetEvents拉取完整事件、然后删除该键发送即消费。渲染与发送createGroupedMail按事件类型SpaceShared、SpaceUnshared、SpaceMembershipExpired、ShareCreated、ShareExpired、ShareRemoved为每个事件挑选对应的MessageTemplate和变量最终调用RenderGroupedEmailTemplate将所有事件主体拼接进Grouped模板——纯文本以空行分隔HTML 以brbrbr分隔见 composer.go 中NewGroupedTextTemplate/NewGroupedHTMLTemplate。用户语言通过l10n.MustGetUserLocale从 settings 中读取配合默认语言做翻译。触发命令send-email分组邮件不是服务内部定时器自动发出的而是需要外部如 cron job触发。README 规定使用opencloud notifications send-email命令且必须至少指定--daily或--weekly中的一项两者也可以同时使用。命令实现见 send_email.go若两个 flag 都未设置命令直接报错at least one of --daily or --weekly must be set命令本身并不直接渲染邮件而是向 NATS 事件总线Publish一个events.SendEmailsEvent{Interval: daily}/{Interval: weekly}由运行中的服务实例消费该事件并执行上述sendGroupedEmailsJob流程。因此一个典型的 cron 配置示例示意为# 每天 06:00 发送每日分组邮件 0 6 * * * opencloud notifications send-email --daily # 每周一 06:00 发送每周分组邮件 0 6 * * 1 opencloud notifications send-email --daily --weekly存储后端NOTIFICATIONS_STORE与 TTL分组事件需要跨实例共享存储notifications服务通过NOTIFICATIONS_STORE或全局的OC_PERSISTENT_STORE配置后端。README 列出的支持类型及说明如下与 config.go 中Store结构的注释一致后端说明memory基础内存存储重启即丢失。不推荐用于本服务redis-sentinel存储到配置的 Redis Sentinel 集群nats-js-kv使用 NATS JetStream 的 key-value store 特性存储默认值noop不存储任何东西仅适合测试不推荐生产使用其他存储类型可能“碰巧”能用但当前不受支持README 同时提醒若使用了已废弃的存储类型应尽快迁移到受支持的类型因为废弃后端将在后续版本移除。两条横向扩展注意README 原文要点服务只有在不使用memory存储时才具备扩展能力且所有实例的存储配置必须完全一致后端专属配置redis-sentinelRedis master 通过OC_CACHE_STORE_NODES配置格式为sentinel-host:sentinel-port/redis-master例如10.10.0.200:26379/mymasternats-js-kv建议将OC_CACHE_STORE_NODES设置为与OC_EVENTS_ENDPOINT相同的值使缓存与事件总线复用同一 NATS 实例此时还可以通过OC_CACHE_DISABLE_PERSISTENCE指示 NATS 不把缓存数据落盘。Store结构还支持NOTIFICATIONS_STORE_DATABASE库名、NOTIFICATIONS_STORE_TABLE表名nats-js-kv场景即 KV bucket、NOTIFICATIONS_STORE_AUTH_USERNAME/NOTIFICATIONS_STORE_AUTH_PASSWORD仅nats-js-kv生效以及一组nats-js-kv专用的 TLS 选项NOTIFICATIONS_STORE_ENABLE_TLS、NOTIFICATIONS_STORE_TLS_INSECURE、NOTIFICATIONS_STORE_TLS_ROOT_CA_CERTIFICATE均可在 config.go 查证。翻译与默认语言内置翻译与自定义翻译notifications服务内嵌了一组经 transifex 渠道来源的翻译为所有部署场景提供基础的多语言能力。仓库中对应的内嵌语言文件位于 l10n/locale覆盖 ca、de、el、es、fi、fr、hu、it、ja、ko、lo、nl、no、pl、pt、ru、sv、zh 等语言每个语言遵循{语言码}/LC_MESSAGES/notifications.po的组织方式。服务额外支持自定义翻译但目前不支持把自定义翻译“叠加”到内置翻译之上——一旦配置了自定义翻译内置翻译即不再使用。配置方式为将NOTIFICATIONS_TRANSLATION_PATH或全局OC_TRANSLATION_PATH指向包含翻译文件的基础文件夹该路径必须对所有实例可见建议同样使用共享存储。翻译文件必须为.po或.mo类型每种语言的文件名必须为notifications.po或notifications.mo并放置在以语言码命名的目录结构中通用路径模式为{NOTIFICATIONS_TRANSLATION_PATH}/{language-code}/LC_MESSAGES/notifications.po语言码模式为language[_territory]其中language是基础语言_territory为可选的国家/地区限定。例如德语de需要把文件放在{NOTIFICATIONS_TRANSLATION_PATH}/de/LC_MESSAGES/notifications.po。语言回退规则README 给出的回退规则是若请求的语言码不可用服务先尝试回退到基础语言。例如请求de_DE不可用时尝试回退到de目录的翻译若基础语言de也不可用服务回退到系统默认英语en即代码中提供的源文本。这里有一个重要的现实约束README 特别以 Important 标注截至文档撰写时OpenCloud Web 前端仅请求主语言码而不处理 territory。也就是说即使你提供了de_DE的翻译前端只会请求de因此翻译必须存在于被请求的language不带 territory下否则会出现回退到默认语言的“翻译失效”现象。这条规则对多语言运维实践非常关键优先维护主语言码目录。默认语言默认语言通过OC_DEFAULT_LANGUAGE环境变量定义config.go 中的注释说明它是“服务与 WebUI 使用的默认语言未定义时以英语为默认”。README 提示可参阅settings服务的文档获取更详细的描述job.go中createGroupedMail在渲染分组邮件时即把该默认语言s.defaultLanguage与按用户解析出的 locale 一并传入模板渲染函数作为翻译回退链的终点。小结OpenCloud 的notifications服务是一个围绕事件总线的轻量邮件通知网关其可运维性体现在三个正交维度模板维度内置 text/html 双模板 三占位符{{ .Greeting }}/{{ .MessageBody }}/{{ .CallToAction }} CID 内嵌图片支持通过NOTIFICATIONS_EMAIL_TEMPLATE_PATH完整覆盖品牌化投递维度即时邮件与 daily/weekly 分组邮件并存分组事件按用户 settings 决定是否落存储经opencloud notifications send-email --daily/--weekly外部触发超 TTL默认 336h自动清除存储与语言维度默认nats-js-kv后端可复用事件总线 NATS 实例实现水平扩展配合内置 18 种语言的 gettext 翻译与language[_territory]回退规则满足多语言部署需求。如需继续深入建议直接阅读 pkg/email模板渲染、pkg/service事件处理与分组消费、pkg/config全量环境变量三个目录以及 测试用例 中针对模板渲染的验证方式。【免费下载链接】opencloud️ OpenCloud is the open source platform for file management, sharing and collaboration. Simple and sovereign.项目地址: https://gitcode.com/GitHub_Trending/op/opencloud创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表