secret/internal/cli/info_helper.go
user 78015afb35 Add secret.Warn() calls for all silent anomalous conditions
Audit of the codebase found 9 locations where errors or anomalous
conditions were silently swallowed or only logged via Debug(). Users
should be informed when something unexpected happens, even if the
program can continue.

Changes:
- DetermineStateDir: warn on config dir fallback to ~/.config
- info_helper: warn when vault/secret stats cannot be read
- unlockers list: warn on metadata read/parse failures (fixes FIXMEs)
- unlockers list: warn on fallback ID generation
- checkUnlockerExists: warn on errors during duplicate checking
- completions: warn on unlocker metadata read/parse failures
- version list: upgrade metadata load failure from Debug to Warn
- secrets: upgrade file close failure from Debug to Warn
- version naming: warn on malformed version directory names

Closes #19
2026-02-20 00:03:49 -08:00

89 lines
2.2 KiB
Go

package cli
import (
"path/filepath"
"time"
"git.eeqj.de/sneak/secret/internal/secret"
"github.com/spf13/afero"
)
// gatherVaultStats collects statistics from all vaults
func gatherVaultStats(
fs afero.Fs,
vaultsDir string,
) (totalSecrets int, totalSize int64, oldestTime, latestTime time.Time, err error) {
vaultEntries, err := afero.ReadDir(fs, vaultsDir)
if err != nil {
return 0, 0, time.Time{}, time.Time{}, err
}
for _, vaultEntry := range vaultEntries {
if !vaultEntry.IsDir() {
continue
}
vaultPath := filepath.Join(vaultsDir, vaultEntry.Name())
secretsPath := filepath.Join(vaultPath, "secrets.d")
// Count secrets in this vault
secretEntries, err := afero.ReadDir(fs, secretsPath)
if err != nil {
secret.Warn("Could not read secrets directory for vault", "vault", vaultEntry.Name(), "error", err)
continue
}
for _, secretEntry := range secretEntries {
if !secretEntry.IsDir() {
continue
}
totalSecrets++
secretPath := filepath.Join(secretsPath, secretEntry.Name())
// Get size and timestamps from all versions
versionsPath := filepath.Join(secretPath, "versions")
versionEntries, err := afero.ReadDir(fs, versionsPath)
if err != nil {
secret.Warn("Could not read versions directory for secret", "secret", secretEntry.Name(), "error", err)
continue
}
for _, versionEntry := range versionEntries {
if !versionEntry.IsDir() {
continue
}
versionPath := filepath.Join(versionsPath, versionEntry.Name())
// Add size of encrypted data
dataPath := filepath.Join(versionPath, "data.age")
if stat, err := fs.Stat(dataPath); err == nil {
totalSize += stat.Size()
}
// Add size of metadata
metaPath := filepath.Join(versionPath, "metadata.age")
if stat, err := fs.Stat(metaPath); err == nil {
totalSize += stat.Size()
}
// Track timestamps
if stat, err := fs.Stat(versionPath); err == nil {
modTime := stat.ModTime()
if oldestTime.IsZero() || modTime.Before(oldestTime) {
oldestTime = modTime
}
if latestTime.IsZero() || modTime.After(latestTime) {
latestTime = modTime
}
}
}
}
}
return totalSecrets, totalSize, oldestTime, latestTime, nil
}