package customerverification import ( "fmt" "io" "os" "path/filepath" ) // allowedTypes mirrors media.allowedTypes but is kept separate on purpose: // verification documents are never routed through the public media module // (whose storage backends always return a publicly reachable URL -- fine // for product photos, never acceptable for an ID document). This is a // small, private, local-disk-only store instead. var allowedTypes = map[string]string{ "image/jpeg": ".jpg", "image/png": ".png", } // Storage persists ID document images under a directory that main.go must // never mount as a static file route. Files are only ever read back through // Handler.serveDocument, which checks the caller is the admin or the // document's own customer before streaming bytes. type Storage struct { dir string } func NewStorage(dir string) (*Storage, error) { if err := os.MkdirAll(dir, 0o700); err != nil { return nil, fmt.Errorf("create verification upload dir: %w", err) } return &Storage{dir: dir}, nil } func (s *Storage) Save(key string, reader io.Reader) error { dest := filepath.Join(s.dir, filepath.Base(key)) f, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600) if err != nil { return fmt.Errorf("create file: %w", err) } defer f.Close() if _, err := io.Copy(f, reader); err != nil { return fmt.Errorf("write file: %w", err) } return nil } func (s *Storage) Path(key string) string { return filepath.Join(s.dir, filepath.Base(key)) } func (s *Storage) Delete(key string) error { err := os.Remove(s.Path(key)) if err != nil && !os.IsNotExist(err) { return fmt.Errorf("delete file: %w", err) } return nil }