/* Copyright © 2023 Thomas von Dein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package api import ( "encoding/json" "fmt" "github.com/alecthomas/repr" bolt "go.etcd.io/bbolt" ) const Bucket string = "uploads" // wrapper for bolt db type Db struct { bolt *bolt.DB } // stores 1 upload object, gets into db type Upload struct { Id string `json:"id"` Expire string `json:"expire"` File string `json:"file"` // final filename (visible to the downloader) Members []string `json:"members"` // contains multiple files, so File is an archive Uploaded Timestamp `json:"uploaded"` Context string `json:"context"` } type Uploads struct { Entries []*Upload `json:"uploads"` } func NewDb(file string) (*Db, error) { b, err := bolt.Open(file, 0600, nil) db := Db{bolt: b} return &db, err } func (db *Db) Close() { db.bolt.Close() } func (db *Db) Insert(id string, entry *Upload) error { err := db.bolt.Update(func(tx *bolt.Tx) error { bucket, err := tx.CreateBucketIfNotExists([]byte(Bucket)) if err != nil { return fmt.Errorf("create bucket: %s", err) } jsonentry, err := json.Marshal(entry) if err != nil { return fmt.Errorf("json marshalling failure: %s", err) } err = bucket.Put([]byte(id), []byte(jsonentry)) if err != nil { return fmt.Errorf("insert data: %s", err) } // results in: // bbolt get /tmp/uploads.db uploads fb242922-86cb-43a8-92bc-b027c35f0586 // {"id":"fb242922-86cb-43a8-92bc-b027c35f0586","expire":"1d","file":"2023-02-17-13-09-data.zip"} return nil }) if err != nil { Log("DB error: %s", err.Error()) } return err } func (db *Db) Lookup(id string) (Upload, error) { var upload Upload err := db.bolt.View(func(tx *bolt.Tx) error { bucket := tx.Bucket([]byte(Bucket)) j := bucket.Get([]byte(id)) if len(j) == 0 { return fmt.Errorf("id %s not found", id) } if err := json.Unmarshal(j, &upload); err != nil { return fmt.Errorf("unable to unmarshal json: %s", err) } return nil }) if err != nil { Log("DB error: %s", err.Error()) return upload, err } return upload, nil } func (db *Db) Delete(id string) error { err := db.bolt.Update(func(tx *bolt.Tx) error { bucket := tx.Bucket([]byte(Bucket)) j := bucket.Get([]byte(id)) if len(j) == 0 { return fmt.Errorf("id %s not found", id) } err := bucket.Delete([]byte(id)) return err }) if err != nil { Log("DB error: %s", err.Error()) } return err } func (db *Db) List(apicontext string) (*Uploads, error) { uploads := &Uploads{} err := db.bolt.View(func(tx *bolt.Tx) error { bucket := tx.Bucket([]byte(Bucket)) err := bucket.ForEach(func(id, j []byte) error { upload := &Upload{} if err := json.Unmarshal(j, &upload); err != nil { return fmt.Errorf("unable to unmarshal json: %s", err) } if apicontext != "" { if apicontext == upload.Context { uploads.Entries = append(uploads.Entries, upload) } } else { uploads.Entries = append(uploads.Entries, upload) } return nil }) return err // might be nil as well }) return uploads, err }