skyview/internal/database/path.go

174 lines
4.4 KiB
Go
Raw Normal View History

package database
import (
"fmt"
"os"
"path/filepath"
"runtime"
)
// ResolveDatabasePath determines the appropriate database file location
// based on configuration, system type, and available permissions
func ResolveDatabasePath(configPath string) (string, error) {
// Use explicit configuration path if provided
if configPath != "" {
if err := ensureDirExists(filepath.Dir(configPath)); err != nil {
return "", fmt.Errorf("cannot create directory for configured path %s: %v", configPath, err)
}
return configPath, nil
}
// Try system location first (for services)
if systemPath, err := trySystemPath(); err == nil {
return systemPath, nil
}
// Try user data directory
if userPath, err := tryUserPath(); err == nil {
return userPath, nil
}
// Fallback to current directory
return tryCurrentDirPath()
}
// trySystemPath attempts to use system-wide database location
func trySystemPath() (string, error) {
var systemDir string
switch runtime.GOOS {
case "linux":
systemDir = "/var/lib/skyview"
case "darwin":
systemDir = "/usr/local/var/skyview"
case "windows":
systemDir = filepath.Join(os.Getenv("PROGRAMDATA"), "skyview")
default:
return "", fmt.Errorf("system path not supported on %s", runtime.GOOS)
}
// Check if directory exists and is writable
if err := ensureDirExists(systemDir); err != nil {
return "", err
}
dbPath := filepath.Join(systemDir, "skyview.db")
// Test write permissions
if err := testWritePermissions(dbPath); err != nil {
return "", err
}
return dbPath, nil
}
// tryUserPath attempts to use user data directory
func tryUserPath() (string, error) {
var userDataDir string
switch runtime.GOOS {
case "linux":
if xdgData := os.Getenv("XDG_DATA_HOME"); xdgData != "" {
userDataDir = xdgData
} else {
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
userDataDir = filepath.Join(home, ".local", "share")
}
case "darwin":
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
userDataDir = filepath.Join(home, "Library", "Application Support")
case "windows":
userDataDir = os.Getenv("APPDATA")
if userDataDir == "" {
return "", fmt.Errorf("APPDATA environment variable not set")
}
default:
return "", fmt.Errorf("user path not supported on %s", runtime.GOOS)
}
skyviewDir := filepath.Join(userDataDir, "skyview")
if err := ensureDirExists(skyviewDir); err != nil {
return "", err
}
dbPath := filepath.Join(skyviewDir, "skyview.db")
// Test write permissions
if err := testWritePermissions(dbPath); err != nil {
return "", err
}
return dbPath, nil
}
// tryCurrentDirPath uses current directory as fallback
func tryCurrentDirPath() (string, error) {
currentDir, err := os.Getwd()
if err != nil {
return "", fmt.Errorf("cannot get current directory: %v", err)
}
dbPath := filepath.Join(currentDir, "skyview.db")
// Test write permissions
if err := testWritePermissions(dbPath); err != nil {
return "", err
}
return dbPath, nil
}
// ensureDirExists creates directory if it doesn't exist
func ensureDirExists(dir string) error {
if _, err := os.Stat(dir); os.IsNotExist(err) {
if err := os.MkdirAll(dir, 0755); err != nil {
return fmt.Errorf("cannot create directory %s: %v", dir, err)
}
} else if err != nil {
return fmt.Errorf("cannot access directory %s: %v", dir, err)
}
return nil
}
// testWritePermissions verifies write access to the database path
func testWritePermissions(dbPath string) error {
dir := filepath.Dir(dbPath)
// Check directory write permissions
testFile := filepath.Join(dir, ".skyview_write_test")
if err := os.WriteFile(testFile, []byte("test"), 0644); err != nil {
return fmt.Errorf("no write permission to directory %s: %v", dir, err)
}
// Clean up test file
os.Remove(testFile)
return nil
}
// GetDatabaseDirectory returns the directory containing the database file
func GetDatabaseDirectory(dbPath string) string {
return filepath.Dir(dbPath)
}
// IsSystemPath returns true if the database path is in a system location
func IsSystemPath(dbPath string) bool {
switch runtime.GOOS {
case "linux":
return filepath.HasPrefix(dbPath, "/var/lib/skyview")
case "darwin":
return filepath.HasPrefix(dbPath, "/usr/local/var/skyview")
case "windows":
programData := os.Getenv("PROGRAMDATA")
return programData != "" && filepath.HasPrefix(dbPath, filepath.Join(programData, "skyview"))
}
return false
}