fix: persist docker sqlite settings path

This commit is contained in:
stupid-h4er
2026-05-27 22:11:36 +08:00
parent 74ea4b81f2
commit 6d185a6d93
4 changed files with 65 additions and 0 deletions
+1
View File
@@ -19,6 +19,7 @@ PORT=8080
# 数据库配置,默认使用本地 SQLite。
STORAGE_DRIVER=sqlite
# sqlite: DATABASE_DSN=data/infinite-canvas.db
# Docker 部署时建议使用绝对路径,避免工作目录变化后写入临时库:DATABASE_DSN=/app/data/infinite-canvas.db
# mysql: DATABASE_DSN=user:password@tcp(127.0.0.1:3306)/infinite_canvas?parseTime=true
# postgres: DATABASE_DSN=postgres://user:password@127.0.0.1:5432/infinite_canvas?sslmode=disable
DATABASE_DSN=data/infinite-canvas.db
+30
View File
@@ -3,6 +3,8 @@ package config
import (
"crypto/rand"
"encoding/base64"
"os"
"path/filepath"
"strings"
"github.com/caarlos0/env/v11"
@@ -30,6 +32,7 @@ func Load() error {
if err := env.Parse(&Cfg); err != nil {
return err
}
normalizeDockerSQLiteDSN("/app/data")
if strings.TrimSpace(Cfg.JWTSecret) == "" || Cfg.JWTSecret == "infinite-canvas" {
secret, err := randomSecret()
if err != nil {
@@ -40,6 +43,33 @@ func Load() error {
return nil
}
func normalizeDockerSQLiteDSN(appDataDir string) {
driver := strings.ToLower(strings.TrimSpace(Cfg.StorageDriver))
if driver != "" && driver != "sqlite" {
return
}
dsn := strings.TrimSpace(Cfg.DatabaseDSN)
if dsn == "" || dsn == ":memory:" || strings.HasPrefix(dsn, "file:") {
return
}
pathPart, suffix := dsn, ""
if index := strings.Index(dsn, "?"); index >= 0 {
pathPart = dsn[:index]
suffix = dsn[index:]
}
if filepath.IsAbs(pathPart) {
return
}
slashPath := filepath.ToSlash(pathPart)
if slashPath != "data" && !strings.HasPrefix(slashPath, "data/") {
return
}
if _, err := os.Stat(appDataDir); err != nil {
return
}
Cfg.DatabaseDSN = filepath.Join(filepath.Dir(appDataDir), filepath.FromSlash(slashPath)) + suffix
}
func randomSecret() (string, error) {
buf := make([]byte, 32)
if _, err := rand.Read(buf); err != nil {
+33
View File
@@ -0,0 +1,33 @@
package config
import (
"os"
"path/filepath"
"testing"
)
func TestNormalizeDockerSQLiteDSNUsesMountedDataDir(t *testing.T) {
root := t.TempDir()
appDataDir := filepath.Join(root, "data")
if err := os.MkdirAll(appDataDir, 0755); err != nil {
t.Fatal(err)
}
Cfg = Config{StorageDriver: "sqlite", DatabaseDSN: "data/infinite-canvas.db?_pragma=busy_timeout(5000)"}
normalizeDockerSQLiteDSN(appDataDir)
want := filepath.Join(root, "data", "infinite-canvas.db") + "?_pragma=busy_timeout(5000)"
if Cfg.DatabaseDSN != want {
t.Fatalf("DatabaseDSN = %q, want %q", Cfg.DatabaseDSN, want)
}
}
func TestNormalizeDockerSQLiteDSNLeavesLocalPathWithoutMountedDataDir(t *testing.T) {
Cfg = Config{StorageDriver: "sqlite", DatabaseDSN: "data/infinite-canvas.db"}
normalizeDockerSQLiteDSN(filepath.Join(t.TempDir(), "missing-data"))
if Cfg.DatabaseDSN != "data/infinite-canvas.db" {
t.Fatalf("DatabaseDSN = %q, want relative local path", Cfg.DatabaseDSN)
}
}
+1
View File
@@ -1,5 +1,6 @@
# 待测试
- Docker 部署时,`DATABASE_DSN=data/infinite-canvas.db` 会在存在 `/app/data` 挂载目录时自动归一到 `/app/data/infinite-canvas.db`,需要验证后台模型配置不会再因为工作目录变为 `/app/web` 而读到空库。
- Seedance 参考素材失败原因排查:后端会把火山上游错误摘要返回给前端;`/video` 和画布视频生成会按 `图片1/视频1/音频1` 自动编号参考素材,并在实际请求提示词中注入编号说明;参考视频会在请求前校验大小、时长、宽高、宽高比和像素总量。
- 画布项目导出改为下载 `.zip` 压缩包,包内包含 `projects.json` 和当前画布引用到的本地图片、视频文件,避免只导出 JSON 时丢失媒体内容。
- 画布库支持多选后一键导出多个画布项目,导出的压缩包可一次恢复多个项目。