listmonk 开发者环境搭建指南:Go 后端 + Vue 前端的双进程开发模式与生产构建
2026/9/15 1:20:19 网站建设 项目流程

listmonk 开发者环境搭建指南:Go 后端 + Vue 前端的双进程开发模式与生产构建

【免费下载链接】listmonkHigh performance, self-hosted, newsletter and mailing list manager with a modern dashboard. Single binary app.项目地址: https://gitcode.com/GitHub_Trending/li/listmonk

导读

listmonk 是一个高吞吐、自托管的新闻通讯与邮件列表管理工具,整体采用"Go 后端 + Vue 前端"的双组件架构。开发者环境与生产环境的关键差异在于:开发模式下前后端各自独立运行、支持热重载,而生产环境则通过单二进制文件将所有静态资源内嵌交付。本篇指南基于仓库内 developer-setup.md 展开,覆盖前置依赖、首次初始化、本地/容器化/DevContainer 三种开发运行方式,以及make dist的生产构建全流程,并结合 Makefile、dev/docker-compose.yml、config.toml.sample 与 cmd/init.go 源码逐层印证,帮助你从零搭建一个可热重载、可调试的 listmonk 开发环境,并理解其最终如何被打包为单个自包含二进制。

双组件架构:Go 后端与 Vue 前端为何要分开运行

listmonk 由两个相对独立的技术栈组成(见 developer-setup.md 开篇说明):

  • Go 后端:负责 HTTP API、数据库访问、邮件发送(SMTP/Postback)、弹回处理、批量导入、订阅者/列表/活动管理及计划任务等核心业务。入口为 cmd/main.go,通过 cmd/init.go 完成配置加载、数据库连接、SQL 查询预编译、消息器初始化与 HTTP 服务器装配。
  • Vue 前端:位于 frontend/,是基于 Vue 2 + Buefy(Bulma)的管理面板,通过/api/*与后端交互。另含独立的 email-builder(Vue 3 + TypeScript)邮件可视化编辑器,构建产物被拷贝进前端资源目录。

开发模式下两者独立运行,分别拥有自己的开发服务器、端口与热重载机制,互不阻塞。生产构建时二者合流:前端构建产物被内嵌进 Go 二进制,最终交付单文件应用。这也解释了为什么文档要求先分别启动两个进程,而非像生产环境那样直接运行一个二进制。

前置依赖

根据 developer-setup.md 的 Pre-requisites 章节,开发 listmonk 需要以下环境:

依赖用途备注
go编译并运行后端项目使用 go.mod,建议 clone 到 Go src 路径之外
nodejs+yarn编译并运行前端仅在参与前端开发时需要
PostgreSQL后端的数据存储本地无安装时,可用 demo DB 容器(docker compose up demo-db)替代

从 dev/app.Dockerfile 可看到仓库开发镜像锁定的版本基线:golang:1.24.1node:16,同时设置CGO_ENABLED=0。当前仓库 frontend/package.json 使用yarn@1.22.22(packageManager 字段固定),依赖 Vue^2.7.14、Buefy^0.9.25、vite^5.4.21与 vuex/vue-router/vue-i18n 等;node 版本若与本仓库开发期基线差异过大(尤其是 Node 16 这类 EOL 版本),可能出现依赖安装或构建失败,建议优先以容器化方式开发以获得一致的构建环境。

关于 Postgres 连接参数的快速参考

config.toml.sample 给出了后端默认的数据库配置段,这正是开发环境需要准备的外部依赖:

[db] host = "localhost" port = 5432 user = "listmonk" password = "listmonk" # Ensure that this database has been created in Postgres. database = "listmonk" ssl_mode = "disable" max_open = 25 max_idle = 25 max_lifetime = "300s" # Optional space separated Postgres DSN params. eg: "application_name=listmonk gssencmode=disable" params = ""

从 cmd/init.go 的initDB()实现可以看到,这些字段会被拼装为 Postgres DSN(host=... port=... user=... password=... dbname=... sslmode=...),并据此配置连接池:SetMaxOpenConns/SetMaxIdleConns/SetConnMaxLifetime一一对应max_openmax_idlemax_lifetimeparams则作为额外的空格分隔 DSN 参数原样追加(如application_name=listmonk gssencmode=disable)。数据库本身(database字段指定的库)必须预先创建--install只负责建表与写入种子数据,不会替你创建数据库。

首次初始化:克隆、配置、建库三步走

克隆仓库

文档给出的命令是:

git clone https://github.com/knadh/listmonk.git

项目使用 go.mod(见仓库根目录 go.mod),因此建议克隆到 Go src 路径之外,避免与 Go Modules 的目录约定冲突。当前仓库对应的克隆地址为https://gitcode.com/GitHub_Trending/li/listmonk

配置 config.toml

cp config.toml.sample config.toml # 编辑 config.toml,填入你自己的数据库等配置

config.toml.sample 只含两个段落:[app]address(默认localhost:9000,即开发服务器监听地址)与[db]的数据库连接。在容器化开发中,dev/config.toml 提供了另一套取值:address = "0.0.0.0:9000"db.host = "db"、用户/密码/库名均为listmonk-dev,与 dev 容器内的 Postgres 服务名对齐。

小技巧:若尚未生成配置文件,后端还支持./listmonk --new-config直接在--config指定的路径生成一份示例配置(见 cmd/init.go 与 cmd/init.go 的--new-config标志说明)。

首次构建与建库

make dist # 构建 listmonk 二进制 ./listmonk --install # 首次执行数据库安装(建表 + 种子数据)

--install交互式执行时会有提示并要求输入y确认;从 cmd/install.go 可以看到安装流程包括:读取并执行 SQL 建表(installSchema)、写入示例列表(installLists)、示例订阅者(installSubs)、示例模板(installTemplates)、示例活动(installCampaign),并可通过环境变量LISTMONK_ADMIN_USER/LISTMONK_ADMIN_PASSWORD(最少 3/8 字符)预置超级管理员;若未设置,则首次访问网页时引导创建。--install --idempotent变体会先检查settings表是否存在,已初始化则跳过,这在 docker-compose.yml 的容器入口命令(./listmonk --install --idempotent --yes ...)中被用来保证幂等启动。

后续开发运行不需要再走make dist,直接使用make run即可(二者区别见下文)。

提示:文档特别推荐 mailhog——一个带 Web UI 的独立 mock SMTP 服务器——用于开发与测试阶段的邮件收验。dev 容器套件中已内置 MailHog(SMTP:1025、UI:8025),本地开发也可自行启动并将 SMTP 配置指向它。

运行开发环境:本地、容器(Makefile)与 DevContainer 三种方式

文档提供了三种开发运行方式,运行成功后统一访问http://localhost:8080(前端管理面板)。

方式一:本地运行(推荐用于日常前后端开发)

make run # 启动后端 dev server,监听 :9000 make run-frontend # 启动 Vue 前端 dev server,监听 :8080,/api/* 代理到 :9000

两个命令对应 Makefile 与 Makefile 的两个 target:

  • make run:等价于CGO_ENABLED=0 go run -ldflags="... -X 'main.frontendDir=frontend/dist'" ./cmd。注意它通过main.frontendDir变量把frontendDir指向frontend/dist(cmd/main.go 的默认值正是frontend/dist),意味着即使在后端 dev 模式下,若前端目录已有构建产物,也会被当作静态资源加载。
  • make run-frontend:等价于在 frontend/ 目录执行yarn dev(即vite),由 Vite 提供热更新。

代理机制:查看 frontend/vite.config.js 的server.proxy配置,前端开发服务器会把以下路径代理到LISTMONK_API_URL || 'http://127.0.0.1:9000'

  • ^/$
  • ^/(api|webhooks|subscription|public|health)
  • ^/admin/login
  • ^/(admin\/custom\.(css|js))

也就是说,/api/*/webhooks/*/subscription/*/public/*/health以及登录页与管理面板自定义 CSS/JS 等请求都会转发到运行在:9000的 Go 后端;前端端口可用环境变量LISTMONK_FRONTEND_PORT覆盖(默认 8080)。这正是文档所述"所有/api/*调用被代理到 :9000 应用"的源码依据。

方式二:容器化运行(Makefile 驱动)

make init-dev-docker # 构建镜像并初始化容器化数据库 make dev-docker # 启动整套容器套件(前端 + 后端 + PG + MailHog + Adminer) make rm-dev-docker # 拆除整套容器(连同数据库卷一起删除)

三个 target 的实现见 Makefile:

  • init-dev-docker:先build-dev-docker(进入 dev/ 执行docker compose build),然后执行docker compose run --rm backend sh -c "make dist && ./listmonk --install --idempotent --yes --config dev/config.toml"——即在容器内完成构建 + 幂等建库,使用的是 dev/config.toml。
  • dev-docker:进入 dev/ 执行docker compose up,启动 dev/docker-compose.yml 定义的整套服务。
  • rm-dev-docker:执行docker compose down -v连同数据库数据卷一并清除,属于彻底清理。

从 dev/docker-compose.yml 可以看到套件包含 5 个服务:

服务端口说明
backend9000Go 后端,命令为make run-backend-docker,使用dev/config.toml,挂载整个仓库到容器
front8080Vue 前端,命令为make run-frontendLISTMONK_API_URL=http://backend:9000
db5432postgres:13,账号listmonk-dev,数据持久化于 volume
mailhog1025(SMTP) /8025(UI)开发邮件捕获
adminer8070数据库 Web 管理界面(访问容器内:8080

backendfront两个容器都挂载了宿主机整个仓库目录(../:/app),这正是 dev/README.md 所述的设计目标:避免每次代码变更都完整docker build,只需重启容器即可。dev/README 还指出,前端代码改动无需任何额外操作(yarn watch + 目录挂载自动生效),后端 Go 代码改动则需重新运行make dev-docker触发重新编译。另注意backend容器把宿主机 Go module 缓存挂载进容器(${GOPATH:-${HOME}/go}/pkg/mod/cache),可加速依赖拉取。

方式三:DevContainer(VS Code 远程开发)

文档步骤:在 VS Code 中打开仓库 → 命令面板(Command Palette)→ 选择"Dev Containers: Rebuild and Reopen in Container"。容器会自动完成数据库初始化并启动前后端服务。

补充说明:当前仓库(截至本仓库快照)并未在根目录包含.devcontainer/目录或docker compose up demo-db对应的一等服务定义(源码检索未发现devcontainer相关文件,demo-db服务也仅在本文档中被提及)。因此:

  • DevContainer 与demo-db属于官方文档描述的能力/历史用法,实际可用性取决于你克隆的上游仓库版本与 VS Code Dev Containers 扩展的远程容器探测逻辑;
  • 在当前仓库内,容器化开发的权威入口是 dev/docker-compose.yml 与 Makefile 的init-dev-docker/dev-docker流程,建议优先使用该路径;
  • 若确需 DevContainer,可在 VS Code 中直接使用"Rebuild and Reopen in Container"后观察其自动探测结果,或自行按 dev/app.Dockerfile 的基线(golang:1.24.1 + node:16)编写容器配置。

三种方式的取舍对比

维度本地容器(Makefile)DevContainer
依赖隔离依赖本机 Go/Node/Postgres完全隔离,环境一致性最好同容器,但配置在 IDE 内
启动速度最快(无容器开销)首次构建镜像较慢,之后仅重启容器首次构建镜像较慢
热重载后端需重编译,前端 yarn watch前端自动,后端需重启容器同容器
适用场景日常快速迭代,前端+后端联调环境统一/多人协作/不想污染本机偏好 IDE 内一体化开发

前端开发内部结构速览

文档指向了 frontend/README.md 作为前端结构的入门。要点如下(有助于在开发中快速定位):

  • 全局注入:frontend/src/main.js 中全局挂载 Buefy、vue-i18n,以及$api(来自 frontend/src/api/index.js 的 API 调用集合)与$utils(frontend/src/utils.js),组件内以this.$apithis.$utils访问;常量集中在 frontend/src/constants.js。
  • 全局状态:采用 Vuex 集中存储几乎所有 API 响应(models,定义在 frontend/src/store/index.js),并有全局loading状态(如loading.campaigns)供各组件显示 spinner。
  • 字段命名约定(重要):GET API 响应的 JSON 字段名会被自动 camelCase 化(如content_typecontentType),而向后端发送时需手动 snake_case;例外是/api/config/api/settings等调用,通过api/index.js中的preserveCase: true保留原样。改动前端代码时务必遵守这一约定,否则字段名不匹配会导致联调异常。
  • 图标方案:Buefy 默认用 Material Design Icons(mdi-前缀),listmonk 只选取少量图标打包为 web font(Fontello)。需要新增图标时,将 frontend/fontello/config.json 拖入 Fontello 官网选择图标,下载后将config.jsonfontello.woff2css/fontello.css分别覆盖回 frontend/fontello、frontend/src/assets/icons/。

生产构建:make dist产出单文件二进制

make dist是文档给出的生产构建命令,其含义(见 Makefile)是依次执行buildbuild-frontendpack-bin三个步骤:

  1. build(Makefile):CGO_ENABLED=0 go build -o listmonk -ldflags="-s -w -X 'main.buildString=...' -X 'main.versionString=...'" ./cmd-ldflags注入版本与构建信息;CGO_ENABLED=0产出纯静态二进制。版本号解析逻辑在 Makefile:优先取 git describe 标签,其次 VERSION 文件,最后回退v0.0.0
  2. build-frontend(Makefile):先后构建 email-builder(yarn build,产物frontend/email-builder/dist拷入frontend/public/static/email-builder)与主前端(frontend目录内yarn build,产物 frontend/dist)。构建前还会先执行依赖安装(yarn install),产物跟随 frontend/package.json 的prebuild跑一次 ESLint 检查。
  3. pack-bin(Makefile):调用 stuffbin 将静态资源"塞"进二进制——即 Makefile 定义的STATIC清单:config.toml.sampleschema.sqlqueries/permissions.jsonstatic/public → /publicstatic/email-templatesfrontend/dist → /admini18n/ → /i18n

运行时 cmd/init.go 的initFS()会先尝试从可执行文件解出内嵌文件系统(stuffbinUnStuff);解不出时(例如直接用go run开发运行)才回退到本地文件系统,从appDirfrontendDir及用户指定的static-dir/i18n-dir按需加载静态资源——这正是make runmake dist产物行为差异的底层原因。最终listmonk是一个自包含二进制:内置数据库 schema、SQL 查询、Go 后端、Vue 管理面板与邮件模板,可单独部署,无需附带任何静态目录(--install首次初始化时从内嵌 FS 读 schema 与种子数据)。

常用开发命令速查表

命令作用对应实现
make run启动 Go 后端 dev server(:9000Makefile
make run-frontend启动 Vue 前端 dev server(:8080,API 代理到 9000)Makefile
make init-dev-docker构建开发镜像 + 幂等初始化容器内数据库Makefile
make dev-docker启动整套开发容器(front/backend/PG/MailHog/Adminer)Makefile
make rm-dev-docker拆除容器并删除数据库卷Makefile
make dist生产构建:Go 二进制 + 前端构建 + stuffbin 内嵌静态资源Makefile
make test运行全部 Go 测试(go test ./...Makefile
./listmonk --install首次数据库初始化(建表 + 种子数据)cmd/install.go
./listmonk --install --idempotent --yes幂等建库,用于脚本/容器首次启动cmd/install.go
./listmonk --new-config--config指定路径生成示例配置cmd/init.go
./listmonk --version显示构建版本信息cmd/main.go

小结

listmonk 的开发环境搭建核心在于理解"前后端分离、按需合流"的运行模型:开发期用make run+make run-frontend双进程协作(或容器化的make init-dev-docker+make dev-docker),借助 Vite 代理打通:8080:9000;交付期用make dist把 Vue 构建产物经 stuffbin 内嵌进 Go 二进制,得到单一自包含可执行文件。配置上只需准备一份config.toml与一个可用的 PostgreSQL 实例(容器套件已替你备好)。开发过程中遇到问题,可参考 dev/README.md(容器套件说明)与 frontend/README.md(前端结构与命名约定),并善用make test验证后端改动。

【免费下载链接】listmonkHigh performance, self-hosted, newsletter and mailing list manager with a modern dashboard. Single binary app.项目地址: https://gitcode.com/GitHub_Trending/li/listmonk

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询