feat: 数据库配置将 MySQL DSN 拆分为独立字段(db_name/username/password/host/port)

This commit is contained in:
2026-08-28 17:02:28 +08:00
parent 6220de9c84
commit 7280650069
5 files changed
+76 -12

No files matched your search

+10 -2
View File
@@ -60,7 +60,11 @@ go run .
# config.yaml
database:
type: sqlite # sqlite(默认)或 mysql
dsn: "" # MySQL 连接串sqlite 模式下忽略
db_name: "" # MySQL 数据库名sqlite 模式下忽略
username: "" # MySQL 用户名,sqlite 模式下忽略
password: "" # MySQL 密码,sqlite 模式下忽略
host: "" # MySQL IP 或主机名,sqlite 模式下忽略
port: "" # MySQL 端口,sqlite 模式下忽略
web:
port: "8080" # Web 服务端口,"" 或 "0" 可只启用 socket
socket: "" # unix socket 路径(Linux 部署推荐,见 install_linux.sh
@@ -78,7 +82,11 @@ secret: <自动生成> # Session 加密密钥;缺失时拒绝启动
```yaml
database:
type: mysql
dsn: user:password@tcp(127.0.0.1:3306)/blog_go?charset=utf8mb4&parseTime=True&loc=Local
db_name: blog_go
username: user
password: password
host: 127.0.0.1
port: "3306"
web:
port: "8080"
```
+23 -6
View File
@@ -3,6 +3,7 @@ package config
import (
"crypto/rand"
"encoding/hex"
"fmt"
"log"
"os"
"path/filepath"
@@ -21,8 +22,20 @@ type Config struct {
// DatabaseConfig 保存数据库相关配置。
type DatabaseConfig struct {
Type string `yaml:"type"` // "sqlite"(默认)或 "mysql"
DSN string `yaml:"dsn"` // MySQL 连接字符串type 为 "mysql" 时必填)
Type string `yaml:"type"` // "sqlite"(默认)或 "mysql"
DBName string `yaml:"db_name"` // 数据库名type 为 "mysql" 时必填)
Username string `yaml:"username"` // 用户名(type 为 "mysql" 时必填)
Password string `yaml:"password"` // 密码(type 为 "mysql" 时必填)
Host string `yaml:"host"` // IP 或主机名(type 为 "mysql" 时必填)
Port string `yaml:"port"` // 端口(type 为 "mysql" 时必填)
}
// MySQLDSN 根据拆分字段构建 MySQL 连接字符串。
func (d *DatabaseConfig) MySQLDSN() string {
return fmt.Sprintf(
"%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
d.Username, d.Password, d.Host, d.Port, d.DBName,
)
}
// WebConfig 保存 Web 服务器监听配置。
@@ -41,8 +54,8 @@ var defaultTrustedProxies = []string{"127.0.0.1", "::1"}
const defaultPort = "8080"
// mysqlExampleDSN 会写入新建的配置文件,作为参考示例
const mysqlExampleDSN = "user:password@tcp(127.0.0.1:3306)/blog_go?charset=utf8mb4&parseTime=True&loc=Local"
// mysqlExampleDBName 作为示例写入新建的配置文件。
const mysqlExampleDBName = "blog_go"
// getConfigPath 返回按操作系统区分的配置目录和配置文件路径。
func getConfigPath() (dir, file string) {
@@ -107,8 +120,12 @@ func LoadConfig(customPath string) *Config {
cfg := &Config{
Database: DatabaseConfig{
Type: "sqlite",
DSN: mysqlExampleDSN,
Type: "sqlite",
DBName: mysqlExampleDBName,
Username: "user",
Password: "password",
Host: "127.0.0.1",
Port: "3306",
},
Web: WebConfig{
Port: defaultPort,
+14
View File
@@ -20,3 +20,17 @@ func TestConfigFileCreatedNotWorldReadable(t *testing.T) {
t.Fatalf("config perms = %v, want 0640", perm)
}
}
func TestMySQLDSN(t *testing.T) {
d := &DatabaseConfig{
DBName: "blog_go",
Username: "user",
Password: "pass:word",
Host: "127.0.0.1",
Port: "3306",
}
want := "user:pass:word@tcp(127.0.0.1:3306)/blog_go?charset=utf8mb4&parseTime=True&loc=Local"
if got := d.MySQLDSN(); got != want {
t.Fatalf("MySQLDSN() = %q, want %q", got, want)
}
}
+5 -1
View File
@@ -53,7 +53,11 @@ if [[ ! -f "${CONFIG_DIR}/config.yaml" ]]; then
cat > "${CONFIG_DIR}/config.yaml" <<EOF
database:
type: sqlite
dsn: ""
db_name: ""
username: ""
password: ""
host: ""
port: ""
web:
port: "8080"
socket: ${SOCKET_PATH}
+24 -3
View File
@@ -2,6 +2,7 @@ package models
import (
"crypto/rand"
"errors"
"log"
"os"
"path/filepath"
@@ -36,6 +37,26 @@ func randomAdminPassword() string {
return string(out)
}
// validateMySQLConfig 校验 MySQL 连接所需字段均非空。
func validateMySQLConfig(d *config.DatabaseConfig) error {
if d.DBName == "" {
return errors.New("'db_name' is required when type is 'mysql'")
}
if d.Username == "" {
return errors.New("'username' is required when type is 'mysql'")
}
if d.Password == "" {
return errors.New("'password' is required when type is 'mysql'")
}
if d.Host == "" {
return errors.New("'host' is required when type is 'mysql'")
}
if d.Port == "" {
return errors.New("'port' is required when type is 'mysql'")
}
return nil
}
// InitDB 打开数据库连接、执行迁移并初始化管理员用户。
func InitDB(cfg *config.Config) *gorm.DB {
// 确保存储目录存在。
@@ -47,10 +68,10 @@ func InitDB(cfg *config.Config) *gorm.DB {
switch cfg.Database.Type {
case "mysql":
if cfg.Database.DSN == "" {
log.Fatalf("Database DSN is required when type is 'mysql'. Please set it in your config file.")
if err := validateMySQLConfig(&cfg.Database); err != nil {
log.Fatalf("Invalid MySQL config: %v", err)
}
dialector = mysql.Open(cfg.Database.DSN)
dialector = mysql.Open(cfg.Database.MySQLDSN())
default:
dbPath := filepath.Join(cfg.Path, "blog.db")
dialector = sqlite.Open(dbPath)