Files
past-go/past-go/internal/config/config.go
T
Blizzard 8be6d9b1ab feat: add account deletion, announcements and subscription renewal
Support personal records and persistent smoking visibility. Add upload quota preflight, file cleanup, privacy entry, image caching and revision-based page refresh across the backend, admin and mini program.
2026-11-03 22:06:47 +08:00

169 lines
5.2 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 读取服务配置(对应 PHP 的 config.php + config.credentials.php)。
package config
import (
"fmt"
"net"
"net/url"
"os"
"path/filepath"
"strconv"
"strings"
"gopkg.in/yaml.v3"
)
type RedisConfig struct {
Addr string `yaml:"addr"`
Username string `yaml:"username"`
Password string `yaml:"password"`
DB int `yaml:"db"`
KeyPrefix string `yaml:"key_prefix"`
}
type LogConfig struct {
Level string `yaml:"level"`
Console bool `yaml:"console"`
RetentionDays int `yaml:"retention_days"`
MaxSizeMB int `yaml:"max_size_mb"`
MaxBackups int `yaml:"max_backups"`
}
type MinioConfig struct {
Endpoint string `yaml:"endpoint"`
AccessKey string `yaml:"access_key"`
SecretKey string `yaml:"secret_key"`
Bucket string `yaml:"bucket"`
UseSSL bool `yaml:"use_ssl"`
}
type WechatPayConfig struct {
Enabled bool `yaml:"enabled"`
AppID string `yaml:"appid"`
MchID string `yaml:"mch_id"`
CertificateSerial string `yaml:"certificate_serial"`
CertificatePath string `yaml:"certificate_path"`
APIv3Key string `yaml:"api_v3_key"`
PrivateKeyPath string `yaml:"private_key_path"`
PublicKeyID string `yaml:"public_key_id"`
PublicKeyPath string `yaml:"public_key_path"`
NotifyURL string `yaml:"notify_url"`
}
func (c WechatPayConfig) ValidateNotifyURL() error {
u, err := url.Parse(c.NotifyURL)
if err != nil || u.Scheme != "https" || u.Hostname() == "" || u.User != nil || u.RawQuery != "" || u.Fragment != "" {
return fmt.Errorf("微信支付 notify_url 必须为公网 HTTPS 地址,且不含查询参数")
}
return nil
}
type Config struct {
Redis RedisConfig `yaml:"redis"`
Log LogConfig `yaml:"log"`
WechatPay WechatPayConfig `yaml:"wechat_pay"`
Minio MinioConfig `yaml:"minio"`
Listen string `yaml:"listen"` // 监听地址,如 ":8080"
Debug bool `yaml:"debug"` // gin debug 模式(打印路由等)
// DataDir 运行目录:log/ 存日志,data/tmp/ 存临时文件;文件存储在 MinIO。
// 对应 PHP 的 backend/ 目录;默认为可执行文件所在目录。
DataDir string `yaml:"data_dir"`
DB struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
Name string `yaml:"name"`
User string `yaml:"user"`
Pass string `yaml:"pass"`
SSLMode string `yaml:"sslmode"`
} `yaml:"db"`
Wechat struct {
AppID string `yaml:"appid"`
Secret string `yaml:"secret"`
// 订阅消息模板 ID(对应 WECHAT_SUBSCRIBE_TEMPLATES['notify'])
NotifyTemplateID string `yaml:"notify_template_id"`
} `yaml:"wechat"`
TencentMapKey string `yaml:"tencent_map_key"`
// PHPCompat 对齐的 PHP 版本:"8.0"(默认,数据库数值按字符串返回)或 "8.1"(原生类型)
PHPCompat string `yaml:"php_compat"`
// MaxUploadMB 单文件上传上限(PHP: MAX_UPLOAD_SIZE = 50MB)
MaxUploadMB int64 `yaml:"max_upload_mb"`
// Notify 定时补发通知(对应 cron_notify.php)的执行间隔(分钟),0 表示关闭
NotifyIntervalMin int `yaml:"notify_interval_min"`
}
func Load(path string) (*Config, error) {
c := &Config{}
c.Redis = RedisConfig{Addr: "127.0.0.1:6379", KeyPrefix: "past-go:"}
c.Log = LogConfig{Level: "info", Console: true, RetentionDays: 30, MaxSizeMB: 100, MaxBackups: 10}
c.Listen = ":8080"
c.DB.Host = "192.168.100.127"
c.DB.Port = 5432
c.DB.Name = "past-go"
c.DB.User = "root"
c.DB.Pass = "root"
c.DB.SSLMode = "disable"
c.MaxUploadMB = 50
c.NotifyIntervalMin = 5
c.Wechat.NotifyTemplateID = "K_iQMwE_jre0j6QDVL78DZz7wiR1SKVY5h2YJCK6uPU"
c.PHPCompat = "8.0"
b, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("读取配置文件 %s: %w", path, err)
}
if err := yaml.Unmarshal(b, c); err != nil {
return nil, fmt.Errorf("解析配置文件 %s: %w", path, err)
}
if c.WechatPay.AppID == "" {
c.WechatPay.AppID = c.Wechat.AppID
}
if c.WechatPay.Enabled && c.WechatPay.AppID != c.Wechat.AppID {
return nil, fmt.Errorf("微信支付 AppID 必须与微信登录 AppID 一致")
}
// Certificate paths are relative to the selected YAML file, independent of cwd.
for _, p := range []*string{&c.WechatPay.PrivateKeyPath, &c.WechatPay.PublicKeyPath, &c.WechatPay.CertificatePath} {
if *p != "" && !filepath.IsAbs(*p) {
*p = filepath.Join(filepath.Dir(path), *p)
}
}
if c.DataDir == "" {
exe, err := os.Executable()
if err != nil {
return nil, err
}
c.DataDir = filepath.Dir(exe)
}
abs, err := filepath.Abs(c.DataDir)
if err != nil {
return nil, err
}
c.DataDir = abs
return c, nil
}
func (c *Config) UploadDir() string { return filepath.Join(c.DataDir, "uploads") }
func (c *Config) LogDir() string { return filepath.Join(c.DataDir, "log") }
// DSN uses URL escaping for database names, passwords and IPv6 hosts.
func (c *Config) DSN() string {
u := &url.URL{Scheme: "postgres", User: url.UserPassword(c.DB.User, c.DB.Pass), Host: net.JoinHostPort(c.DB.Host, strconv.Itoa(c.DB.Port)), Path: "/" + c.DB.Name}
q := url.Values{}
mode := c.DB.SSLMode
if mode == "" {
mode = "disable"
}
q.Set("sslmode", mode)
q.Set("TimeZone", "Asia/Shanghai")
q.Set("connect_timeout", "10")
u.RawQuery = strings.ReplaceAll(q.Encode(), "%2F", "/")
return u.String()
}