Store upload defaults locally (#214), breaking API changes

This commit is contained in:
Marc Ole Bulling
2024-12-06 14:36:08 +01:00
committed by GitHub
parent 520f796c50
commit 6f582b25d8
20 changed files with 117 additions and 306 deletions
@@ -74,10 +74,6 @@ func Migrate(configOld, configNew models.DbConnection) {
dbNew.SaveHotlink(file)
}
}
defaults, ok := dbOld.GetUploadDefaults()
if ok {
dbNew.SaveUploadDefaults(defaults)
}
dbOld.Close()
dbNew.Close()
}
@@ -226,30 +222,6 @@ func DeleteAllSessions() {
db.DeleteAllSessions()
}
// Upload Defaults Section
// GetUploadDefaults returns the last used setting for amount of downloads allowed, last expiry in days and
// a password for the file
func GetUploadDefaults() models.LastUploadValues {
values, ok := db.GetUploadDefaults()
if ok {
return values
}
defaultValues := models.LastUploadValues{
Downloads: 1,
TimeExpiry: 14,
Password: "",
UnlimitedDownload: false,
UnlimitedTime: false,
}
return defaultValues
}
// SaveUploadDefaults saves the last used setting for an upload
func SaveUploadDefaults(values models.LastUploadValues) {
db.SaveUploadDefaults(values)
}
// Upload Status Section
// GetAllUploadStatus returns all UploadStatus values from the past 24 hours
@@ -1,7 +1,6 @@
package database
import (
"fmt"
"github.com/alicebob/miniredis/v2"
"github.com/forceu/gokapi/internal/configuration/database/dbabstraction"
"github.com/forceu/gokapi/internal/models"
@@ -101,26 +100,6 @@ func TestSessions(t *testing.T) {
runAllTypesCompareTwoOutputs(t, func() (any, any) { return GetSession("newsession") }, models.Session{}, false)
}
func TestUploadDefaults(t *testing.T) {
defaultValues := models.LastUploadValues{
Downloads: 1,
TimeExpiry: 14,
Password: "",
UnlimitedDownload: false,
UnlimitedTime: false,
}
runAllTypesCompareOutput(t, func() any { return GetUploadDefaults() }, defaultValues)
newValues := models.LastUploadValues{
Downloads: 5,
TimeExpiry: 20,
Password: "123",
UnlimitedDownload: true,
UnlimitedTime: true,
}
runAllTypesNoOutput(t, func() { SaveUploadDefaults(newValues) })
runAllTypesCompareOutput(t, func() any { return GetUploadDefaults() }, newValues)
}
func TestUploadStatus(t *testing.T) {
runAllTypesCompareTwoOutputs(t, func() (any, any) { return GetUploadStatus("newstatus") }, models.UploadStatus{}, false)
runAllTypesCompareOutput(t, func() any { return GetAllUploadStatus() }, []models.UploadStatus{})
@@ -288,7 +267,6 @@ func TestMigration(t *testing.T) {
dbOld.SaveMetaData(testFile)
dbOld.SaveHotlink(testFile)
dbOld.SaveApiKey(models.ApiKey{Id: "api123"})
dbOld.SaveUploadDefaults(models.LastUploadValues{Password: "pw123"})
dbOld.SaveHotlink(testFile)
dbOld.Close()
@@ -300,10 +278,6 @@ func TestMigration(t *testing.T) {
test.IsEqualBool(t, ok, true)
_, ok = dbNew.GetApiKey("api123")
test.IsEqualBool(t, ok, true)
defaults, ok := dbNew.GetUploadDefaults()
test.IsEqualBool(t, ok, true)
fmt.Printf("defaults: %+v\n", defaults)
test.IsEqualString(t, defaults.Password, "pw123")
_, ok = dbNew.GetMetaDataById("file1234")
test.IsEqualBool(t, ok, true)
}
@@ -82,12 +82,6 @@ type Database interface {
// DeleteAllSessions logs all users out
DeleteAllSessions()
// GetUploadDefaults returns the last used setting for amount of downloads allowed, last expiry in days and
// a password for the file
GetUploadDefaults() (models.LastUploadValues, bool)
// SaveUploadDefaults saves the last used setting for an upload
SaveUploadDefaults(values models.LastUploadValues)
// GetUploadStatus returns a models.UploadStatus from the ID passed or false if the id is not valid
GetUploadStatus(id string) (models.UploadStatus, bool)
// GetAllUploadStatus returns all UploadStatus values from the past 24 hours
GetAllUploadStatus() []models.UploadStatus
@@ -68,8 +68,6 @@ func TestMigration(t *testing.T) {
test.IsEqualBool(t, ok, true)
_, ok = database.GetApiKey("validkey")
test.IsEqualBool(t, ok, true)
defaults := database.GetUploadDefaults()
test.IsEqualString(t, defaults.Password, "123")
_, ok = database.GetMetaDataById("Wzol7LyY2QVczXynJtVo")
test.IsEqualBool(t, ok, true)
}
@@ -331,25 +331,6 @@ func TestSession(t *testing.T) {
test.IsEqualBool(t, ok, false)
}
func TestUploadDefaults(t *testing.T) {
defaults, ok := dbInstance.GetUploadDefaults()
test.IsEqualBool(t, ok, false)
dbInstance.SaveUploadDefaults(models.LastUploadValues{
Downloads: 20,
TimeExpiry: 30,
Password: "abcd",
UnlimitedDownload: true,
UnlimitedTime: true,
})
defaults, ok = dbInstance.GetUploadDefaults()
test.IsEqualBool(t, ok, true)
test.IsEqualInt(t, defaults.Downloads, 20)
test.IsEqualInt(t, defaults.TimeExpiry, 30)
test.IsEqualString(t, defaults.Password, "abcd")
test.IsEqualBool(t, defaults.UnlimitedDownload, true)
test.IsEqualBool(t, defaults.UnlimitedTime, true)
}
func TestUploadStatus(t *testing.T) {
allStatus := dbInstance.GetAllUploadStatus()
test.IsEqualInt(t, len(allStatus), 0)
@@ -1,30 +0,0 @@
package redis
import (
"github.com/forceu/gokapi/internal/helper"
"github.com/forceu/gokapi/internal/models"
redigo "github.com/gomodule/redigo/redis"
)
const (
idUploadDefaults = "uploadDefaults"
)
// GetUploadDefaults returns the last used setting for amount of downloads allowed, last expiry in days and
// a password for the file
func (p DatabaseProvider) GetUploadDefaults() (models.LastUploadValues, bool) {
var result models.LastUploadValues
values, ok := p.getHashMap(idUploadDefaults)
if !ok {
return models.LastUploadValues{}, false
}
err := redigo.ScanStruct(values, &result)
helper.Check(err)
return result, true
}
// SaveUploadDefaults saves the last used setting for an upload
func (p DatabaseProvider) SaveUploadDefaults(values models.LastUploadValues) {
p.setHashMap(p.buildArgs(idUploadDefaults).AddFlat(values))
}
@@ -253,27 +253,8 @@ func TestSession(t *testing.T) {
test.IsEqualBool(t, ok, false)
}
func TestUploadDefaults(t *testing.T) {
defaults, ok := dbInstance.GetUploadDefaults()
test.IsEqualBool(t, ok, false)
dbInstance.SaveUploadDefaults(models.LastUploadValues{
Downloads: 20,
TimeExpiry: 30,
Password: "abcd",
UnlimitedDownload: true,
UnlimitedTime: true,
})
defaults, ok = dbInstance.GetUploadDefaults()
test.IsEqualBool(t, ok, true)
test.IsEqualInt(t, defaults.Downloads, 20)
test.IsEqualInt(t, defaults.TimeExpiry, 30)
test.IsEqualString(t, defaults.Password, "abcd")
test.IsEqualBool(t, defaults.UnlimitedDownload, true)
test.IsEqualBool(t, defaults.UnlimitedTime, true)
}
func TestGarbageCollectionUploads(t *testing.T) {
orgiginalFunc := currentTime
originalFunc := currentTime
currentTime = func() time.Time {
return time.Now().Add(-25 * time.Hour)
}
@@ -297,7 +278,7 @@ func TestGarbageCollectionUploads(t *testing.T) {
ChunkId: "ctodelete5",
CurrentStatus: 1,
})
currentTime = orgiginalFunc
currentTime = originalFunc
dbInstance.SaveUploadStatus(models.UploadStatus{
ChunkId: "ctokeep1",
@@ -1,61 +0,0 @@
package sqlite
import (
"database/sql"
"errors"
"github.com/forceu/gokapi/internal/helper"
"github.com/forceu/gokapi/internal/models"
)
type schemaUploadConfig struct {
Id int64
Downloads int
TimeExpiry int
Password string
UnlimitedDownloads int
UnlimitedTime int
}
// GetUploadDefaults returns the last used setting for amount of downloads allowed, last expiry in days and
// a password for the file
func (p DatabaseProvider) GetUploadDefaults() (models.LastUploadValues, bool) {
rowResult := schemaUploadConfig{}
row := p.sqliteDb.QueryRow("SELECT * FROM UploadConfig WHERE id = 1")
err := row.Scan(&rowResult.Id, &rowResult.Downloads, &rowResult.TimeExpiry, &rowResult.Password, &rowResult.UnlimitedDownloads, &rowResult.UnlimitedTime)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return models.LastUploadValues{}, false
}
helper.Check(err)
return models.LastUploadValues{}, false
}
result := models.LastUploadValues{
Downloads: rowResult.Downloads,
TimeExpiry: rowResult.TimeExpiry,
Password: rowResult.Password,
UnlimitedDownload: rowResult.UnlimitedDownloads == 1,
UnlimitedTime: rowResult.UnlimitedTime == 1,
}
return result, true
}
// SaveUploadDefaults saves the last used setting for an upload
func (p DatabaseProvider) SaveUploadDefaults(values models.LastUploadValues) {
newData := schemaUploadConfig{
Downloads: values.Downloads,
TimeExpiry: values.TimeExpiry,
Password: values.Password,
}
if values.UnlimitedDownload {
newData.UnlimitedDownloads = 1
}
if values.UnlimitedTime {
newData.UnlimitedTime = 1
}
_, err := p.sqliteDb.Exec("INSERT OR REPLACE INTO UploadConfig (id, Downloads,TimeExpiry,Password,UnlimitedDownloads,UnlimitedTime) VALUES (1, ?, ?, ?, ?, ?)",
newData.Downloads, newData.TimeExpiry, newData.Password, newData.UnlimitedDownloads, newData.UnlimitedTime)
helper.Check(err)
}
-9
View File
@@ -36,15 +36,6 @@ type Encryption struct {
ChecksumSalt string
}
// LastUploadValues is used to save the last used values for uploads in the database
type LastUploadValues struct {
Downloads int `redis:"downloads"`
TimeExpiry int `redis:"time_expiry"`
Password string `redis:"password"`
UnlimitedDownload bool `redis:"unlimited_download"`
UnlimitedTime bool `redis:"unlimited_time"`
}
// ToJson returns an idented JSon representation
func (c Configuration) ToJson() []byte {
result, err := json.MarshalIndent(c, "", " ")
@@ -53,13 +53,6 @@ func Create(initFiles bool) {
}
database.Connect(config)
writeTestSessions()
database.SaveUploadDefaults(models.LastUploadValues{
Downloads: 3,
TimeExpiry: 20,
Password: "123",
UnlimitedDownload: false,
UnlimitedTime: false,
})
writeTestFiles()
database.SaveHotlink(models.File{Id: "n1tSTAGj8zan9KaT4u6p", HotlinkId: "PhSs6mFtf8O5YGlLMfNw9rYXx9XRNkzCnJZpQBi7inunv3Z4A.jpg", ExpireAt: time.Now().Add(time.Hour).Unix()})
writeApiKeys()
+18 -29
View File
@@ -558,28 +558,23 @@ type e2ESetupView struct {
// UploadView contains parameters for the admin menu template
type UploadView struct {
Items []models.FileApiOutput
ApiKeys []models.ApiKey
ServerUrl string
DefaultPassword string
Logs string
PublicName string
SystemKey string
IsAdminView bool
IsDownloadView bool
IsApiView bool
IsLogoutAvailable bool
DefaultUnlimitedDownload bool
DefaultUnlimitedTime bool
EndToEndEncryption bool
IncludeFilename bool
MaxFileSize int
DefaultDownloads int
DefaultExpiry int
ActiveView int
ChunkSize int
MaxParallelUploads int
TimeNow int64
Items []models.FileApiOutput
ApiKeys []models.ApiKey
ServerUrl string
Logs string
PublicName string
SystemKey string
IsAdminView bool
IsDownloadView bool
IsApiView bool
IsLogoutAvailable bool
EndToEndEncryption bool
IncludeFilename bool
MaxFileSize int
ActiveView int
ChunkSize int
MaxParallelUploads int
TimeNow int64
}
// ViewMain is the identifier for the main menu
@@ -640,12 +635,6 @@ func (u *UploadView) convertGlobalConfig(view int) *UploadView {
u.ActiveView = view
u.MaxFileSize = config.MaxFileSizeMB
u.IsLogoutAvailable = authentication.IsLogoutAvailable()
defaultValues := database.GetUploadDefaults()
u.DefaultDownloads = defaultValues.Downloads
u.DefaultExpiry = defaultValues.TimeExpiry
u.DefaultPassword = defaultValues.Password
u.DefaultUnlimitedDownload = defaultValues.UnlimitedDownload
u.DefaultUnlimitedTime = defaultValues.UnlimitedTime
u.EndToEndEncryption = config.Encryption.Level == encryption.EndToEndEncryption
u.MaxParallelUploads = config.MaxParallelUploads
u.ChunkSize = config.ChunkSize
@@ -671,7 +660,7 @@ func uploadChunk(w http.ResponseWriter, r *http.Request) {
// If the user is authenticated, this parses the uploaded chunk and stores it
func uploadComplete(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
err := fileupload.CompleteChunk(w, r, false)
err := fileupload.CompleteChunk(w, r)
responseError(w, err)
}
+1 -1
View File
@@ -481,7 +481,7 @@ func TestPostUpload(t *testing.T) {
Key: "filesize",
Value: "50",
}},
RequiredContent: []string{"{\"Result\":\"OK\"", "\"Name\":\"fileupload.jpg\"", "DownloadsRemaining\":3"},
RequiredContent: []string{"{\"Result\":\"OK\"", "\"Name\":\"fileupload.jpg\"", "DownloadsRemaining\":1"},
ExcludedContent: []string{"\"Id\":\"\"", "HotlinkId\":\"\"", "ErrorMessage"},
Cookies: []test.Cookie{{
Name: "session_token",
+1 -1
View File
@@ -289,7 +289,7 @@ func chunkComplete(w http.ResponseWriter, request apiRequest) {
return
}
request.request.Form.Set("chunkid", request.request.Form.Get("uuid"))
err = fileupload.CompleteChunk(w, request.request, true)
err = fileupload.CompleteChunk(w, request.request)
if err != nil {
sendError(w, http.StatusBadRequest, err.Error())
}
+6 -19
View File
@@ -2,7 +2,6 @@ package fileupload
import (
"github.com/forceu/gokapi/internal/configuration"
"github.com/forceu/gokapi/internal/configuration/database"
"github.com/forceu/gokapi/internal/models"
"github.com/forceu/gokapi/internal/storage"
"github.com/forceu/gokapi/internal/storage/chunking"
@@ -19,7 +18,7 @@ func Process(w http.ResponseWriter, r *http.Request, maxMemory int) error {
return err
}
defer r.MultipartForm.RemoveAll()
config, err := parseConfig(r.Form, false)
config, err := parseConfig(r.Form)
if err != nil {
return err
}
@@ -63,13 +62,13 @@ func ProcessNewChunk(w http.ResponseWriter, r *http.Request, isApiCall bool) err
}
// CompleteChunk processes a file after all the chunks have been completed
func CompleteChunk(w http.ResponseWriter, r *http.Request, isApiCall bool) error {
func CompleteChunk(w http.ResponseWriter, r *http.Request) error {
err := r.ParseForm()
if err != nil {
return err
}
chunkId := r.Form.Get("chunkid")
config, err := parseConfig(r.Form, !isApiCall)
config, err := parseConfig(r.Form)
if err != nil {
return err
}
@@ -86,19 +85,17 @@ func CompleteChunk(w http.ResponseWriter, r *http.Request, isApiCall bool) error
return nil
}
func parseConfig(values formOrHeader, setNewDefaults bool) (models.UploadRequest, error) {
func parseConfig(values formOrHeader) (models.UploadRequest, error) {
allowedDownloads := values.Get("allowedDownloads")
expiryDays := values.Get("expiryDays")
password := values.Get("password")
allowedDownloadsInt, err := strconv.Atoi(allowedDownloads)
if err != nil {
previousValues := database.GetUploadDefaults()
allowedDownloadsInt = previousValues.Downloads
allowedDownloadsInt = 1
}
expiryDaysInt, err := strconv.Atoi(expiryDays)
if err != nil {
previousValues := database.GetUploadDefaults()
expiryDaysInt = previousValues.TimeExpiry
expiryDaysInt = 14
}
unlimitedDownload := values.Get("isUnlimitedDownload") == "true"
@@ -111,16 +108,6 @@ func parseConfig(values formOrHeader, setNewDefaults bool) (models.UploadRequest
unlimitedTime = true
}
if setNewDefaults {
values := models.LastUploadValues{
Downloads: allowedDownloadsInt,
TimeExpiry: expiryDaysInt,
Password: password,
UnlimitedDownload: unlimitedDownload,
UnlimitedTime: unlimitedTime,
}
database.SaveUploadDefaults(values)
}
var isEnd2End bool
var realSize int64
if values.Get("isE2E") == "true" {
@@ -4,7 +4,6 @@ import (
"bytes"
"encoding/json"
"github.com/forceu/gokapi/internal/configuration"
"github.com/forceu/gokapi/internal/configuration/database"
"github.com/forceu/gokapi/internal/models"
"github.com/forceu/gokapi/internal/test"
"github.com/forceu/gokapi/internal/test/testconfiguration"
@@ -36,43 +35,38 @@ func TestParseConfig(t *testing.T) {
isE2E: "",
realSize: "",
}
config, err := parseConfig(data, false)
config, err := parseConfig(data)
test.IsNil(t, err)
test.IsEqualBool(t, config.IsEndToEndEncrypted, false)
test.IsEqualInt64(t, config.RealSize, 0)
defaults := database.GetUploadDefaults()
test.IsEqualInt(t, config.AllowedDownloads, 9)
test.IsEqualString(t, config.Password, "123")
test.IsEqualInt(t, config.Expiry, 5)
test.IsEqualInt(t, defaults.Downloads, 3)
config, err = parseConfig(data, true)
config, err = parseConfig(data)
test.IsNil(t, err)
defaults = database.GetUploadDefaults()
test.IsEqualInt(t, defaults.Downloads, 9)
database.SaveUploadDefaults(models.LastUploadValues{Downloads: 3, TimeExpiry: 20})
data.allowedDownloads = ""
data.expiryDays = "invalid"
config, err = parseConfig(data, false)
config, err = parseConfig(data)
test.IsNil(t, err)
test.IsEqualInt(t, config.AllowedDownloads, 3)
test.IsEqualInt(t, config.Expiry, 20)
test.IsEqualInt(t, config.AllowedDownloads, 1)
test.IsEqualInt(t, config.Expiry, 14)
test.IsEqualBool(t, config.UnlimitedTime, false)
test.IsEqualBool(t, config.UnlimitedDownload, false)
data.allowedDownloads = "0"
data.expiryDays = "0"
config, err = parseConfig(data, false)
config, err = parseConfig(data)
test.IsNil(t, err)
test.IsEqualBool(t, config.UnlimitedTime, true)
test.IsEqualBool(t, config.UnlimitedDownload, true)
data.isE2E = "true"
data.realSize = "200"
config, err = parseConfig(data, false)
config, err = parseConfig(data)
test.IsNil(t, err)
test.IsEqualBool(t, config.IsEndToEndEncrypted, true)
test.IsEqualInt64(t, config.RealSize, 200)
@@ -122,12 +116,12 @@ func TestProcessNewChunk(t *testing.T) {
func TestCompleteChunk(t *testing.T) {
w, r := test.GetRecorder("POST", "/uploadComplete", nil, nil, strings.NewReader("invalid§$%&%§"))
err := CompleteChunk(w, r, false)
err := CompleteChunk(w, r)
test.IsNotNil(t, err)
w = httptest.NewRecorder()
r = getFileUploadRecorder(false)
err = CompleteChunk(w, r, false)
err = CompleteChunk(w, r)
test.IsNotNil(t, err)
data := url.Values{}
@@ -139,7 +133,7 @@ func TestCompleteChunk(t *testing.T) {
data.Set("filesize", "13")
w, r = test.GetRecorder("POST", "/uploadComplete", nil, nil, strings.NewReader(data.Encode()))
r.Header.Set("Content-type", "application/x-www-form-urlencoded")
err = CompleteChunk(w, r, false)
err = CompleteChunk(w, r)
test.IsNil(t, err)
result := struct {
@@ -154,7 +148,7 @@ func TestCompleteChunk(t *testing.T) {
data.Set("chunkid", "invalid")
w, r = test.GetRecorder("POST", "/uploadComplete", nil, nil, strings.NewReader(data.Encode()))
r.Header.Set("Content-type", "application/x-www-form-urlencoded")
err = CompleteChunk(w, r, false)
err = CompleteChunk(w, r)
test.IsNotNil(t, err)
}
@@ -721,11 +721,11 @@
},
"allowedDownloads": {
"type": "integer",
"description": "How many downloads are allowed. Last used value from web interface will be used if empty. Unlimited if 0 is passed."
"description": "How many downloads are allowed. Default of 1 will be used if empty. Unlimited if 0 is passed."
},
"expiryDays": {
"type": "integer",
"description": "How many days the file will be stored. Last used value from web interface will be used if empty. Unlimited if 0 is passed."
"description": "How many days the file will be stored. Default of 14 will be used if empty. Unlimited if 0 is passed."
},
"password": {
"type": "string",
@@ -811,11 +811,11 @@
},
"allowedDownloads": {
"type": "integer",
"description": "How many downloads are allowed. Last used value from web interface will be used if empty. Unlimited if 0 is passed."
"description": "How many downloads are allowed. Default of 1 will be used if empty. Unlimited if 0 is passed."
},
"expiryDays": {
"type": "integer",
"description": "How many days the file will be stored. Last used value from web interface will be used if empty. Unlimited if 0 is passed."
"description": "How many days the file will be stored. Default of 14 will be used if empty. Unlimited if 0 is passed."
},
"password": {
"type": "string",
+62 -15
View File
@@ -23,6 +23,7 @@ Dropzone.options.uploaddropzone = {
init: function() {
dropzoneObject = this;
this.on("addedfile", file => {
saveUploadDefaults();
addFileProgress(file);
});
this.on("queuecomplete", function() {
@@ -73,10 +74,10 @@ function updateProgressbar(file, progress, bytesSent) {
let millisSinceUpload = Date.now() - container.getAttribute('data-starttime');
let megabytePerSecond = bytesSent / (millisSinceUpload / 1000) / 1024 / 1024;
document.getElementById(`us-progressbar-${chunkId}`).style.width = rounded + "%";
let uploadSpeed = Math.round(megabytePerSecond * 10) / 10;
if (!Number.isNaN(uploadSpeed))
document.getElementById(`us-progress-info-${chunkId}`).innerText = rounded + "% - " + uploadSpeed + "MB/s";
document.getElementById(`us-progress-info-${chunkId}`).innerText = rounded + "% - " + uploadSpeed + "MB/s";
}
function setProgressStatus(chunkId, progressCode) {
@@ -130,6 +131,52 @@ document.onpaste = function(event) {
}
}
function setUploadDefaults() {
let defaultDownloads = getLocalStorageWithDefault("defaultDownloads", 1);
let defaultExpiry = getLocalStorageWithDefault("defaultExpiry", 14);
let defaultPassword = getLocalStorageWithDefault("defaultPassword", "");
let defaultUnlimitedDownloads = getLocalStorageWithDefault("defaultUnlimitedDownloads", false) === "true";
let defaultUnlimitedTime = getLocalStorageWithDefault("defaultUnlimitedTime", false) === "true";
document.getElementById("allowedDownloads").value = defaultDownloads;
document.getElementById("expiryDays").value = defaultExpiry;
document.getElementById("password").value = defaultPassword;
document.getElementById("enableDownloadLimit").checked = !defaultUnlimitedDownloads;
document.getElementById("enableTimeLimit").checked = !defaultUnlimitedTime;
if (defaultPassword === "") {
document.getElementById("enablePassword").checked = false;
document.getElementById("password").disabled = true;
} else {
document.getElementById("enablePassword").checked = true;
document.getElementById("password").disabled = false;
}
if (defaultUnlimitedDownloads) {
document.getElementById("allowedDownloads").disabled = true;
}
if (defaultUnlimitedTime) {
document.getElementById("expiryDays").disabled = true;
}
}
function saveUploadDefaults() {
localStorage.setItem("defaultDownloads", document.getElementById("allowedDownloads").value);
localStorage.setItem("defaultExpiry", document.getElementById("expiryDays").value);
localStorage.setItem("defaultPassword", document.getElementById("password").value);
localStorage.setItem("defaultUnlimitedDownloads", !document.getElementById("enableDownloadLimit").checked);
localStorage.setItem("defaultUnlimitedTime", !document.getElementById("enableTimeLimit").checked);
}
function getLocalStorageWithDefault(key, default_value) {
var value = localStorage.getItem(key);
if (value === null) {
return default_value;
}
return value;
}
function urlencodeFormData(fd) {
let s = '';
@@ -430,7 +477,7 @@ function changeApiPermission(apiKey, permission, buttonId) {
function deleteApiKey(apiKey) {
document.getElementById("delete-"+apiKey).disabled = true;
document.getElementById("delete-" + apiKey).disabled = true;
var apiUrl = './api/auth/delete';
const requestOptions = {
method: 'POST',
@@ -448,7 +495,7 @@ function deleteApiKey(apiKey) {
}
})
.then(data => {
document.getElementById("row-"+apiKey).remove();
document.getElementById("row-" + apiKey).remove();
})
.catch(error => {
alert("Unable to delete API key: " + error);
@@ -478,7 +525,7 @@ function newApiKey() {
}
})
.then(data => {
location.reload();
location.reload();
})
.catch(error => {
alert("Unable to create API key: " + error);
@@ -489,7 +536,7 @@ function newApiKey() {
function deleteFile(id) {
document.getElementById("button-delete-"+id).disabled = true;
document.getElementById("button-delete-" + id).disabled = true;
var apiUrl = './api/files/delete';
const requestOptions = {
method: 'POST',
@@ -507,7 +554,7 @@ function deleteFile(id) {
}
})
.then(data => {
location.reload();
location.reload();
})
.catch(error => {
alert("Unable to delete file: " + error);
@@ -546,7 +593,7 @@ function registerChangeHandler() {
source.onmessage = (event) => {
try {
let eventData = JSON.parse(event.data);
setProgressStatus(eventData.chunkid, eventData.currentstatus);
setProgressStatus(eventData.chunkid, eventData.currentstatus);
} catch (e) {
console.error("Failed to parse event data:", e);
}
@@ -638,7 +685,7 @@ function addRow(jsonText) {
let item = jsonObject.FileInfo;
let table = document.getElementById("downloadtable");
let row = table.insertRow(0);
row.id = "row-"+ item.Id;
row.id = "row-" + item.Id;
let cellFilename = row.insertCell(0);
let cellFileSize = row.insertCell(1);
let cellRemainingDownloads = row.insertCell(2);
@@ -668,12 +715,12 @@ function addRow(jsonText) {
cellUrl.innerHTML = '<a target="_blank" style="color: inherit" id="url-href-' + item.Id + '" href="' + item.UrlDownload + '">' + item.Id + '</a>' + lockIcon;
let buttons = '<button type="button" onclick="showToast()" id="url-button-' + item.Id + '" data-clipboard-text="' + item.UrlDownload + '" class="copyurl btn btn-outline-light btn-sm"><i class="bi bi-copy"></i> URL</button> ';
if (item.UrlHotlink === "") {
buttons = buttons + '<button type="button"class="copyurl btn btn-outline-light btn-sm disabled"><i class="bi bi-copy"></i> Hotlink</button> ';
} else {
buttons = buttons + '<button type="button" onclick="showToast()" data-clipboard-text="' + item.UrlHotlink + '" class="copyurl btn btn-outline-light btn-sm"><i class="bi bi-copy"></i> Hotlink</button> ';
}
buttons = buttons + '<button type="button" id="qrcode-'+item.Id+'" title="QR Code" class="btn btn-outline-light btn-sm" onclick="showQrCode(\'' + item.UrlDownload + '\');"><i class="bi bi-qr-code"></i></button> ';
if (item.UrlHotlink === "") {
buttons = buttons + '<button type="button"class="copyurl btn btn-outline-light btn-sm disabled"><i class="bi bi-copy"></i> Hotlink</button> ';
} else {
buttons = buttons + '<button type="button" onclick="showToast()" data-clipboard-text="' + item.UrlHotlink + '" class="copyurl btn btn-outline-light btn-sm"><i class="bi bi-copy"></i> Hotlink</button> ';
}
buttons = buttons + '<button type="button" id="qrcode-' + item.Id + '" title="QR Code" class="btn btn-outline-light btn-sm" onclick="showQrCode(\'' + item.UrlDownload + '\');"><i class="bi bi-qr-code"></i></button> ';
buttons = buttons + '<button type="button" title="Edit" class="btn btn-outline-light btn-sm" onclick="showEditModal(\'' + item.Name + '\',\'' + item.Id + '\', ' + item.DownloadsRemaining + ', ' + item.ExpireAt + ', ' + item.IsPasswordProtected + ', ' + item.UnlimitedDownloads + ', ' + item.UnlimitedTime + ');"><i class="bi bi-pencil"></i></button> ';
buttons = buttons + '<button type="button" id="button-delete-' + item.Id + '" title="Delete" class="btn btn-outline-danger btn-sm" onclick="deleteFile(\'' + item.Id + '\')"><i class="bi bi-trash3"></i></button>';
File diff suppressed because one or more lines are too long
@@ -19,27 +19,27 @@
<div class="container-md">
<div class="row">
<div class="form-group col">
<input class="form-check-input" type="checkbox" name="enableDownloadLimit" id="enableDownloadLimit" onchange="checkBoxChanged(this, 'allowedDownloads')" value="" aria-label="Enable Download Limit" {{ if not .DefaultUnlimitedDownload }} checked {{ end }}>
<input class="form-check-input" type="checkbox" name="enableDownloadLimit" id="enableDownloadLimit" onchange="checkBoxChanged(this, 'allowedDownloads')" value="" aria-label="Enable Download Limit" checked>
<label class="control-label small" for="allowedDownloads">Limit Downloads</label>
<div class="input-group mb-3">
<input type="number" class="form-control admin-input" value="{{ .DefaultDownloads }}" name="allowedDownloads" id="allowedDownloads" min="1" {{ if .DefaultUnlimitedDownload }} disabled {{ end }} style="text-align: right;">
<input type="number" class="form-control admin-input" value="1" name="allowedDownloads" id="allowedDownloads" min="1" style="text-align: right;">
<span class="input-group-text">Downloads</span>
</div>
</div>
<div class="break"></div>
<div class="form-group col">
<input class="form-check-input" type="checkbox" name="enableTimeLimit" id="enableTimeLimit" onchange="checkBoxChanged(this, 'expiryDays')" value="" aria-label="Enable Time Limit" {{ if not .DefaultUnlimitedTime }} checked {{ end }}>
<input class="form-check-input" type="checkbox" name="enableTimeLimit" id="enableTimeLimit" onchange="checkBoxChanged(this, 'expiryDays')" value="" aria-label="Enable Time Limit" checked>
<label class="control-label small" for="expiryDays">Expiry</label>
<div class="input-group mb-3">
<input type="number" class="form-control admin-input" value="{{ .DefaultExpiry }}" name="expiryDays" id="expiryDays" min="1" {{ if .DefaultUnlimitedTime }} disabled {{ end }} style="text-align: right;">
<input type="number" class="form-control admin-input" value="14" name="expiryDays" id="expiryDays" min="1" style="text-align: right;">
<span class="input-group-text">Days</span>
</div>
</div>
<div class="break"></div>
<div class="form-group col">
<input class="form-check-input" type="checkbox" name="enablePassword" id="enablePassword" onchange="checkBoxChanged(this, 'password')" value="" aria-label="Enable Password Protection" {{ if ne .DefaultPassword "" }} checked {{ end }}>
<input class="form-check-input" type="checkbox" name="enablePassword" id="enablePassword" onchange="checkBoxChanged(this, 'password')" value="" aria-label="Enable Password Protection">
<label class="control-label small" for="password">Password</label>
<input class="form-control admin-input" value="{{ .DefaultPassword }}" name="password" id="password" placeholder="None" {{ if eq .DefaultPassword "" }} disabled {{ end }}>
<input class="form-control admin-input" value="" name="password" id="password" placeholder="None" disabled>
</div>
<div id="errordiv" class="alert alert-danger" style="display:none">
<span id="errormessage" ></span>
@@ -177,6 +177,7 @@
});
registerChangeHandler();
var systemKey = "{{.SystemKey}}";
setUploadDefaults();
</script>
{{ if .EndToEndEncryption }}
+4 -4
View File
@@ -721,11 +721,11 @@
},
"allowedDownloads": {
"type": "integer",
"description": "How many downloads are allowed. Last used value from web interface will be used if empty. Unlimited if 0 is passed."
"description": "How many downloads are allowed. Default of 1 will be used if empty. Unlimited if 0 is passed."
},
"expiryDays": {
"type": "integer",
"description": "How many days the file will be stored. Last used value from web interface will be used if empty. Unlimited if 0 is passed."
"description": "How many days the file will be stored. Default of 14 will be used if empty. Unlimited if 0 is passed."
},
"password": {
"type": "string",
@@ -811,11 +811,11 @@
},
"allowedDownloads": {
"type": "integer",
"description": "How many downloads are allowed. Last used value from web interface will be used if empty. Unlimited if 0 is passed."
"description": "How many downloads are allowed. Default of 1 will be used if empty. Unlimited if 0 is passed."
},
"expiryDays": {
"type": "integer",
"description": "How many days the file will be stored. Last used value from web interface will be used if empty. Unlimited if 0 is passed."
"description": "How many days the file will be stored. Default of 14 will be used if empty. Unlimited if 0 is passed."
},
"password": {
"type": "string",