Use in-memory for presigned URLs instead of database, added tests

This commit is contained in:
Marc Ole Bulling
2026-01-26 18:49:32 +01:00
parent cea7900616
commit d52ea3f3cc
16 changed files with 282 additions and 174 deletions
@@ -18,7 +18,6 @@ var db dbabstraction.Database
// Connect establishes a connection to the database and creates the table structure, if necessary
func Connect(config models.DbConnection) {
var err error
dbcache.Init()
db, err = dbabstraction.GetNew(config)
if err != nil {
panic(err)
@@ -354,20 +353,3 @@ func SaveFileRequest(request models.FileRequest) {
func DeleteFileRequest(request models.FileRequest) {
db.DeleteFileRequest(request)
}
// Presigned URLs
// GetPresignedUrl returns the presigned url with the given ID or false if not a valid ID
func GetPresignedUrl(id string) (models.Presign, bool) {
return db.GetPresignedUrl(id)
}
// DeletePresignedUrl deletes the presigned url with the given ID
func DeletePresignedUrl(id string) {
db.DeletePresignedUrl(id)
}
// SavePresignedUrl saves the presigned url
func SavePresignedUrl(presign models.Presign) {
db.SavePresignedUrl(presign)
}
@@ -279,7 +279,7 @@ func TestUsers(t *testing.T) {
}, 2)
test.IsEqual(t, allUsersSqlite, allUsersRedis)
runAllTypesNoOutput(t, func() {
dbcache.Init()
dbcache.ResetAll()
UpdateUserLastOnline(1)
})
runAllTypesCompareTwoOutputs(t, func() (any, any) {
@@ -108,13 +108,6 @@ type Database interface {
SaveFileRequest(request models.FileRequest)
// DeleteFileRequest deletes a file request with the given ID
DeleteFileRequest(request models.FileRequest)
// GetPresignedUrl returns the presigned url with the given ID or false if not a valid ID
GetPresignedUrl(id string) (models.Presign, bool)
// DeletePresignedUrl deletes the presigned url with the given ID
DeletePresignedUrl(id string)
// SavePresignedUrl saves the presigned url
SavePresignedUrl(presign models.Presign)
}
// GetNew connects to the given database and initialises it
@@ -5,14 +5,9 @@ import (
"time"
)
var lastOnlineTimeUpdate map[int]int64
var lastOnlineTimeUpdate = make(map[int]int64)
var lastOnlineTimeMutex sync.Mutex
// Init starts the DB Cache
func Init() {
lastOnlineTimeUpdate = make(map[int]int64)
}
// LastOnlineRequiresSave returns true if the last update time of the user is older than 60 seconds.
func LastOnlineRequiresSave(userId int) bool {
lastOnlineTimeMutex.Lock()
@@ -24,3 +19,10 @@ func LastOnlineRequiresSave(userId int) bool {
}
return false
}
// ResetAll is only used for testing purposes
func ResetAll() {
lastOnlineTimeMutex.Lock()
lastOnlineTimeUpdate = make(map[int]int64)
lastOnlineTimeMutex.Unlock()
}
@@ -0,0 +1,25 @@
package dbcache
import (
"testing"
"testing/synctest"
"time"
"github.com/forceu/gokapi/internal/test"
)
func TestLastOnlineRequiresSave(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
test.IsEqualBool(t, LastOnlineRequiresSave(100), true)
test.IsEqualBool(t, LastOnlineRequiresSave(100), false)
time.Sleep(61 * time.Second)
test.IsEqualBool(t, LastOnlineRequiresSave(100), true)
})
}
func TestResetAll(t *testing.T) {
LastOnlineRequiresSave(100)
test.IsEqualInt(t, len(lastOnlineTimeUpdate), 1)
ResetAll()
test.IsEqualInt(t, len(lastOnlineTimeUpdate), 0)
}
@@ -1,34 +0,0 @@
package redis
import (
"github.com/forceu/gokapi/internal/helper"
"github.com/forceu/gokapi/internal/models"
redigo "github.com/gomodule/redigo/redis"
)
const (
prefixPresign = "ps:"
)
// GetPresignedUrl returns the presigned url with the given ID or false if not a valid ID
func (p DatabaseProvider) GetPresignedUrl(id string) (models.Presign, bool) {
hashmapEntry, ok := p.getHashMap(prefixPresign + id)
if !ok {
return models.Presign{}, false
}
var result models.Presign
err := redigo.ScanStruct(hashmapEntry, &result)
helper.Check(err)
return result, true
}
// SavePresignedUrl saves the presigned url
func (p DatabaseProvider) SavePresignedUrl(presign models.Presign) {
p.setHashMap(p.buildArgs(prefixPresign + presign.Id).AddFlat(presign))
p.setExpiryAt(prefixPresign+presign.Id, presign.Expiry)
}
// DeletePresignedUrl deletes the presigned url with the given ID
func (p DatabaseProvider) DeletePresignedUrl(id string) {
p.deleteKey(prefixPresign + id)
}
@@ -62,14 +62,7 @@ func (p DatabaseProvider) Upgrade(currentDbVersion int) {
"apiKey" TEXT NOT NULL UNIQUE,
"note" TEXT NOT NULL,
PRIMARY KEY("id")
);
CREATE TABLE "Presign" (
"id" TEXT NOT NULL UNIQUE,
"fileIds" TEXT NOT NULL,
"expiry" INTEGER NOT NULL,
"filename" TEXT NOT NULL,
PRIMARY KEY("id")
);`)
);`)
helper.Check(err)
grantUploadPerm := environment.New().PermRequestGrantedByDefault
for _, user := range p.GetAllUsers() {
@@ -154,7 +147,6 @@ func (p DatabaseProvider) Close() {
func (p DatabaseProvider) RunGarbageCollection() {
p.cleanExpiredSessions()
p.cleanApiKeys()
p.cleanPresignedUrls()
}
func (p DatabaseProvider) createNewDatabase() error {
@@ -231,15 +223,7 @@ func (p DatabaseProvider) createNewDatabase() error {
"apiKey" TEXT NOT NULL UNIQUE,
"note" TEXT NOT NULL,
PRIMARY KEY("id")
);
CREATE TABLE "Presign" (
"id" TEXT NOT NULL UNIQUE,
"fileIds" TEXT NOT NULL,
"expiry" INTEGER NOT NULL,
"filename" TEXT NOT NULL,
PRIMARY KEY("id")
);
`
);`
err := p.rawSqlite(sqlStmt)
if err != nil {
return err
@@ -1,56 +0,0 @@
package sqlite
import (
"database/sql"
"errors"
"strings"
"github.com/forceu/gokapi/internal/helper"
"github.com/forceu/gokapi/internal/models"
)
type schemaPresign struct {
Id string
FileId string
Expiry int64
Filename string
}
// GetPresignedUrl returns the presigned url with the given ID or false if not a valid ID
func (p DatabaseProvider) GetPresignedUrl(id string) (models.Presign, bool) {
var rowResult schemaPresign
row := p.sqliteDb.QueryRow("SELECT * FROM Presign WHERE id = ?", id)
err := row.Scan(&rowResult.Id, &rowResult.FileId, &rowResult.Expiry, &rowResult.Filename)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return models.Presign{}, false
}
helper.Check(err)
return models.Presign{}, false
}
result := models.Presign{
Id: rowResult.Id,
FileIds: strings.Split(rowResult.FileId, ","),
Expiry: rowResult.Expiry,
Filename: rowResult.Filename,
}
return result, true
}
// SavePresignedUrl saves the presigned url
func (p DatabaseProvider) SavePresignedUrl(presign models.Presign) {
_, err := p.sqliteDb.Exec("INSERT OR REPLACE INTO Presign (id, fileIds, expiry, filename) VALUES (?, ?, ?, ?)",
presign.Id, strings.Join(presign.FileIds, ","), presign.Expiry, presign.Filename)
helper.Check(err)
}
// DeletePresignedUrl deletes the presigned url with the given ID
func (p DatabaseProvider) DeletePresignedUrl(id string) {
_, err := p.sqliteDb.Exec("DELETE FROM Presign WHERE id = ?", id)
helper.Check(err)
}
func (p DatabaseProvider) cleanPresignedUrls() {
_, err := p.sqliteDb.Exec("DELETE FROM Presign WHERE expiry < ?", currentTime().Unix())
helper.Check(err)
}
+49 -15
View File
@@ -13,25 +13,18 @@ func TestApiKey_GetRedactedId(t *testing.T) {
func TestSetPermission(t *testing.T) {
key := &ApiKey{}
test.IsEqualBool(t, key.HasPermission(ApiPermView), false)
key.GrantPermission(ApiPermView)
if !key.HasPermission(ApiPermView) {
t.Errorf("expected permission %d to be set", ApiPermView)
}
if key.HasPermission(ApiPermEdit) {
t.Errorf("expected permission %d to be not set", ApiPermEdit)
}
test.IsEqualBool(t, key.HasPermission(ApiPermView), true)
test.IsEqualBool(t, key.HasPermission(ApiPermEdit), false)
}
func TestRemovePermission(t *testing.T) {
key := &ApiKey{}
key.GrantPermission(ApiPermView)
if !key.HasPermission(ApiPermView) {
t.Errorf("expected permission %d to be set", ApiPermView)
}
test.IsEqualBool(t, key.HasPermission(ApiPermView), true)
key.RemovePermission(ApiPermView)
if key.HasPermission(ApiPermView) {
t.Errorf("expected permission %d to be removed", ApiPermView)
}
test.IsEqualBool(t, key.HasPermission(ApiPermView), false)
}
func TestHasPermission(t *testing.T) {
@@ -53,9 +46,7 @@ func TestHasPermission(t *testing.T) {
func TestHasPermissionView(t *testing.T) {
key := &ApiKey{}
if key.HasPermissionView() {
t.Errorf("expected view permission to be not set")
}
test.IsEqualBool(t, key.HasPermissionManageFileRequests(), false)
key.GrantPermission(ApiPermView)
if !key.HasPermissionView() {
t.Errorf("expected view permission to be set")
@@ -64,6 +55,7 @@ func TestHasPermissionView(t *testing.T) {
func TestHasPermissionUpload(t *testing.T) {
key := &ApiKey{}
test.IsEqualBool(t, key.HasPermissionManageFileRequests(), false)
key.GrantPermission(ApiPermUpload)
if !key.HasPermissionUpload() {
t.Errorf("expected upload permission to be set")
@@ -72,6 +64,7 @@ func TestHasPermissionUpload(t *testing.T) {
func TestHasPermissionDelete(t *testing.T) {
key := &ApiKey{}
test.IsEqualBool(t, key.HasPermissionManageFileRequests(), false)
key.GrantPermission(ApiPermDelete)
if !key.HasPermissionDelete() {
t.Errorf("expected delete permission to be set")
@@ -80,6 +73,7 @@ func TestHasPermissionDelete(t *testing.T) {
func TestHasPermissionApiMod(t *testing.T) {
key := &ApiKey{}
test.IsEqualBool(t, key.HasPermissionManageFileRequests(), false)
key.GrantPermission(ApiPermApiMod)
if !key.HasPermissionApiMod() {
t.Errorf("expected ApiMod permission to be set")
@@ -88,6 +82,7 @@ func TestHasPermissionApiMod(t *testing.T) {
func TestHasPermissionEdit(t *testing.T) {
key := &ApiKey{}
test.IsEqualBool(t, key.HasPermissionManageFileRequests(), false)
key.GrantPermission(ApiPermEdit)
if !key.HasPermissionEdit() {
t.Errorf("expected edit permission to be set")
@@ -96,6 +91,7 @@ func TestHasPermissionEdit(t *testing.T) {
func TestHasPermissionReplace(t *testing.T) {
key := &ApiKey{}
test.IsEqualBool(t, key.HasPermissionManageFileRequests(), false)
key.GrantPermission(ApiPermReplace)
if !key.HasPermissionReplace() {
t.Errorf("expected edit permission to be set")
@@ -103,6 +99,7 @@ func TestHasPermissionReplace(t *testing.T) {
}
func TestHasPermissionManageUsers(t *testing.T) {
key := &ApiKey{}
test.IsEqualBool(t, key.HasPermissionManageFileRequests(), false)
key.GrantPermission(ApiPermManageUsers)
if !key.HasPermissionManageUsers() {
t.Errorf("expected edit permission to be set")
@@ -110,12 +107,26 @@ func TestHasPermissionManageUsers(t *testing.T) {
}
func TestHasPermissionManageLogs(t *testing.T) {
key := &ApiKey{}
test.IsEqualBool(t, key.HasPermissionManageFileRequests(), false)
key.GrantPermission(ApiPermManageLogs)
if !key.HasPermissionManageLogs() {
t.Errorf("expected edit permission to be set")
}
}
func TestHasPermissionManageFileRequests(t *testing.T) {
key := &ApiKey{}
test.IsEqualBool(t, key.HasPermissionManageFileRequests(), false)
key.GrantPermission(ApiPermManageFileRequests)
test.IsEqualBool(t, key.HasPermissionManageFileRequests(), true)
}
func TestHasPPermissionDownload(t *testing.T) {
key := &ApiKey{}
test.IsEqualBool(t, key.HasPermissionDownload(), false)
key.GrantPermission(ApiPermDownload)
test.IsEqualBool(t, key.HasPermissionDownload(), true)
}
func TestApiPermAllNoApiMod(t *testing.T) {
key := &ApiKey{}
key.GrantPermission(ApiPermDefault)
@@ -141,6 +152,8 @@ func checkOnlyPermissionSet(t *testing.T, key *ApiKey, perm ApiPermission) {
{ApiPermReplace, "ApiPermReplace"},
{ApiPermManageUsers, "ApiPermManageUsers"},
{ApiPermManageLogs, "ApiPermManageLogs"},
{ApiPermManageFileRequests, "ApiPermManageFileRequests"},
{ApiPermDownload, "ApiPermDownload"},
}
for _, p := range allPermissions {
@@ -172,6 +185,8 @@ func TestSetIndividualPermissions(t *testing.T) {
{ApiPermReplace, "ApiPermReplace"},
{ApiPermManageUsers, "ApiPermManageUsers"},
{ApiPermManageLogs, "ApiPermManageLogs"},
{ApiPermManageFileRequests, "ApiPermManageFileRequests"},
{ApiPermDownload, "ApiPermDownload"},
}
for _, p := range permissions {
@@ -201,6 +216,8 @@ func TestSetCombinedPermissions(t *testing.T) {
ApiPermReplace,
ApiPermManageUsers,
ApiPermManageLogs,
ApiPermManageFileRequests,
ApiPermDownload,
}
// Test setting permissions in combination
@@ -213,6 +230,13 @@ func TestSetCombinedPermissions(t *testing.T) {
}
}
func TestApiKey_IsUploadRequestKey(t *testing.T) {
key := &ApiKey{Permissions: ApiPermManageFileRequests, UploadRequestId: "test"}
test.IsEqualBool(t, key.IsUploadRequestKey(), true)
key.UploadRequestId = ""
test.IsEqualBool(t, key.IsUploadRequestKey(), false)
}
func TestApiPermissionFromString(t *testing.T) {
tests := []struct {
name string
@@ -260,6 +284,16 @@ func TestApiPermissionFromString(t *testing.T) {
input: "PERM_MANAGE_LOGS",
wantPerm: ApiPermManageLogs,
},
{
name: "PERM_MANAGE_FILE_REQUESTS",
input: "PERM_MANAGE_FILE_REQUESTS",
wantPerm: ApiPermManageFileRequests,
},
{
name: "PERM_DOWNLOAD",
input: "PERM_DOWNLOAD",
wantPerm: ApiPermDownload,
},
{
name: "invalid permission",
input: "PERM_UNKNOWN",
+11 -10
View File
@@ -2,7 +2,6 @@ package models
import (
"encoding/json"
"log"
)
// Configuration is a struct that contains the global configuration
@@ -26,7 +25,7 @@ type Configuration struct {
IncludeFilename bool `json:"IncludeFilename"`
}
// Encryption hold information about the encryption used on this file
// Encryption holds information about the encryption used on this file
type Encryption struct {
Level int
Cipher []byte
@@ -35,20 +34,22 @@ type Encryption struct {
ChecksumSalt string
}
// ToJson returns an idented JSon representation
// ToJson returns an indented Json representation
func (c Configuration) ToJson() []byte {
result, err := json.MarshalIndent(c, "", " ")
if err != nil {
log.Fatal("Error encoding configuration:", err)
}
checkError(err)
return result
}
// ToString returns the object as an unidented Json string used for test units
// ToString returns the object as an unindented JSON string used for test units
func (c Configuration) ToString() string {
result, err := json.Marshal(c)
if err != nil {
log.Fatal(err)
}
checkError(err)
return string(result)
}
func checkError(err error) {
if err != nil {
panic(err)
}
}
+8 -2
View File
@@ -1,6 +1,7 @@
package models
import (
"errors"
"strings"
"testing"
@@ -43,7 +44,12 @@ func TestConfiguration_ToJson(t *testing.T) {
}
func TestConfiguration_ToString(t *testing.T) {
test.IsEqualString(t, testConfig.ToString(), exptectedUnidentedOutput)
test.IsEqualString(t, testConfig.ToString(), expectedUnindentedOutput)
}
const exptectedUnidentedOutput = `{"Authentication":{"Method":0,"SaltAdmin":"saltadmin","SaltFiles":"saltfiles","Username":"admin","HeaderKey":"","OauthProvider":"","OAuthClientId":"","OAuthClientSecret":"","OauthGroupScope":"","OAuthRecheckInterval":0,"OAuthGroups":null,"OnlyRegisteredUsers":false},"Port":":12345","ServerUrl":"https://testserver.com/","RedirectUrl":"https://test.com","PublicName":"public-name","DataDir":"test","DatabaseUrl":"sqlite://./test/gokapitest.sqlite","ConfigVersion":14,"MaxFileSizeMB":20,"MaxMemory":50,"ChunkSize":0,"MaxParallelUploads":0,"Encryption":{"Level":1,"Cipher":"AA==","Salt":"encsalt","Checksum":"encsum","ChecksumSalt":"encsumsalt"},"UseSsl":true,"PicturesAlwaysLocal":true,"SaveIp":false,"IncludeFilename":false}`
func TestCheck(t *testing.T) {
defer test.ExpectPanic(t)
checkError(errors.New("test"))
}
const expectedUnindentedOutput = `{"Authentication":{"Method":0,"SaltAdmin":"saltadmin","SaltFiles":"saltfiles","Username":"admin","HeaderKey":"","OauthProvider":"","OAuthClientId":"","OAuthClientSecret":"","OauthGroupScope":"","OAuthRecheckInterval":0,"OAuthGroups":null,"OnlyRegisteredUsers":false},"Port":":12345","ServerUrl":"https://testserver.com/","RedirectUrl":"https://test.com","PublicName":"public-name","DataDir":"test","DatabaseUrl":"sqlite://./test/gokapitest.sqlite","ConfigVersion":14,"MaxFileSizeMB":20,"MaxMemory":50,"ChunkSize":0,"MaxParallelUploads":0,"Encryption":{"Level":1,"Cipher":"AA==","Salt":"encsalt","Checksum":"encsum","ChecksumSalt":"encsumsalt"},"UseSsl":true,"PicturesAlwaysLocal":true,"SaveIp":false,"IncludeFilename":false}`
+84
View File
@@ -0,0 +1,84 @@
package models
import (
"testing"
"time"
"github.com/forceu/gokapi/internal/test"
)
func TestFileRequest_PopulateAndHelpers(t *testing.T) {
now := time.Now().Unix()
files := map[string]File{
"file1": {
Id: "file1",
UploadRequestId: "req1",
SizeBytes: 1000,
UploadDate: now - 100,
},
"file2": {
Id: "file2",
UploadRequestId: "req1",
SizeBytes: 2000,
UploadDate: now,
},
"file3": {
Id: "file3",
UploadRequestId: "other",
SizeBytes: 9999,
UploadDate: now,
},
}
fr := &FileRequest{
Id: "req1",
MaxFiles: 5,
MaxSize: 10,
}
fr.Populate(files, 8)
test.IsEqualInt(t, fr.UploadedFiles, 2)
test.IsEqualInt(t, fr.MaxFiles, 5)
test.IsEqualInt(t, fr.CombinedMaxSize, 8)
test.IsEqualInt(t, fr.FilesRemaining(), 3)
test.IsEqualInt64(t, fr.TotalFileSize, int64(3000))
test.IsEqualInt64(t, fr.LastUpload, now)
test.IsEqualInt(t, len(fr.FileIdList), 2)
test.IsNotEqualString(t, fr.GetReadableDateLastUpdate(), "None")
test.IsNotEqualString(t, fr.GetFilesAsString(), "")
fr = &FileRequest{
Id: "req2",
UploadedFiles: 5,
MaxFiles: 2,
TotalFileSize: 102400,
}
test.IsEqualInt(t, fr.FilesRemaining(), 0)
test.IsEqualString(t, fr.GetReadableDateLastUpdate(), "None")
test.IsEqualString(t, fr.GetReadableTotalSize(), "100.0 kB")
}
func TestFileRequest_UnlimitedFlags(t *testing.T) {
fr := &FileRequest{
MaxFiles: 0,
MaxSize: 0,
Expiry: 0,
}
test.IsEqualBool(t, fr.IsUnlimitedFiles(), true)
test.IsEqualBool(t, fr.IsUnlimitedSize(), true)
test.IsEqualBool(t, fr.IsUnlimitedTime(), true)
test.IsEqualBool(t, !fr.HasRestrictions(), true)
}
func TestFileRequest_IsExpired(t *testing.T) {
fr := &FileRequest{
Expiry: time.Now().Unix() - 10,
}
test.IsEqualBool(t, fr.IsExpired(), true)
}
+25 -1
View File
@@ -16,7 +16,8 @@ func TestUserPermAll(t *testing.T) {
!user.HasPermission(UserPermReplaceOtherUploads) ||
!user.HasPermission(UserPermManageLogs) ||
!user.HasPermission(UserPermManageApiKeys) ||
!user.HasPermission(UserPermManageUsers) {
!user.HasPermission(UserPermManageUsers) ||
!user.HasPermission(UserPermGuestUploads) {
t.Errorf("expected all permissions to be set")
}
}
@@ -35,6 +36,7 @@ func checkOnlyUserPermissionSet(t *testing.T, user *User, perm UserPermission) {
{UserPermManageLogs, "UserPermManageLogs"},
{UserPermManageApiKeys, "UserPermManageApiKeys"},
{UserPermManageUsers, "UserPermManageUsers"},
{UserPermGuestUploads, "UserPermGuestUploads"},
}
for _, p := range allPermissions {
@@ -66,6 +68,7 @@ func TestSetIndividualUserPermissions(t *testing.T) {
{UserPermManageLogs, "UserPermManageLogs"},
{UserPermManageApiKeys, "UserPermManageApiKeys"},
{UserPermManageUsers, "UserPermManageUsers"},
{UserPermGuestUploads, "UserPermGuestUploads"},
}
for _, p := range permissions {
@@ -95,6 +98,7 @@ func TestSetCombinedUserPermissions(t *testing.T) {
UserPermManageLogs,
UserPermManageApiKeys,
UserPermManageUsers,
UserPermGuestUploads,
}
// Test setting permissions in combination
@@ -133,6 +137,19 @@ func TestUser_IsSuperAdmin(t *testing.T) {
test.IsEqualBool(t, user.IsSuperAdmin(), false)
}
func TestUser_IsAdmin(t *testing.T) {
user := &User{
UserLevel: UserLevelSuperAdmin,
}
test.IsEqualBool(t, user.IsAdmin(), true)
user.UserLevel = UserLevelAdmin
test.IsEqualBool(t, user.IsAdmin(), true)
user.UserLevel = UserLevelUser
test.IsEqualBool(t, user.IsAdmin(), false)
user.UserLevel = 4
test.IsEqualBool(t, user.IsAdmin(), false)
}
func TestUser_IsSameUser(t *testing.T) {
user := &User{
Id: 5,
@@ -239,6 +256,13 @@ func TestUser_HasPermissionManageUsers(t *testing.T) {
test.IsEqualBool(t, user.HasPermissionManageUsers(), true)
}
func TestUser_HasPermissionManageGuestUploads(t *testing.T) {
user := &User{}
test.IsEqualBool(t, user.HasPermissionCreateFileRequests(), false)
user.GrantPermission(UserPermGuestUploads)
test.IsEqualBool(t, user.HasPermissionCreateFileRequests(), true)
}
func TestUser_ToJson(t *testing.T) {
user := &User{
Id: 4,
+61
View File
@@ -0,0 +1,61 @@
package presign
import (
"sync"
"time"
"github.com/forceu/gokapi/internal/models"
)
var presignedUrls = make(map[string]models.Presign)
var mutex sync.RWMutex
var cleanupStarted = false
// Save saves the presigned url
func Save(presign models.Presign) {
mutex.Lock()
presignedUrls[presign.Id] = presign
mutex.Unlock()
go cleanUp(true)
}
// Get returns the presigned url with the given ID or false if not a valid ID
func Get(id string) (models.Presign, bool) {
mutex.RLock()
defer mutex.RUnlock()
result, ok := presignedUrls[id]
if !ok {
return models.Presign{}, false
}
if result.Expiry < time.Now().Unix() {
return models.Presign{}, false
}
return result, true
}
// Delete deletes the presigned url with the given ID
func Delete(id string) {
mutex.Lock()
delete(presignedUrls, id)
mutex.Unlock()
}
func cleanUp(periodic bool) {
if cleanupStarted {
return
}
cleanupStarted = true
mutex.Lock()
for k, v := range presignedUrls {
if v.Expiry < time.Now().Unix() {
delete(presignedUrls, k)
}
}
mutex.Unlock()
if periodic {
select {
case <-time.After(20 * time.Minute):
go cleanUp(true)
}
}
}
+6 -5
View File
@@ -32,6 +32,7 @@ import (
"github.com/forceu/gokapi/internal/models"
"github.com/forceu/gokapi/internal/storage"
"github.com/forceu/gokapi/internal/storage/filerequest"
"github.com/forceu/gokapi/internal/storage/presign"
"github.com/forceu/gokapi/internal/webserver/api"
"github.com/forceu/gokapi/internal/webserver/authentication"
"github.com/forceu/gokapi/internal/webserver/authentication/oauth"
@@ -986,13 +987,13 @@ func downloadPresigned(w http.ResponseWriter, r *http.Request) {
responseError(w, storage.ErrorInvalidPresign)
return
}
presign, ok := database.GetPresignedUrl(presignKey[0])
if !ok || presign.Expiry < time.Now().Unix() {
presignedUrl, ok := presign.Get(presignKey[0])
if !ok {
responseError(w, storage.ErrorInvalidPresign)
return
}
files := make([]models.File, 0)
for _, file := range presign.FileIds {
for _, file := range presignedUrl.FileIds {
storedFile, ok := storage.GetFile(file)
if !ok {
responseError(w, storage.ErrorFileNotFound)
@@ -1000,13 +1001,13 @@ func downloadPresigned(w http.ResponseWriter, r *http.Request) {
}
files = append(files, storedFile)
}
database.DeletePresignedUrl(presign.Id)
presign.Delete(presignedUrl.Id)
if len(files) == 1 {
storage.ServeFile(files[0], w, r, true, false, true)
return
}
storage.ServeFilesAsZip(files, presign.Filename, w, r)
storage.ServeFilesAsZip(files, presignedUrl.Filename, w, r)
}
func serveFile(id string, isRootUrl bool, w http.ResponseWriter, r *http.Request) {
+2 -1
View File
@@ -19,6 +19,7 @@ import (
"github.com/forceu/gokapi/internal/storage/chunking/chunkreservation"
"github.com/forceu/gokapi/internal/storage/filerequest"
"github.com/forceu/gokapi/internal/storage/filerequest/ratelimiter"
"github.com/forceu/gokapi/internal/storage/presign"
"github.com/forceu/gokapi/internal/webserver/api/errorcodes"
"github.com/forceu/gokapi/internal/webserver/authentication/users"
"github.com/forceu/gokapi/internal/webserver/fileupload"
@@ -642,7 +643,7 @@ func createAndOutputPresignedUrl(ids []string, w http.ResponseWriter, filename s
Expiry: time.Now().Add(time.Second * 30).Unix(),
Filename: filename,
}
database.SavePresignedUrl(presignUrl)
presign.Save(presignUrl)
response := struct {
Result string `json:"Result"`
DownloadUrl string `json:"downloadUrl"`