fix: 数据持久化到/app/data目录,简化部署配置

- 配置文件和SQLite数据库存储在/app/data
- Docker volume持久化数据
- 无需预设环境变量,安装向导自动配置
This commit is contained in:
2026-05-03 12:03:53 +08:00
parent 41fe48e128
commit f7f92c9853
4 changed files with 51 additions and 67 deletions
+15 -4
View File
@@ -2,29 +2,40 @@ package config
import ( import (
"log" "log"
"os"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
// Init 初始化配置 var dataDir = "/app/data"
func Init() { func Init() {
if dir := os.Getenv("DATA_DIR"); dir != "" {
dataDir = dir
}
if _, err := os.Stat(dataDir); os.IsNotExist(err) {
os.MkdirAll(dataDir, 0755)
}
viper.SetConfigName("config") viper.SetConfigName("config")
viper.SetConfigType("yaml") viper.SetConfigType("yaml")
viper.AddConfigPath(dataDir)
viper.AddConfigPath(".") viper.AddConfigPath(".")
viper.AddConfigPath("./config") viper.AddConfigPath("./config")
// 设置默认值
setDefaults() setDefaults()
// 读取配置文件
if err := viper.ReadInConfig(); err != nil { if err := viper.ReadInConfig(); err != nil {
log.Printf("Warning: Config file not found, using defaults: %v", err) log.Printf("Warning: Config file not found, using defaults: %v", err)
} }
// 环境变量覆盖
viper.AutomaticEnv() viper.AutomaticEnv()
} }
func GetDataDir() string {
return dataDir
}
// setDefaults 设置默认配置值 // setDefaults 设置默认配置值
func setDefaults() { func setDefaults() {
// 应用配置 // 应用配置
+9 -26
View File
@@ -11,7 +11,6 @@ import (
"context" "context"
"fmt" "fmt"
"log" "log"
"os"
"path/filepath" "path/filepath"
"time" "time"
"verification-platform-backend/internal/config" "verification-platform-backend/internal/config"
@@ -52,26 +51,18 @@ func initDatabase() {
} }
func initSQLite() { func initSQLite() {
dbPath := "verification_platform.db" dataDir := config.GetDataDir()
absPath, err := filepath.Abs(dbPath) dbPath := filepath.Join(dataDir, "verification_platform.db")
if err != nil {
log.Fatal("Failed to get absolute path:", err.Error())
}
wd, err := os.Getwd()
if err == nil && filepath.Base(wd) == "cmd" {
absPath = filepath.Join(filepath.Dir(wd), "verification_platform.db")
}
var errOpen error var errOpen error
DB, errOpen = gorm.Open(sqlite.Open(absPath), &gorm.Config{ DB, errOpen = gorm.Open(sqlite.Open(dbPath), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent), Logger: logger.Default.LogMode(logger.Silent),
DisableForeignKeyConstraintWhenMigrating: true, DisableForeignKeyConstraintWhenMigrating: true,
}) })
if errOpen != nil { if errOpen != nil {
log.Fatal("Failed to create SQLite database:", errOpen.Error()) log.Fatal("Failed to create SQLite database:", errOpen.Error())
} }
log.Printf("Using SQLite database: %s\n", absPath) log.Printf("Using SQLite database: %s\n", dbPath)
if sqlDB, err := DB.DB(); err == nil { if sqlDB, err := DB.DB(); err == nil {
sqlDB.SetMaxIdleConns(10) sqlDB.SetMaxIdleConns(10)
@@ -132,27 +123,19 @@ func initAutoDetect() {
}) })
if err != nil { if err != nil {
log.Println("Failed to connect to MySQL, using SQLite database for development:", err.Error()) log.Println("Failed to connect to MySQL, using SQLite database:", err.Error())
dbPath := "verification_platform.db" dataDir := config.GetDataDir()
absPath, err := filepath.Abs(dbPath) dbPath := filepath.Join(dataDir, "verification_platform.db")
if err != nil {
log.Fatal("Failed to get absolute path:", err.Error())
}
wd, err := os.Getwd() DB, err = gorm.Open(sqlite.Open(dbPath), &gorm.Config{
if err == nil && filepath.Base(wd) == "cmd" {
absPath = filepath.Join(filepath.Dir(wd), "verification_platform.db")
}
DB, err = gorm.Open(sqlite.Open(absPath), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent), Logger: logger.Default.LogMode(logger.Silent),
DisableForeignKeyConstraintWhenMigrating: true, DisableForeignKeyConstraintWhenMigrating: true,
}) })
if err != nil { if err != nil {
log.Fatal("Failed to create SQLite database:", err.Error()) log.Fatal("Failed to create SQLite database:", err.Error())
} }
log.Printf("Database file path: %s\n", absPath) log.Printf("Using SQLite database: %s\n", dbPath)
} }
if sqlDB, err := DB.DB(); err == nil { if sqlDB, err := DB.DB(); err == nil {
+14 -7
View File
@@ -80,9 +80,9 @@ func handleTestDatabase(c *gin.Context) {
} }
if req.DbType == "sqlite" { if req.DbType == "sqlite" {
dbPath := "verification_platform.db" dataDir := config.GetDataDir()
absPath, _ := filepath.Abs(dbPath) dbPath := filepath.Join(dataDir, "verification_platform.db")
testDB, err := gorm.Open(sqlite.Open(absPath), &gorm.Config{ testDB, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent), Logger: logger.Default.LogMode(logger.Silent),
}) })
if err != nil { if err != nil {
@@ -167,9 +167,9 @@ func handleSetup(c *gin.Context) {
var err error var err error
if req.DbType == "sqlite" { if req.DbType == "sqlite" {
dbPath := "verification_platform.db" dataDir := config.GetDataDir()
absPath, _ := filepath.Abs(dbPath) dbPath := filepath.Join(dataDir, "verification_platform.db")
newDB, err = gorm.Open(sqlite.Open(absPath), &gorm.Config{ newDB, err = gorm.Open(sqlite.Open(dbPath), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent), Logger: logger.Default.LogMode(logger.Silent),
DisableForeignKeyConstraintWhenMigrating: true, DisableForeignKeyConstraintWhenMigrating: true,
}) })
@@ -241,6 +241,11 @@ func handleSetup(c *gin.Context) {
} }
func checkConfigFile() bool { func checkConfigFile() bool {
dataDir := config.GetDataDir()
configPath := filepath.Join(dataDir, "config.yaml")
if _, err := os.Stat(configPath); err == nil {
return true
}
paths := []string{"config.yaml", "./config/config.yaml"} paths := []string{"config.yaml", "./config/config.yaml"}
for _, p := range paths { for _, p := range paths {
if _, err := os.Stat(p); err == nil { if _, err := os.Stat(p); err == nil {
@@ -338,7 +343,9 @@ func generateConfigFile(dbType string, req SetupRequest, jwtSecret string) strin
} }
func saveConfigFile(content string) error { func saveConfigFile(content string) error {
file, err := os.Create("config.yaml") dataDir := config.GetDataDir()
configPath := filepath.Join(dataDir, "config.yaml")
file, err := os.Create(configPath)
if err != nil { if err != nil {
return err return err
} }
+13 -30
View File
@@ -1,4 +1,16 @@
services: services:
app:
image: ghcr.io/cmakecpp/verify:main
container_name: verify-app
restart: unless-stopped
ports:
- "${APP_PORT:-8080}:8080"
volumes:
- app_data:/app/data
- uploads:/app/uploads
networks:
- verify-network
mysql: mysql:
image: mysql:8.0 image: mysql:8.0
container_name: verify-mysql container_name: verify-mysql
@@ -8,42 +20,13 @@ services:
MYSQL_DATABASE: ${MYSQL_DATABASE:-verification_platform} MYSQL_DATABASE: ${MYSQL_DATABASE:-verification_platform}
MYSQL_CHARACTER_SET_SERVER: utf8mb4 MYSQL_CHARACTER_SET_SERVER: utf8mb4
MYSQL_COLLATION_SERVER: utf8mb4_unicode_ci MYSQL_COLLATION_SERVER: utf8mb4_unicode_ci
ports:
- "${MYSQL_PORT:-3306}:3306"
volumes: volumes:
- mysql_data:/var/lib/mysql - mysql_data:/var/lib/mysql
networks: networks:
- verify-network - verify-network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
app:
image: ghcr.io/cmakecpp/verify:main
container_name: verify-app
restart: unless-stopped
environment:
- APP_ENV=production
- APP_PORT=8080
- APP_JWT_SECRET=${JWT_SECRET:-your-jwt-secret-change-me}
- DATABASE_HOST=mysql
- DATABASE_PORT=3306
- DATABASE_NAME=${MYSQL_DATABASE:-verification_platform}
- DATABASE_USERNAME=root
- DATABASE_PASSWORD=${MYSQL_ROOT_PASSWORD:-root123456}
ports:
- "${APP_PORT:-8080}:8080"
volumes:
- uploads:/app/uploads
depends_on:
mysql:
condition: service_healthy
networks:
- verify-network
volumes: volumes:
app_data:
mysql_data: mysql_data:
uploads: uploads: