feat(models): add alert and box models
Adds comprehensive data structures for Alert and Box functionality across models.
This commit is contained in:
188
lib/metastore/boxes.go
Normal file
188
lib/metastore/boxes.go
Normal file
@@ -0,0 +1,188 @@
|
||||
package metastore
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dgraph-io/badger/v4"
|
||||
)
|
||||
|
||||
func (store *Store) UpsertBoxRecord(record BoxRecord) error {
|
||||
record.ID = strings.TrimSpace(record.ID)
|
||||
if record.ID == "" {
|
||||
return errors.New("box id cannot be empty")
|
||||
}
|
||||
record.OwnerID = strings.TrimSpace(record.OwnerID)
|
||||
record.OwnerUsername = strings.TrimSpace(record.OwnerUsername)
|
||||
record.FileNames = uniqueStrings(record.FileNames)
|
||||
record.UpdatedAt = time.Now().UTC()
|
||||
return store.db.Update(func(txn *badger.Txn) error {
|
||||
return putJSON(txn, boxRecordKey(record.ID), record)
|
||||
})
|
||||
}
|
||||
|
||||
func (store *Store) GetBoxRecord(id string) (BoxRecord, bool, error) {
|
||||
var record BoxRecord
|
||||
err := store.db.View(func(txn *badger.Txn) error {
|
||||
return getJSON(txn, boxRecordKey(id), &record)
|
||||
})
|
||||
if errors.Is(err, ErrNotFound) {
|
||||
return BoxRecord{}, false, nil
|
||||
}
|
||||
return record, err == nil, err
|
||||
}
|
||||
|
||||
func (store *Store) DeleteBoxRecord(id string) error {
|
||||
return store.db.Update(func(txn *badger.Txn) error {
|
||||
err := txn.Delete(boxRecordKey(id))
|
||||
if errors.Is(err, badger.ErrKeyNotFound) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (store *Store) ListBoxRecords(filters BoxFilters, page BoxPageRequest) (BoxRecordPage, error) {
|
||||
if page.Page < 1 {
|
||||
page.Page = 1
|
||||
}
|
||||
switch page.PageSize {
|
||||
case 25, 50, 100:
|
||||
default:
|
||||
page.PageSize = 25
|
||||
}
|
||||
|
||||
rows := []BoxRecord{}
|
||||
err := store.db.View(func(txn *badger.Txn) error {
|
||||
opts := badger.DefaultIteratorOptions
|
||||
opts.Prefix = []byte("box_record/")
|
||||
it := txn.NewIterator(opts)
|
||||
defer it.Close()
|
||||
|
||||
for it.Rewind(); it.Valid(); it.Next() {
|
||||
var record BoxRecord
|
||||
if err := it.Item().Value(func(data []byte) error {
|
||||
return json.Unmarshal(data, &record)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if boxRecordMatches(record, filters) {
|
||||
rows = append(rows, record)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return BoxRecordPage{}, err
|
||||
}
|
||||
|
||||
sortBoxRecords(rows, filters.Sort)
|
||||
total := len(rows)
|
||||
start := (page.Page - 1) * page.PageSize
|
||||
if start > total {
|
||||
start = total
|
||||
}
|
||||
end := start + page.PageSize
|
||||
if end > total {
|
||||
end = total
|
||||
}
|
||||
totalPages := 1
|
||||
if total > 0 {
|
||||
totalPages = (total + page.PageSize - 1) / page.PageSize
|
||||
}
|
||||
return BoxRecordPage{
|
||||
Rows: rows[start:end],
|
||||
Page: page.Page,
|
||||
PageSize: page.PageSize,
|
||||
Total: total,
|
||||
HasPrev: page.Page > 1,
|
||||
HasNext: end < total,
|
||||
PrevPage: maxInt(page.Page-1, 1),
|
||||
NextPage: page.Page + 1,
|
||||
TotalPages: totalPages,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func boxRecordMatches(record BoxRecord, filters BoxFilters) bool {
|
||||
query := strings.ToLower(strings.TrimSpace(filters.Query))
|
||||
if query != "" {
|
||||
haystack := strings.ToLower(record.ID + " " + record.OwnerUsername + " " + strings.Join(record.FileNames, " "))
|
||||
if !strings.Contains(haystack, query) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
owner := strings.ToLower(strings.TrimSpace(filters.Owner))
|
||||
if owner != "" && owner != "all" && strings.ToLower(record.OwnerUsername) != owner && strings.ToLower(record.OwnerID) != owner {
|
||||
return false
|
||||
}
|
||||
status := strings.ToLower(strings.TrimSpace(filters.Status))
|
||||
if status != "" && status != "all" && boxRecordStatus(record) != status {
|
||||
return false
|
||||
}
|
||||
switch strings.ToLower(strings.TrimSpace(filters.Flag)) {
|
||||
case "", "all":
|
||||
return true
|
||||
case "password":
|
||||
return record.PasswordProtected
|
||||
case "one-time":
|
||||
return record.OneTimeDownload
|
||||
case "zip-disabled":
|
||||
return record.DisableZip
|
||||
case "expired":
|
||||
return boxRecordExpired(record)
|
||||
case "refreshable":
|
||||
return !record.OneTimeDownload && !boxRecordExpired(record)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func sortBoxRecords(rows []BoxRecord, sortKey string) {
|
||||
switch strings.ToLower(strings.TrimSpace(sortKey)) {
|
||||
case "oldest":
|
||||
sort.Slice(rows, func(i int, j int) bool { return rows[i].CreatedAt.Before(rows[j].CreatedAt) })
|
||||
case "largest":
|
||||
sort.Slice(rows, func(i int, j int) bool { return rows[i].TotalSize > rows[j].TotalSize })
|
||||
case "expires":
|
||||
sort.Slice(rows, func(i int, j int) bool { return rows[i].ExpiresAt.Before(rows[j].ExpiresAt) })
|
||||
case "expired":
|
||||
sort.Slice(rows, func(i int, j int) bool {
|
||||
left := boxRecordExpired(rows[i])
|
||||
right := boxRecordExpired(rows[j])
|
||||
if left == right {
|
||||
return rows[i].CreatedAt.After(rows[j].CreatedAt)
|
||||
}
|
||||
return left
|
||||
})
|
||||
default:
|
||||
sort.Slice(rows, func(i int, j int) bool { return rows[i].CreatedAt.After(rows[j].CreatedAt) })
|
||||
}
|
||||
}
|
||||
|
||||
func boxRecordStatus(record BoxRecord) string {
|
||||
if boxRecordExpired(record) {
|
||||
return "expired"
|
||||
}
|
||||
if record.ExpiresAt.IsZero() {
|
||||
return "pending"
|
||||
}
|
||||
return "active"
|
||||
}
|
||||
|
||||
func boxRecordExpired(record BoxRecord) bool {
|
||||
return !record.ExpiresAt.IsZero() && time.Now().UTC().After(record.ExpiresAt)
|
||||
}
|
||||
|
||||
func boxRecordKey(id string) []byte {
|
||||
return []byte("box_record/" + strings.TrimSpace(id))
|
||||
}
|
||||
|
||||
func maxInt(a int, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
Reference in New Issue
Block a user