Skip to content

从源码构建 ​

入口有两个,互为等价的薄壳,任选其一:

入口构建命令
仓库根 main.gogo build -o GoPanel .
cmd/gopanel/main.gogo build -o GoPanel ./cmd/gopanel

两者都只做转发,真正的启动编排在 internal/app,行为完全一致。

环境要求 ​

工具版本说明
Go≥ 1.26与 go.mod 的 go 指令一致
Node.js≥ 22构建前端 SPA;只构建后端时可不装
npm≥ 10仓库根也带 pnpm-lock.yaml,用 pnpm 亦可

国内网络建议先设模块代理:

bash
go env -w GOPROXY=https://goproxy.cn,direct

最短路径(三行) ​

bash
git clone https://cnb.cool/liumou_site/GoPanel.git
cd GoPanel

# 1. 构建前端(产物 frontend/management/dist —— 正是 go:embed 的目标,无需复制)
cd frontend/management && npm ci && npm run build && cd ../..

# 2. 构建后端(前端产物被 go:embed 原地打进二进制)
go build -o GoPanel .          # 等价写法:go build -o GoPanel ./cmd/gopanel

跑起来:

bash
./GoPanel              # 默认监听 12395(会去读 config/config.toml,缺失则用环境变量/内置默认值)
./GoPanel -version     # 只看版本号
./GoPanel -h           # 看全部参数

浏览器打开 http://localhost:12395/,未初始化时会直接跳转安装向导。

embed 在哪、为什么不用复制 ​

前端不是外挂目录,而是编译期嵌进二进制的:

go
// frontend/embed.go
//go:embed all:management/dist
var distFS embed.FS

关键点:go:embed 的路径是相对本包目录的,而本包就在 frontend/ 下, vite 的输出恰好也在 frontend/management/dist —— 于是嵌入目录 = 构建产物目录, 一条相对路径就够,npm run build 之后直接编译,中间不需要任何复制动作。

text
frontend/
├── embed.go            //go:embed all:management/dist
└── management/dist/    ← npm run build 的原始输出(真实产物不入库)

干净的 git 检出任然能 go build:.gitignore 只放行一个占位 index.html (/frontend/management/dist/* + !/frontend/management/dist/index.html), 保证 //go:embed 在目录为空时不会以 pattern all:management/dist: no matching files found 报错。

注意:嵌入的是你构建时那一刻的 dist。先 go build 再改前端,二进制里仍是旧页面 —— 改了前端记得重新编译。

读取规则:磁盘优先、embed 兜底 ​

  • 容器内发布形态:产物已嵌在二进制里,运行时不需要任何外挂文件
  • 本机源码运行:进程还能从磁盘 frontend/management/dist 读到刚构建的产物, 改完前端不必重编译后端(internal/server.WithAssetsDir,目录不存在则自动跳过)

为什么 embed 不放在根包 ​

go:embed 只能嵌入本包目录及其子目录,不允许 ..。internal/server 需要导入 SPA 处理器,若它反向导入根包就构成 import cycle。所以:

  • 嵌入放在 frontend/embed.go(只依赖 embed / io/fs,零第三方依赖)
  • HTTP 处理器放在 internal/handler/frontend(只依赖 gin)
  • 两个入口(根 main.go / cmd/gopanel/main.go)调用 frontend.Assets() 后 经 server.WithAssets(...) 注入

三方互不反向依赖,既无 ../、也无循环。

数据库支持与构建标签 ​

后端自带 GORM,四个数据库默认全部可用,无需额外标签:

mysql / mariadb / postgres / sqlite

SQLite 走纯 Go 驱动(github.com/glebarez/sqlite,不依赖 CGO), 所以 CGO_ENABLED=0 也能开箱用 SQLite。

只做本地调试时,可以完全不装数据库:

bash
GOPANEL_DB_TYPE=sqlite GOPANEL_DB_PATH=config/gopanel.db ./GoPanel

官方镜像(Dockerfile.backend 等)在编译时仍带 -tags db_sqlite, 这是为了兼容历史构建脚本,不影响源码构建的使用。

交叉编译 / 自己出发布包 ​

发布产物命名与安装脚本约定一致:GoPanel-linux-<arch>[-softfloat]。

bash
# amd64
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
  go build -tags db_sqlite -ldflags "-s -w" -o GoPanel-linux-amd64 .

# arm64
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 \
  go build -tags db_sqlite -ldflags "-s -w" -o GoPanel-linux-arm64 .

# armv7(32 位 arm 约定加 -softfloat 后缀)
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 \
  go build -tags db_sqlite -ldflags "-s -w" -o GoPanel-linux-arm-softfloat .

想让「面板里的版本号 / 检查更新」显示正确,可以在构建期注入:

bash
go build -o GoPanel . \
  -ldflags "-X cnb.cool/liumou.site/GoPanel/internal/bootstrap.build.Version=v1.0.0 \
            -X cnb.cool/liumou.site/GoPanel/internal/bootstrap.build.Commit=$(git rev-parse --short HEAD) \
            -X cnb.cool/liumou.site/GoPanel/internal/bootstrap.build.BuildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)"

运行期还可用 GOPANEL_VERSION / GOPANEL_COMMIT / GOPANEL_BUILD_TIME 覆盖(环境变量优先)。

注意事项 ​

  • 不要提交前端产物:frontend/management/dist/*(仅 index.html 占位入库) (除占位 index.html)都在 .gitignore 里
  • go build 请带 -o:裸跑 go build / go build ./... 会在仓库根留下一个 150MB+ 的无扩展名二进制,被 gvt 提交钩子的「无扩展名二进制」检查拦下
  • 只改后端、图快可以先跳过前端:沿用仓库里已有的 frontend/management/dist 产物直接编译
  • 前端构建报错时,先确认 Node 版本够新(node -v),依赖用 npm ci 而非 npm install

可选:用脚本一条命令跑通 ​

仓库自带本地调试脚本,把「前端构建 → 复制产物 → 交叉编译 → 起镜像 + 数据库」串起来:

bash
sh docker-compose/dev/build.sh                 # postgres + 构建 + 启动
sh docker-compose/dev/build.sh --db=mysql      # 换 MySQL / mariadb
sh docker-compose/dev/build.sh --no-start      # 只构建

产物落在 docker-compose/dev/artifacts/(GoPanel 二进制 + frontend-dist/)。

前端开发模式 ​

bash
cd frontend/management
npm ci
npm run dev        # http://localhost:3003

开发服务器把 /api、/captcha、/term、/server 代理到后端, 目标地址由 frontend/management/.env.development 的 VITE_API_PROXY_TARGET 控制 (默认 http://localhost:12395)。

质量检查:

bash
npm run typecheck  # tsc --noEmit
npm run lint       # oxlint --max-warnings 0
npm run format     # prettier --write

只想要 Docker 镜像 ​

不想在本机装 Go / Node,可以让 Docker 多阶段构建在镜像内完成全部编译:

bash
# 后端单体镜像(前端在镜像内构建并嵌入)
docker build -f Dockerfile.backend -t gopanel:local .

# 前端独立镜像(Nginx + /api 反代)
docker build -f Dockerfile.frontend -t gopanel-frontend:local .

# 单容器版
docker build -f Dockerfile.allinone -t gopanel-allinone:local .
docker build -f Dockerfile.nginx    -t gopanel-nginx:local .
文件用途
Dockerfile.backend后端单体镜像(多阶段,前端已嵌入)
Dockerfile.frontend前端独立 Nginx 镜像
Dockerfile.allinone单容器版(Caddy + GoPanel)
Dockerfile.nginx单容器版(Nginx + GoPanel)
Dockerfile.bin产出 Release 二进制附件(GoPanel-linux-<arch>)

本机装了 gvt 也可以用 gvt docker(配置见 .gvt/docker/,默认标签 dev)。

官网与文档站 ​

本官网(VitePress)源码在 frontend/vitepress/:

bash
cd frontend/vitepress
npm install
npm run dev      # 本地预览(默认 5173)
npm run build    # 产出 .vitepress/dist

相关 ​

基于 AGPL-3.0 协议发布