HedgeDoc MotD 功能全解析:从 motd.md 配置到前端展示与缓存机制
后端前端云原生【免费下载链接】hedgedocHedgeDoc - Ideas grow better together项目地址https://gitcode.com/gh_mirrors/he/hedgedoc点击查看免费下载HedgeDoc 内置了经典的MotDMessage of the Day每日消息功能用于在用户打开实例时展示一条可定制的公告。本指南以仓库中 frontend/public/public/motd.md与 backend/public/motd.md 内容一致为核心入口完整讲解 MotD 文件的默认内容、后端静态服务的提供方式、前端获取与缓存去重逻辑、Markdown 渲染管线以及覆盖该功能的端到端测试让读者既能直接上手自定义公告也能理解其底层工作原理。一、MotD 文件默认内容与文件定位motd.md是 HedgeDoc 中 MotD 消息的载体文件当前仓库中前后端各保留了一份完全相同的副本默认内容只有两行This is the test motd text :smile:第一行是一段普通的纯文本公告内容语义上等价于“这是一条测试用 MotD 文本”第二行的:smile:是 GitHub 风格的 emoji 短码表明 MotD 正文以完整的 Markdown 语法解析emoji 短码会被渲染为对应的表情符号。两份文件分别位于后端静态资源目录backend/public/motd.md前端公共资源目录frontend/public/public/motd.md从源码结构看真正对外提供服务的版本是后端目录中的那份后端通过 Fastify 静态文件插件把public文件夹整体映射到/public/URL 前缀详见下文而前端目录中的副本主要用于 Next.js 构建期的本地化场景。默认内容即为测试文本实例管理员在实际部署时应将其替换为自己的公告内容。二、后端如何提供 motd.md静态文件服务挂载HedgeDoc 后端NestJS Fastify在应用启动装配阶段注册静态文件服务相关实现位于 backend/src/app-init.tslogger.log(Serving the local folder public under /public, AppBootstrap); const path await import(path); await app.register(import(fastify/static), { root: path.resolve(public), prefix: /public/, decorateReply: false, });关键配置点root指向后端工作目录下的public文件夹motd.md即位于该目录内prefix/public/因此motd.md的最终访问 URL 为http(s)://实例地址/public/motd.mddecorateReply: false避免 Fastify 静态插件覆盖 reply 对象上的默认装饰。代码注释中还保留了开发团队的说明目前public文件夹的主要使用场景就是intro.md与motd.md两个文件未来也可能考虑将其改为 API 端点见 backend/src/app-init.ts 的 TODO 注释。得益于fastify/static对静态文件的处理浏览器请求该 URL 时响应头会自动携带Last-Modified或etag标识这为前端判断“MotD 是否更新过”提供了依据。三、前端获取 MotDfetch-motd 与变更检测前端通过 frontend/src/components/global-dialogs/motd-modal/fetch-motd.ts 完成 MotD 的拉取。其核心逻辑如下export interface MotdApiResponse { motdText: string lastModified: string } export const fetchMotd async (baseUrl: string): PromiseMotdApiResponse | undefined { if (isBuildTime) { return } const motdUrl ${baseUrl}public/motd.md const response await fetch(motdUrl, { ...defaultConfig, cache: undefined, next: { revalidate: 60 } }) if (response.status ! 200) { return } const lastModified response.headers.get(Last-Modified) || response.headers.get(etag) if (lastModified null) { return } return { lastModified, motdText: await response.text() } }可以提炼出如下要点请求地址${baseUrl}public/motd.mdbaseUrl为后端实例地址与后端/public/前缀一一对应构建期短路isBuildTime为真时直接返回undefined避免在静态构建阶段发起网络请求缓存策略显式设置cache: undefined并配合next: { revalidate: 60 }Next.js 增量静态再验证60 秒兼顾实时性与缓存开销变更标识从响应头中优先读取Last-Modified缺失时回退到etag两者皆无则不返回数据响应契约最终返回{ motdText, lastModified }其中motdText为原始 Markdown 文本lastModified供后续去重判断使用。获取结果通过 frontend/src/components/motd/motd-context.tsx 中定义的MotdProvider与useMotdContextValue注入全局 React Context供模态框与 About 页面共享。四、展示与缓存去重CachedMotdModal 的工作机制MotD 弹窗的“只在内容变化时展示一次”行为由 frontend/src/components/global-dialogs/motd-modal/cached-motd-modal.tsx 实现其关键逻辑const [cachedLastModified, saveLocalStorage] useLocalStoragestring(MOTD_LOCAL_STORAGE_KEY, undefined, { raw: true }) const show useMemo(() { const lastModified contextValue?.lastModified if (cachedLastModified IGNORE_MOTD isTestMode) { return false } if (cachedLastModified lastModified || lastModified undefined) { return false } return !dismissed }, [cachedLastModified, contextValue?.lastModified, dismissed])判断流程若localStorage中缓存的上次修改标识与当前lastModified相同说明 MotD 未更新弹窗不展示若后端未返回lastModified同样不展示测试模式下可通过特殊值IGNORE_MOTD强制屏蔽弹窗。用户点击“Dismiss关闭”后doDismiss会将当前lastModified写入 localStorage此后即使刷新页面也不再弹窗直到管理员修改了motd.md导致Last-Modified变化。相关存储键定义在 frontend/src/components/global-dialogs/motd-modal/local-storage-keys.tsexport const MOTD_LOCAL_STORAGE_KEY: string motd.lastModified export const IGNORE_MOTD: string IGNORE_MOTD弹窗本体由 frontend/src/components/global-dialogs/motd-modal/motd-modal.tsx 渲染使用CommonModal承载标题引用 i18n 键motd.title正文区包裹EditorToRendererCommunicatorContextProvider后渲染MotdContent底部提供common.dismiss翻译键对应的成功样式按钮同时只有motdText非空时才真正显示show (contextValue?.motdText.length ?? 0) 0。五、Markdown 渲染MotdContent 与 RendererIframeMotD 之所以支持 Markdown 与 emoji 短码是因为它复用了 HedgeDoc 的渲染器。核心组件 frontend/src/components/motd/motd-content.tsx 的实现const lines useMemo(() { const rawLines contextValue?.motdText.split(\n) if (rawLines undefined || rawLines.length 0) { return [] } return rawLines }, [contextValue?.motdText]) return ( RendererIframe frameClasses{w-100} rendererType{RendererType.SIMPLE} markdownContentLines{lines} adaptFrameHeightToContent{true} showWaitSpinner{true} / )要点将motdText按\n拆分为行数组传入RendererIframerendererType使用RendererType.SIMPLE即简化渲染模式adaptFrameHeightToContent{true}让 iframe 高度自适应内容避免出现滚动条渲染在 iframe 中完成正文区域加载时显示等待指示器。这意味着用户可以在motd.md中自由使用标题、列表、链接、粗体等 Markdown 语法包括:smile:这类 emoji 短码其渲染能力与普通笔记保持一致。此外frontend/src/components/about-page/motd-card.tsx 在“关于About”页面中以卡片形式复用了同一MotdContent让管理员和用户在不打开弹窗时也能查看当前公告。六、测试验证从单元测试到端到端MotD 功能拥有完整的测试覆盖可作为理解其行为契约的权威参考端到端测试frontend/cypress/e2e/motd.spec.ts 验证了完整用户旅程const motdMockHtml This is the test motd text describe(Motd, () { it(shows, dismisses and wont show again a motd modal, () { window.localStorage.removeItem(MOTD_LOCAL_STORAGE_KEY) cy.visitHistory() cy.getSimpleRendererBody().should(contain.text, motdMockHtml) cy.getByCypressId(motd-dismiss).click() cy.getByCypressId(motd-modal).should(not.exist) cy.reload() cy.get(main).should(exist) cy.getByCypressId(motd-modal).should(not.exist) }) })该用例完整覆盖了“首次访问显示弹窗 → 点击关闭 → 弹窗消失 → 刷新后不再显示”的完整流程与CachedMotdModal中基于lastModified的去重逻辑相互印证。测试断言正文中包含This is the test motd text与默认motd.md的测试文本完全对应。单元测试frontend/src/components/global-dialogs/motd-modal/fetch-motd.spec.ts 则针对请求地址拼接、响应状态码判断、Last-Modified/etag读取等分支进行验证。七、运维实践如何自定义你的 MotD综合以上机制实例管理员自定义公告的操作路径非常清晰修改文件编辑后端目录下的 backend/public/motd.md替换默认的测试文本支持完整的 Markdown 语法与 emoji 短码重新部署由于静态文件由后端进程直接读取修改后需要重新构建/重启后端容器参考 backend/docker/Dockerfile 与根目录 README.md 中的部署说明或根据部署方式挂载该文件为外部卷生效与去重用户端会在请求时通过响应头Last-Modified/etag感知到文件变更即使老用户已关闭过旧公告也会因为标识变化而再次看到新内容测试模式前端测试环境下可通过 localStorage 特殊值IGNORE_MOTD屏蔽弹窗不影响开发调试。结语HedgeDoc 的 MotD 功能虽然入口文件只有寥寥两行但其背后是一套完整的“静态文件服务 → 前端拉取 → Context 分发 → localStorage 去重 → iframe Markdown 渲染”链路。理解 motd.md 与 backend/src/app-init.ts、fetch-motd.ts、cached-motd-modal.tsx 等实现之间的协作关系既能帮助管理员快速定制公告也能为二次开发提供清晰的切入点。赞分享后端前端云原生【免费下载链接】hedgedocHedgeDoc - Ideas grow better together项目地址https://gitcode.com/gh_mirrors/he/hedgedoc点击查看免费下载相关推荐Ceph Dashboard MOTDMessage of the Day插件配置、过期机制与前端展示完全指南Ceph Dashboard MOTDMessage of the Day插件配置、过期机制与前端展示完全指南 导读 Ceph Dashboard 的 M存储分布式文件系统对象存储后端高可用Instructor 原生缓存机制全解析从 AutoCache 到自定义缓存后端的零配置性能优化Instructor 原生缓存机制全解析从 AutoCache 到自定义缓存后端的零配置性能优化 Instructor 从 v1.9.1 起内置了覆盖所有 P人工智能大模型AI 应用HedgeDoc 前端 Changelog 解读从 HedgeDoc 1 到 2 的功能演进、弃用与迁移指南HedgeDoc 前端 Changelog 解读从 HedgeDoc 1 到 2 的功能演进、弃用与迁移指南 本指南以 frontend/CHANGELOG.后端前端云原生上一篇Winhance界面全导览软件、优化、自定义三大模块一文看懂下一篇React 360 SEO终极指南让你的VR内容轻松被搜索引擎收录创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Lona Compiler 使用指南:用 lonac 命令行工具从设计系统 JSON 生成跨平台 UI 代码

Lona Compiler 使用指南:用 lonac 命令行工具从设计系统 JSON 生成跨平台 UI 代码

设计系统前端开发工具UI组件 【免费下载链接】Lona A tool for defining design systems and using them to generate cross-platform UI code, Sketch files, and other artifacts. 项目地址: https://gitcode.com/gh_mirrors/lo/Lona 点击查看 免费下载 Lona Com…

2026/9/30 4:20:44 阅读更多 →
如何搭建虚拟商品发卡系统:0到1的完整开店指南

如何搭建虚拟商品发卡系统:0到1的完整开店指南

如何搭建虚拟商品发卡系统:0到1的完整开店指南 【免费下载链接】acg-faka 个人发卡源码,发卡系统,二次元发卡系统,二次元发卡源码,发卡程序,动漫发卡,PHP发卡源码,异次元发卡 项目…

2026/9/30 14:33:28 阅读更多 →
Forge 工具调用失败重试提示模板解析:从错误追踪到 Agent 自愈闭环

Forge 工具调用失败重试提示模板解析:从错误追踪到 Agent 自愈闭环

人工智能AI Agent代码智能体AI 应用CLI开发工具 【免费下载链接】forgecode AI enabled pair programmer for Claude, GPT, O Series, Grok, Deepseek, Gemini and 300 models 项目地址: https://gitcode.com/gh_mirrors/forge39/forgecode 点击查看 免费下载 在 F…

2026/9/30 10:45:34 阅读更多 →

最新新闻

月薪三万的Python开发者,每天都在用什么库

月薪三万的Python开发者,每天都在用什么库

打开招聘网站,Python高级开发工程师的月薪普遍在2.5万到3万之间,AI应用方向甚至更高。高薪背后,不是会写更多语法,而是技术选型比别人更精准。月薪三万的Python开发者,每天都在用这些库。AI应用开发:LangCh…

2026/9/30 14:48:47 阅读更多 →
Telegram AI 翻译客服机器人源码搭建与避坑指南

Telegram AI 翻译客服机器人源码搭建与避坑指南

简介:这是一套面向Telegram平台运营者与客服系统开发者的AI全自动翻译客服机器人源码,重点解决跨语言客户沟通中的实时翻译与本地化表达问题。机器人支持双向翻译,可将客户消息自动转换为客服预设语言,也能把客服回复翻译成符合客…

2026/9/30 14:48:47 阅读更多 →
数据结构与算法刷题全攻略:两遍刷题法真正掌握笔试算法

数据结构与算法刷题全攻略:两遍刷题法真正掌握笔试算法

简介:面向备战大厂算法面试的求职者与在校生,这份压缩包是一份体系化的数据结构与算法刷题代码合集,覆盖剑指Offer题解、程序员代码面试指南、九章算法、牛客直通BAT课程及lintcode/大公司笔试真题编程题。资源同时收录第一遍学习代码和两个月…

2026/9/30 14:48:47 阅读更多 →
Android Studio 2021.2.1.10 Windows离线包部署与避坑指南

Android Studio 2021.2.1.10 Windows离线包部署与避坑指南

简介:Android Studio Chipmunk(2021.2.1)Beta 3 的 Windows 版安装包,面向需要在 Windows 平台搭建 Android 开发环境的移动开发者、学生与教学人员。作为 2021.2.1 分支的花栗鼠版本,它介于 Bumblebee 与 Dolphin 之间…

2026/9/30 14:48:47 阅读更多 →
零到全栈(无状态的 Web,怎么记住一个人)

零到全栈(无状态的 Web,怎么记住一个人)

上一篇完成了一次教科书式的两步走:先把存储代码从 main.py 原样搬进 storage.py,把 "取几条” 的决定权交还给调用方;再把存储实现整个换成 SQLite——建表、INSERT、一句 SELECT 加索引,接口约定纹丝不动,前端毫…

2026/9/30 14:47:46 阅读更多 →
大功率户外电源精品定制、长续航款生产厂家质量参考评选

大功率户外电源精品定制、长续航款生产厂家质量参考评选

中山市鑫耀电子有限公司,是一家专注储能产品研发智造,面向全球客户提供一站式储能解决方案与柔性合作服务的源头生产企业,我们的精准定位是为海内外贸易商、品牌商、能源企业打造稳定可靠的储能产品供应链,助力客户开拓全球新能源…

2026/9/30 14:46:45 阅读更多 →

日新闻

Base64 图片头部特征识别:从文件头到格式判断的完整指南

Base64 图片头部特征识别:从文件头到格式判断的完整指南

1. 项目概述:为什么说看懂 base64 图片头部是基本功这几年跟 base64 打交道的机会越来越多,后端接口返回图片、前端渲染验证码、小程序里存小图、还有一些老系统导出报表,动不动就给你一段长到怀疑人生的 base64 字符串。很多人拿到字符串就直…

2026/9/30 0:00:35 阅读更多 →
Java公交站牌广告管理系统:JSP+Servlet+MySQL实战落地指南

Java公交站牌广告管理系统:JSP+Servlet+MySQL实战落地指南

简介:本资源是一份面向Java初学者与课程设计学生的公交站牌广告灯箱管理系统毕业设计文档,聚焦城市公共广告资源信息化管理痛点,提供从需求分析到技术实现的完整方案。文档采用标准学术论文结构,含摘要、英文摘要、目录及五章正文…

2026/9/30 0:00:35 阅读更多 →
用 Redis Lua 构建大模型 API 多租户原子配额治理体系

用 Redis Lua 构建大模型 API 多租户原子配额治理体系

我去年年底接了一个内部 AI 平台的治理需求,背景很直接:公司把 DeepSeek、MiniMax 这类大模型 API 统一封装成内部网关,开放给几个业务团队用。结果第一个月账单出来,额度直接超了 4 倍。仔细查日志,发现原因并不复杂—…

2026/9/30 0:00:35 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/30 13:14:22 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/29 16:41:41 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/9/30 13:14:49 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/29 19:29:29 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/29 5:58:00 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/29 3:55:56 阅读更多 →