package database import ( "context" "fmt" "io" "net/http" "os" "path" "path/filepath" "slices" "strings" "sync" "qulay/config" "qulay/helpers" "codeberg.org/UzbekLinux/uzbekdb-go" "github.com/codeclysm/extract/v3" ) var _name string = filepath.Base(os.Args[0]) type Repo struct { Name string URL string } func GetRepos(destDir string) []Repo { dir := filepath.Join(destDir, paths.ReposConfigDir) entries, err := os.ReadDir(dir) if err != nil { return nil } var repos []Repo for _, entry := range entries { if entry.IsDir() { continue } data, err := os.ReadFile( filepath.Join(dir, entry.Name()), ) if err != nil { continue } m := uzbekdb.Loads(string(data), "map") if m == nil { continue } mp, ok := m.(map[interface{}][]interface{}) if !ok { continue } repo := Repo{Name: entry.Name()} if vals, ok := mp["url"]; ok && len(vals) > 0 { repo.URL = fmt.Sprintf("%v", vals[0]) } repos = append(repos, repo) } slices.SortFunc(repos, func(a, b Repo) int { return strings.Compare(a.Name, b.Name) }) return repos } func RepoURL(destDir, name string) string { for _, repo := range GetRepos(destDir) { if repo.Name == name { return repo.URL } } return "" } func DownloadRepos(destDir string) { var wg sync.WaitGroup for _, repo := range GetRepos(destDir) { wg.Add(1) go func(base, name string) { defer wg.Done() link := base if !strings.HasSuffix(link, "/") { link += "/" } link += name + ".tar.zst" archiveName := path.Base(link) destArchivePath := filepath.Join(destDir, "tmp", archiveName) destPath := filepath.Join(destDir, paths.ReposDir) fullDestPath := filepath.Join(destPath, name) os.MkdirAll(filepath.Dir(destArchivePath), 0755) defer os.Remove(destArchivePath) client := helpers.HTTPClient() resp, err := client.Get(link) if err != nil { fmt.Println("!! failed to download", link, err) return } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { fmt.Println("!! http error", resp.StatusCode, link) return } f, err := os.Create(destArchivePath) if err != nil { fmt.Println("!! failed to create file", err) return } defer f.Close() _, err = io.Copy(f, resp.Body) if err != nil { fmt.Println("!! failed to copy", err) return } fmt.Println(":: downloaded", archiveName, "to", destArchivePath) fmt.Println(":: removing old repo", fullDestPath) err = os.RemoveAll(fullDestPath) if err != nil { fmt.Println("w! failed to delete old repo", fullDestPath, err) } file, err := os.Open(destArchivePath) if err != nil { fmt.Println("!! failed to open archive file", destArchivePath, err) return } defer file.Close() err = extract.Zstd(context.TODO(), file, fullDestPath, nil) if err != nil { fmt.Println("!! failed to extract", err) return } }(repo.URL, repo.Name) } wg.Wait() fmt.Println(":: repo updating ended") } func ReadInstalled(destDir string) map[interface{}][]interface{} { installedFullPath := filepath.Join(destDir, paths.InstalledFile) file, err := os.OpenFile(installedFullPath, os.O_RDONLY, 0644) if err != nil { return map[interface{}][]interface{}{} } defer file.Close() text, err := io.ReadAll(file) if err != nil { return map[interface{}][]interface{}{} } data := uzbekdb.Loads(string(text), "map") if data == nil { return map[interface{}][]interface{}{} } return data.(map[interface{}][]interface{}) } func WriteInstalled(data map[interface{}][]interface{}, destDir string) error { installedFullPath := filepath.Join(destDir, paths.InstalledFile) file, err := os.OpenFile(installedFullPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { return err } defer file.Close() _, err = file.WriteString(uzbekdb.Dumps(data)) return err } func IsInstalled(name string, destDir string) bool { data := ReadInstalled(destDir) if !strings.Contains(name, "/") { return false } _, ok := data[name] return ok } func GetPackageVersion(name string, destDir string) string { data := ReadInstalled(destDir) if !strings.Contains(name, "/") { for k := range data { if kStr, ok := k.(string); ok { if strings.HasSuffix(kStr, "/"+name) { name = kStr break } } } } alo, _ := data[name] if len(alo) > 0 { return fmt.Sprintf("%v", alo[0]) } return "" } func RegPackage(name string, version string, destDir string) error { data := ReadInstalled(destDir) val, installed := data[name] if installed && len(val) > 0 { currentVersion := fmt.Sprintf("%v", val[0]) if currentVersion != version { data[name][0] = version } } else { data[name] = []interface{}{version} } return WriteInstalled(data, destDir) } func UnRegPackage(name string, destDir string) error { data := ReadInstalled(destDir) _, ok := data[name] if !ok { return nil } delete(data, name) return WriteInstalled(data, destDir) } func PurgeRepository(name string, destDir string) error { dirPath := filepath.Join(destDir, paths.ReposDir, name) if _, err := os.Stat(dirPath); err != nil { return fmt.Errorf("repository '%s' not found", name) } err := os.RemoveAll(dirPath) if err != nil { return err } return nil }