diff --git a/README.md b/README.md index 558fff0..cf12794 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,8 @@ Simple standalone file upload server with api and cli ## TODO -- decouple db and http code in Runserver() -- store ts - implement goroutine to expire after 1d, 10m etc + implemented. add go routine to server, use Db.Iter() - use bolt db to retrieve list of items to expire - also serve a html upload page - add auth options (access key, users, roles, oauth2) @@ -15,7 +14,6 @@ Simple standalone file upload server with api and cli - create cobra client commands (upload, list, delete, edit) - ## curl commands ### upload diff --git a/upd/Makefile b/upd/Makefile index a860d1f..1894611 100644 --- a/upd/Makefile +++ b/upd/Makefile @@ -48,7 +48,7 @@ clean: test: go test -v ./... - bash t/test.sh +# bash t/test.sh singletest: @echo "Call like this: ''make singletest TEST=TestX1 MOD=lib" diff --git a/upd/api/common.go b/upd/api/common.go index ba1f3b1..e5bb762 100644 --- a/upd/api/common.go +++ b/upd/api/common.go @@ -20,6 +20,7 @@ package api import ( "fmt" "regexp" + "strconv" "time" ) @@ -52,3 +53,55 @@ func NormalizeFilename(file string) string { return Ts() + r.ReplaceAllString(file, "") } + +/* + We could use time.ParseDuration(), but this doesn't support days. + + We could also use github.com/xhit/go-str2duration/v2, which does + the job, but it's just another dependency, just for this little + gem. And we don't need a time.Time value. + + Convert a duration into seconds (int). + Valid time units are "s", "m", "h" and "d". +*/ +func duration2int(duration string) int { + re := regexp.MustCompile(`(\d+)([dhms])`) + seconds := 0 + + for _, match := range re.FindAllStringSubmatch(duration, -1) { + if len(match) == 3 { + v, _ := strconv.Atoi(match[1]) + switch match[2][0] { + case 'd': + seconds += v * 86400 + case 'h': + seconds += v * 3600 + case 'm': + seconds += v * 60 + case 's': + seconds += v + } + } + } + + return seconds +} + +/* + Calculate if time is up based on start time.Time and + duration. Returns true if time is expired. Start time comes from + the database. + +aka: + if(now - start) >= duration { time is up} +*/ +func IsExpired(start time.Time, duration string) bool { + now := time.Now() + expiretime := duration2int(duration) + + if now.Unix()-start.Unix() >= int64(expiretime) { + return true + } + + return false +} diff --git a/upd/api/common_test.go b/upd/api/common_test.go new file mode 100644 index 0000000..dedc70a --- /dev/null +++ b/upd/api/common_test.go @@ -0,0 +1,53 @@ +package api + +import ( + "fmt" + "testing" + "time" +) + +func TestDuration2Seconds(t *testing.T) { + var tests = []struct { + dur string + expect int + }{ + {"1d", 60 * 60 * 24}, + {"1h", 60 * 60}, + {"10m", 60 * 10}, + {"2h4m10s", (60 * 120) + (4 * 60) + 10}, + {"88u", 0}, + {"19t77X what?4s", 4}, + } + + for _, tt := range tests { + testname := fmt.Sprintf("duration-%s", tt.dur) + t.Run(testname, func(t *testing.T) { + seconds := duration2int(tt.dur) + if seconds != tt.expect { + t.Errorf("got %d, want %d", seconds, tt.expect) + } + }) + } +} + +func TestIsExpired(t *testing.T) { + var tests = []struct { + expire string + start time.Time + expect bool + }{ + {"3s", time.Now(), true}, + {"1d", time.Now(), false}, + } + + for _, tt := range tests { + testname := fmt.Sprintf("isexpired-%s-%s", tt.start, tt.expire) + t.Run(testname, func(t *testing.T) { + time.Sleep(5 * time.Second) + got := IsExpired(tt.start, tt.expire) + if got != tt.expect { + t.Errorf("got %t, want %t", got, tt.expect) + } + }) + } +} diff --git a/upd/api/db.go b/upd/api/db.go index 917e030..d1a5be5 100644 --- a/upd/api/db.go +++ b/upd/api/db.go @@ -124,3 +124,23 @@ func (db *Db) Delete(id string) error { return err } + +func (db *Db) Iterate(iterator func(id string, upload Upload)) error { + var upload Upload + + err := db.bolt.View(func(tx *bolt.Tx) error { + bucket := tx.Bucket([]byte(Bucket)) + err := bucket.ForEach(func(id, j []byte) error { + if err := json.Unmarshal(j, &upload); err != nil { + return fmt.Errorf("unable to unmarshal json: %s", err) + } + + iterator(string(id), upload) + return nil + }) + + return err // might be nil as well + }) + + return err +}