docs: expand configuration docs for admin and BadgerDB

Update README to explain startup config precedence (defaults/env/admin overrides),
document admin/bootstrap and feature toggles, and clarify storage locations under
WARPBOX_DATA_DIR including BadgerDB metadata. Also refresh project layout to
include new config and metastore packages.docs: expand configuration docs for admin and BadgerDB

Update README to explain startup config precedence (defaults/env/admin overrides),
document admin/bootstrap and feature toggles, and clarify storage locations under
WARPBOX_DATA_DIR including BadgerDB metadata. Also refresh project layout to
include new config and metastore packages.
This commit is contained in:
2026-04-28 21:11:37 +03:00
parent fc3de58b5b
commit a5d6d69be0
27 changed files with 3499 additions and 97 deletions

View File

@@ -13,6 +13,7 @@ import (
"net/url"
"os"
"path/filepath"
"sort"
"strings"
"sync"
"time"
@@ -22,13 +23,15 @@ import (
)
const (
UploadRoot = "data/uploads"
manifestFile = ".warpbox.json"
OneTimeDownloadRetentionKey = "one-time"
)
var manifestMu sync.Mutex
var (
uploadRoot = filepath.Join("data", "uploads")
manifestMu sync.Mutex
)
var retentionOptions = []models.RetentionOption{
{Key: "10s", Label: "10 seconds", Seconds: 10},
@@ -58,8 +61,19 @@ func DefaultRetentionOption() models.RetentionOption {
return retentionOptions[0]
}
func SetUploadRoot(path string) {
if path == "" {
return
}
uploadRoot = filepath.Clean(path)
}
func UploadRoot() string {
return uploadRoot
}
func BoxPath(boxID string) string {
return filepath.Join(UploadRoot, boxID)
return filepath.Join(uploadRoot, boxID)
}
func ManifestPath(boxID string) string {
@@ -74,6 +88,71 @@ func DeleteBox(boxID string) error {
return os.RemoveAll(BoxPath(boxID))
}
func ListBoxSummaries() ([]models.BoxSummary, error) {
entries, err := os.ReadDir(uploadRoot)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, err
}
summaries := make([]models.BoxSummary, 0, len(entries))
for _, entry := range entries {
if !entry.IsDir() || !ValidBoxID(entry.Name()) {
continue
}
summary, err := BoxSummary(entry.Name())
if err != nil {
continue
}
summaries = append(summaries, summary)
}
sort.Slice(summaries, func(i int, j int) bool {
return summaries[i].CreatedAt.After(summaries[j].CreatedAt)
})
return summaries, nil
}
func BoxSummary(boxID string) (models.BoxSummary, error) {
files, err := ListFiles(boxID)
if err != nil {
return models.BoxSummary{}, err
}
var manifest models.BoxManifest
hasManifest := false
if readManifest, err := ReadManifest(boxID); err == nil {
manifest = readManifest
hasManifest = true
}
totalSize := int64(0)
for _, file := range files {
totalSize += file.Size
}
summary := models.BoxSummary{
ID: boxID,
FileCount: len(files),
TotalSize: totalSize,
TotalSizeLabel: helpers.FormatBytes(totalSize),
}
if hasManifest {
summary.CreatedAt = manifest.CreatedAt
summary.ExpiresAt = manifest.ExpiresAt
summary.Expired = IsExpired(manifest)
summary.OneTimeDownload = manifest.OneTimeDownload
summary.PasswordProtected = IsPasswordProtected(manifest)
} else if info, err := os.Stat(BoxPath(boxID)); err == nil {
summary.CreatedAt = info.ModTime().UTC()
}
return summary, nil
}
func ListFiles(boxID string) ([]models.BoxFile, error) {
if manifest, err := reconcileManifest(boxID); err == nil && len(manifest.Files) > 0 {
files := make([]models.BoxFile, 0, len(manifest.Files))
@@ -240,6 +319,21 @@ func WriteManifest(boxID string, manifest models.BoxManifest) error {
return writeManifestUnlocked(boxID, manifest)
}
func RenewManifest(boxID string, seconds int64) (models.BoxManifest, error) {
manifestMu.Lock()
defer manifestMu.Unlock()
manifest, err := readManifestUnlocked(boxID)
if err != nil {
return manifest, err
}
if seconds <= 0 || manifest.OneTimeDownload || manifest.ExpiresAt.IsZero() {
return manifest, nil
}
manifest.ExpiresAt = time.Now().UTC().Add(time.Duration(seconds) * time.Second)
return manifest, writeManifestUnlocked(boxID, manifest)
}
func AddFileToZip(zipWriter *zip.Writer, boxID string, filename string) error {
source, err := os.Open(filepath.Join(BoxPath(boxID), filename))
if err != nil {

View File

@@ -4,8 +4,8 @@ import (
"image"
"image/color"
"image/draw"
"image/jpeg"
_ "image/gif"
"image/jpeg"
_ "image/png"
"net/url"
"os"
@@ -66,7 +66,7 @@ func ThumbnailFilePath(boxID string, fileID string) (string, bool) {
}
func collectThumbnailTasks(batchSize int) []thumbnailTask {
entries, err := os.ReadDir(UploadRoot)
entries, err := os.ReadDir(uploadRoot)
if err != nil {
return nil
}