package main import ( "crypto/sha256" "encoding/hex" "fmt" "io" "os" "path/filepath" "strings" "os/exec" "github.com/spf13/viper" ) const ( ARCH_X86_64 = "x86_64" ARCH_RISCV64 = "riscv64" ARCH_I386 = "i386" ARCH_AARCH64 = "aarch64" ARCH_NOARCH = "noarch" ARCH_SRC = "src" ) type Repository struct { BasePath string } func NewRepository() *Repository { return &Repository{ BasePath: viper.GetString("repository.path"), } } // ProcessPackage обрабатывает загруженный пакет func (r *Repository) ProcessPackage(arch, filename string, body io.Reader) error { // Преобразуем body к io.ReadSeeker для возможности использовать Seek readSeeker, ok := body.(io.ReadSeeker) if !ok { logger.Errorf("Failed to cast body to io.ReadSeeker for file %s", filename) return fmt.Errorf("failed to cast body to io.ReadSeeker") } // Вычисляем контрольную сумму загруженного файла hash := sha256.New() if _, err := io.Copy(hash, readSeeker); err != nil { logger.Errorf("Error computing checksum for file %s: %v", filename, err) return err } checksum := hex.EncodeToString(hash.Sum(nil)) // Возвращаемся к началу файла для записи if _, err := readSeeker.Seek(0, 0); err != nil { logger.Errorf("Error seeking to the beginning of file %s: %v", filename, err) return err } // Проверяем, существует ли файл с такой контрольной суммой if r.fileExistsWithChecksum(arch, filename, checksum) { logger.Infof("File %s already exists with the same checksum, skipping upload.", filename) return nil } // Сохраняем пакет if err := r.SavePackage(arch, filename, readSeeker); err != nil { logger.Errorf("Error saving package %s: %v", filename, err) return err } return nil } func (r *Repository) SavePackage(arch, name string, body io.Reader) error { logger.Infof("Saving package %s for architecture %s", name, arch) dir := filepath.Join(r.BasePath, arch) if err := os.MkdirAll(dir, 0755); err != nil { logger.Errorf("Failed to create directory %s: %v", dir, err) return err } filePath := filepath.Join(dir, name) file, err := os.Create(filePath) if err != nil { logger.Errorf("Failed to create file %s: %v", filePath, err) return err } defer file.Close() _, err = io.Copy(file, body) if err != nil { logger.Errorf("Failed to copy file content to %s: %v", filePath, err) return err } // Generate metadata cmd := exec.Command("genhdlist2", "--update", "--root", dir) logger.Infof("Generating metadata for directory %s", dir) if err := cmd.Run(); err != nil { logger.Errorf("Failed to generate metadata: %v", err) return fmt.Errorf("failed to generate metadata: %w", err) } logger.Infof("Package %s saved successfully for architecture %s", name, arch) return nil } func (r *Repository) fileExistsWithChecksum(arch, filename, checksum string) bool { filePath := filepath.Join(r.BasePath, arch, filename) file, err := os.Open(filePath) if err != nil { if os.IsNotExist(err) { return false } logger.Errorf("Error opening file %s: %v", filePath, err) return false } defer file.Close() hash := sha256.New() if _, err := io.Copy(hash, file); err != nil { logger.Errorf("Error computing checksum for file %s: %v", filePath, err) return false } existingChecksum := hex.EncodeToString(hash.Sum(nil)) return existingChecksum == checksum } func (r *Repository) GetArch(arch string) string { switch strings.ToLower(arch) { case ARCH_X86_64: return ARCH_X86_64 case ARCH_RISCV64: return ARCH_RISCV64 case ARCH_I386: return ARCH_I386 case ARCH_AARCH64: return ARCH_AARCH64 case ARCH_NOARCH: return ARCH_NOARCH case ARCH_SRC: return ARCH_SRC default: return "" } }