feat: 新增 Docker 容器化部署,支持 Docker Hub 镜像发布
Some checks failed
Docker / docker (push) Has been cancelled

多阶段 Dockerfile、Compose、/health 探活与 JIANG13_* 环境变量;
entrypoint 自动修正 /data 卷权限,适配 1Panel 等平台;更新 README 部署文档。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-23 18:30:53 +08:00
parent 4f85a86077
commit c07d1fd2bd
16 changed files with 398 additions and 13 deletions

View File

@@ -34,9 +34,9 @@ type Config struct {
DevMode bool
}
// Parse 解析命令行与 app.ini并初始化数据目录
// Parse 解析命令行、环境变量与 app.ini并初始化数据目录
//
// 优先级(高 → 低):命令行显式参数 > app.ini > 内置默认值
// 优先级(高 → 低):命令行显式参数 > 环境变量 > app.ini > 内置默认值
func Parse() (*Config, error) {
configFlag := flag.String("config", "", "配置文件路径(默认:工作目录/app.ini")
workFlag := flag.String("work-path", "", "工作目录(默认:可执行文件所在目录)")
@@ -52,12 +52,20 @@ func Parse() (*Config, error) {
return nil, fmt.Errorf("无效的 -service 动作 %q可选install|uninstall|start|stop|restart|status", *serviceFlag)
}
workPath, err := resolveWorkPath(*workFlag)
workPathInput := strings.TrimSpace(*workFlag)
if workPathInput == "" {
workPathInput = envOrDefault(envWorkPath)
}
workPath, err := resolveWorkPath(workPathInput)
if err != nil {
return nil, err
}
configFile, err := resolveConfigPath(workPath, *configFlag)
configInput := strings.TrimSpace(*configFlag)
if configInput == "" {
configInput = envOrDefault(envConfig)
}
configFile, err := resolveConfigPath(workPath, configInput)
if err != nil {
return nil, err
}
@@ -73,11 +81,17 @@ func Parse() (*Config, error) {
}
port := fileCfg.Port
if p := envIntOrZero(envHTTPPort); p > 0 {
port = p
}
if *portFlag > 0 {
port = *portFlag
}
dataInput := fileCfg.DataRel
if v := envOrDefault(envData); v != "" {
dataInput = v
}
if strings.TrimSpace(*dataFlag) != "" {
dataInput = *dataFlag
}
@@ -86,9 +100,12 @@ func Parse() (*Config, error) {
return nil, fmt.Errorf("解析数据目录失败: %w", err)
}
jwtSecret := strings.TrimSpace(*jwtFlag)
if jwtSecret == "" {
jwtSecret = fileCfg.JWTSecret
jwtSecret := fileCfg.JWTSecret
if v := envOrDefault(envJWTSecret); v != "" {
jwtSecret = v
}
if strings.TrimSpace(*jwtFlag) != "" {
jwtSecret = strings.TrimSpace(*jwtFlag)
}
cfg := &Config{

32
config/env.go Normal file
View File

@@ -0,0 +1,32 @@
package config
import (
"os"
"strconv"
"strings"
)
// 容器 / 编排常用环境变量(优先级:命令行 > 环境变量 > app.ini > 内置默认)
const (
envWorkPath = "JIANG13_WORK_PATH"
envConfig = "JIANG13_CONFIG"
envHTTPPort = "JIANG13_HTTP_PORT"
envData = "JIANG13_DATA"
envJWTSecret = "JIANG13_JWT_SECRET"
)
func envOrDefault(key string) string {
return strings.TrimSpace(os.Getenv(key))
}
func envIntOrZero(key string) int {
v := envOrDefault(key)
if v == "" {
return 0
}
n, err := strconv.Atoi(v)
if err != nil || n <= 0 || n > 65535 {
return 0
}
return n
}