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 }