// Package storage gère la persistance des fichiers PDF sur le disque. package storage import ( "fmt" "io" "os" "path/filepath" "github.com/google/uuid" ) // Store écrit et lit les fichiers PDF sous un dossier racine. type Store struct { root string } // New crée un Store en s'assurant que le dossier racine existe. func New(root string) (*Store, error) { if err := os.MkdirAll(root, 0o750); err != nil { return nil, fmt.Errorf("création du dossier de stockage : %w", err) } return &Store{root: root}, nil } // path retourne le chemin absolu d'un fichier à partir de son identifiant. func (s *Store) path(id string) string { return filepath.Join(s.root, id) } // Save écrit le contenu de r dans un nouveau fichier et retourne son identifiant // ainsi que le nombre d'octets écrits. func (s *Store) Save(r io.Reader) (id string, size int64, err error) { id = uuid.NewString() f, err := os.OpenFile(s.path(id), os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0o640) if err != nil { return "", 0, err } defer f.Close() size, err = io.Copy(f, r) if err != nil { _ = os.Remove(s.path(id)) return "", 0, err } return id, size, nil } // Overwrite remplace le contenu du fichier identifié par id. func (s *Store) Overwrite(id string, r io.Reader) (size int64, err error) { f, err := os.OpenFile(s.path(id), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o640) if err != nil { return 0, err } defer f.Close() return io.Copy(f, r) } // Open ouvre le fichier identifié par id en lecture. func (s *Store) Open(id string) (*os.File, error) { return os.Open(s.path(id)) } // Delete supprime le fichier identifié par id. L'absence du fichier n'est pas une erreur. func (s *Store) Delete(id string) error { err := os.Remove(s.path(id)) if err != nil && !os.IsNotExist(err) { return err } return nil }