Files
jiang13-forum/config/ini.go
freefire 9487c8ab02 支持 app.ini 配置与系统服务安装,并优化前端布局与无障碍体验。
引入类 Gitea 的 app.ini、Windows Service/systemd 控制;前端增加侧栏抽屉、回到顶部、标签输入与浮层 a11y。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 14:09:58 +08:00

107 lines
2.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package config
import (
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"gopkg.in/ini.v1"
)
const (
defaultPort = 3000
defaultDataRel = "data"
defaultConfName = "app.ini"
)
// fileSettings 从 app.ini 读出的原始值(尚未解析为绝对路径)
type fileSettings struct {
Port int
DataRel string
JWTSecret string
}
func defaultFileSettings() fileSettings {
return fileSettings{
Port: defaultPort,
DataRel: defaultDataRel,
}
}
func loadAppINI(path string) (fileSettings, error) {
out := defaultFileSettings()
cfg, err := ini.LoadSources(ini.LoadOptions{
IgnoreInlineComment: true,
}, path)
if err != nil {
return out, fmt.Errorf("读取配置文件失败: %w", err)
}
if sec, err := cfg.GetSection("server"); err == nil {
if k := sec.Key("HTTP_PORT"); k.String() != "" {
p, err := k.Int()
if err != nil {
return out, fmt.Errorf("server.HTTP_PORT 无效: %w", err)
}
if p <= 0 || p > 65535 {
return out, fmt.Errorf("server.HTTP_PORT 超出范围: %d", p)
}
out.Port = p
}
}
if sec, err := cfg.GetSection("paths"); err == nil {
if v := strings.TrimSpace(sec.Key("DATA").String()); v != "" {
out.DataRel = v
}
}
if sec, err := cfg.GetSection("security"); err == nil {
out.JWTSecret = strings.TrimSpace(sec.Key("JWT_SECRET").String())
}
return out, nil
}
// writeAppINI 写入/覆盖 app.ini安装服务或首次生成时使用
func writeAppINI(path string, port int, dataRel, jwtSecret string) error {
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return err
}
var b strings.Builder
b.WriteString("; 姜十三论坛 Jiang13 Forum — 配置文件(风格类似 Gitea app.ini\n")
b.WriteString("; 修改后重启进程/服务生效。命令行参数优先级高于本文件。\n")
b.WriteString(";\n")
b.WriteString("; 默认位置:程序工作目录下的 app.ini\n")
b.WriteString("; 可用 --config / --work-path 覆盖。\n")
b.WriteString("\n")
b.WriteString("[server]\n")
b.WriteString("HTTP_PORT = ")
b.WriteString(strconv.Itoa(port))
b.WriteString("\n\n")
b.WriteString("[paths]\n")
b.WriteString("; 相对路径相对于工作目录(默认可执行文件所在目录)\n")
b.WriteString("DATA = ")
b.WriteString(dataRel)
b.WriteString("\n\n")
b.WriteString("[security]\n")
b.WriteString("; 留空则自动生成并持久化到 data/.jwt_secret勿把生产密钥提交到仓库\n")
b.WriteString("JWT_SECRET = ")
b.WriteString(jwtSecret)
b.WriteString("\n")
return os.WriteFile(path, []byte(b.String()), 0644)
}
// resolveDataRelForINI 把绝对数据目录尽量写成相对工作目录的路径,便于 app.ini 可读
func resolveDataRelForINI(workPath, absData string) string {
rel, err := filepath.Rel(workPath, absData)
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
return absData
}
return filepath.ToSlash(rel)
}