// Package config 负责运行参数和文件路径。 // // 改动本文件前必读 admin/AGENTS.md。最容易踩的一条: // 路径必须走 DataDir(),不许硬编码、不许直接用 os.Getwd(), // 打包成 exe 后那些写法会失效。详见 docs/admin/02-architecture.md §6。 package config import ( "os" "path/filepath" "strings" ) // DataDir 返回可写数据目录,不存在就创建。 // // 打包成 exe 后 = exe 旁边的 data/ // go run 时 = 当前工作目录下的 data/ // // 目录结构见 docs/admin/02-architecture.md §6: // // data/ // ├── admin.db // ├── logs/ // └── uploads/ func DataDir() (string, error) { exe, err := os.Executable() if err != nil { return "", err } dir := filepath.Join(filepath.Dir(exe), "data") // go run 会把程序编译到系统临时目录再执行, // 这种情况下 exe 旁边不是项目目录,要改用当前工作目录。 if isTempBuild(exe) { wd, err := os.Getwd() if err != nil { return "", err } dir = filepath.Join(wd, "data") } if err := os.MkdirAll(dir, 0o755); err != nil { return "", err } return dir, nil } // SubDir 返回 data/ 下的子目录,不存在就创建。 // 例如 SubDir("logs") -> /logs func SubDir(name string) (string, error) { base, err := DataDir() if err != nil { return "", err } dir := filepath.Join(base, name) if err := os.MkdirAll(dir, 0o755); err != nil { return "", err } return dir, nil } // isTempBuild 判断这个可执行文件是不是 go run 生成的临时产物。 func isTempBuild(exe string) bool { tmp := os.TempDir() if tmp != "" && strings.HasPrefix(exe, tmp) { return true } // go run 的产物路径里通常带 go-build 字样 return strings.Contains(filepath.ToSlash(exe), "/go-build") }