Breaking: Make API output less verbose #63, fixed hotlink not working with "duplicate" API call, added option to change filename with duplicate API call

This commit is contained in:
Marc Ole Bulling
2022-07-15 02:29:49 +02:00
parent a10c036c6a
commit 5bb338f91c
11 changed files with 204 additions and 80 deletions

View File

@@ -3,6 +3,7 @@ package models
import (
"encoding/json"
"fmt"
"github.com/jinzhu/copier"
)
// File is a struct used for saving information about an uploaded file
@@ -25,6 +26,26 @@ type File struct {
RequiresClientSideDecryption bool `json:"RequiresClientSideDecryption"`
}
// FileApiOutput will be displayed for public outputs from the ID, hiding sensitive information
type FileApiOutput struct {
Id string `json:"Id"`
Name string `json:"Name"`
Size string `json:"Size"`
SHA256 string `json:"SHA256"`
HotlinkId string `json:"HotlinkId"`
ContentType string `json:"ContentType"`
ExpireAt int64 `json:"ExpireAt"`
ExpireAtString string `json:"ExpireAtString"`
DownloadsRemaining int `json:"DownloadsRemaining"`
DownloadCount int `json:"DownloadCount"`
UnlimitedDownloads bool `json:"UnlimitedDownloads"`
UnlimitedTime bool `json:"UnlimitedTime"`
RequiresClientSideDecryption bool `json:"RequiresClientSideDecryption"`
IsEncrypted bool `json:"IsEncrypted"`
IsPasswordProtected bool `json:"IsPasswordProtected"`
IsSavedOnLocalStorage bool `json:"IsSavedOnLocalStorage"`
}
// EncryptionInfo holds information about the encryption used on the file
type EncryptionInfo struct {
IsEncrypted bool `json:"IsEncrypted"`
@@ -32,31 +53,51 @@ type EncryptionInfo struct {
Nonce []byte `json:"Nonce"`
}
func (f *File) ToFileApiOutput() (FileApiOutput, error) {
var result FileApiOutput
err := copier.Copy(&result, &f)
if err != nil {
return FileApiOutput{}, err
}
result.IsPasswordProtected = f.PasswordHash != ""
result.IsEncrypted = f.Encryption.IsEncrypted
result.IsSavedOnLocalStorage = f.AwsBucket == ""
return result, nil
}
// ToJsonResult converts the file info to a json String used for returning a result for an upload
func (f *File) ToJsonResult(serverUrl string) string {
info, err := f.ToFileApiOutput()
if err != nil {
return errorAsJson(err)
}
result := Result{
Result: "OK",
Url: serverUrl + "d?id=",
HotlinkUrl: serverUrl + "hotlink/",
GenericHotlinkUrl: serverUrl + "downloadFile?id=",
FileInfo: f,
FileInfo: info,
}
bytes, err := json.Marshal(result)
if err != nil {
fmt.Println(err)
return "{\"Result\":\"error\",\"ErrorMessage\":\"" + err.Error() + "\"}"
return errorAsJson(err)
}
return string(bytes)
}
func errorAsJson(err error) string {
fmt.Println(err)
return "{\"Result\":\"error\",\"ErrorMessage\":\"" + err.Error() + "\"}"
}
// Result is the struct used for the result after an upload
// swagger:model UploadResult
type Result struct {
Result string `json:"Result"`
FileInfo *File `json:"FileInfo"`
Url string `json:"Url"`
HotlinkUrl string `json:"HotlinkUrl"`
GenericHotlinkUrl string `json:"GenericHotlinkUrl"`
Result string `json:"Result"`
FileInfo FileApiOutput `json:"FileInfo"`
Url string `json:"Url"`
HotlinkUrl string `json:"HotlinkUrl"`
GenericHotlinkUrl string `json:"GenericHotlinkUrl"`
}
// DownloadStatus contains current downloads, so they do not get removed during cleanup

View File

@@ -27,5 +27,5 @@ func TestToJsonResult(t *testing.T) {
UnlimitedDownloads: true,
UnlimitedTime: true,
}
test.IsEqualString(t, file.ToJsonResult("serverurl/"), `{"Result":"OK","FileInfo":{"Id":"testId","Name":"testName","Size":"10 B","SHA256":"sha256","ExpireAt":50,"ExpireAtString":"future","DownloadsRemaining":1,"DownloadCount":3,"PasswordHash":"pwhash","HotlinkId":"hotlinkid","ContentType":"text/html","AwsBucket":"test","Encryption":{"IsEncrypted":true,"DecryptionKey":"AQ==","Nonce":"Ag=="},"UnlimitedDownloads":true,"UnlimitedTime":true,"RequiresClientSideDecryption":false},"Url":"serverurl/d?id=","HotlinkUrl":"serverurl/hotlink/","GenericHotlinkUrl":"serverurl/downloadFile?id="}`)
test.IsEqualString(t, file.ToJsonResult("serverurl/"), `{"Result":"OK","FileInfo":{"Id":"testId","Name":"testName","Size":"10 B","SHA256":"sha256","HotlinkId":"hotlinkid","ContentType":"text/html","ExpireAt":50,"ExpireAtString":"future","DownloadsRemaining":1,"DownloadCount":3,"UnlimitedDownloads":true,"UnlimitedTime":true,"RequiresClientSideDecryption":false,"IsEncrypted":true,"IsPasswordProtected":true,"IsSavedOnLocalStorage":false},"Url":"serverurl/d?id=","HotlinkUrl":"serverurl/hotlink/","GenericHotlinkUrl":"serverurl/downloadFile?id="}`)
}

View File

@@ -148,10 +148,11 @@ const (
ParamExpiry int = 1 << iota
ParamDownloads
ParamPassword
ParamName
)
// DuplicateFile creates a copy of an existing file with new parameters
func DuplicateFile(file models.File, parametersToChange int, fileParameters models.UploadRequest) (models.File, error) {
func DuplicateFile(file models.File, parametersToChange int, newFileName string, fileParameters models.UploadRequest) (models.File, error) {
var newFile models.File
err := copier.Copy(&newFile, &file)
if err != nil {
@@ -161,6 +162,7 @@ func DuplicateFile(file models.File, parametersToChange int, fileParameters mode
changeExpiry := parametersToChange&ParamExpiry != 0
changeDownloads := parametersToChange&ParamDownloads != 0
changePassword := parametersToChange&ParamPassword != 0
changeName := parametersToChange&ParamName != 0
if changeExpiry {
newFile.ExpireAt = fileParameters.ExpiryTimestamp
@@ -174,8 +176,13 @@ func DuplicateFile(file models.File, parametersToChange int, fileParameters mode
if changePassword {
newFile.PasswordHash = configuration.HashPassword(fileParameters.Password, true)
}
if changeName {
newFile.Name = newFileName
}
newFile.Id = createNewId()
newFile.DownloadCount = 0
addHotlink(&file)
database.SaveMetaData(newFile)
return newFile, nil

View File

@@ -333,7 +333,7 @@ func TestDuplicateFile(t *testing.T) {
retrievedFile.DownloadCount = 5
database.SaveMetaData(retrievedFile)
newFile, err := DuplicateFile(retrievedFile, 0, models.UploadRequest{})
newFile, err := DuplicateFile(retrievedFile, 0, "123", models.UploadRequest{})
test.IsNil(t, err)
test.IsEqualInt(t, newFile.DownloadCount, 0)
test.IsEqualInt(t, newFile.DownloadsRemaining, 1)
@@ -341,6 +341,7 @@ func TestDuplicateFile(t *testing.T) {
test.IsEqualString(t, newFile.PasswordHash, "")
test.IsEqualBool(t, newFile.UnlimitedDownloads, false)
test.IsEqualBool(t, newFile.UnlimitedTime, false)
test.IsEqualString(t, newFile.Name, "test.dat")
uploadRequest := models.UploadRequest{
AllowedDownloads: 5,
@@ -351,7 +352,7 @@ func TestDuplicateFile(t *testing.T) {
UnlimitedTime: true,
}
newFile, err = DuplicateFile(retrievedFile, 0, uploadRequest)
newFile, err = DuplicateFile(retrievedFile, 0, "123", uploadRequest)
test.IsNil(t, err)
test.IsEqualInt(t, newFile.DownloadCount, 0)
test.IsEqualInt(t, newFile.DownloadsRemaining, 1)
@@ -359,8 +360,19 @@ func TestDuplicateFile(t *testing.T) {
test.IsEqualString(t, newFile.PasswordHash, "")
test.IsEqualBool(t, newFile.UnlimitedDownloads, false)
test.IsEqualBool(t, newFile.UnlimitedTime, false)
test.IsEqualString(t, newFile.Name, "test.dat")
newFile, err = DuplicateFile(retrievedFile, ParamExpiry, uploadRequest)
newFile, err = DuplicateFile(retrievedFile, ParamName, "123", uploadRequest)
test.IsNil(t, err)
test.IsEqualInt(t, newFile.DownloadCount, 0)
test.IsEqualInt(t, newFile.DownloadsRemaining, 1)
test.IsEqualInt64(t, newFile.ExpireAt, 2147483600)
test.IsEqualString(t, newFile.PasswordHash, "")
test.IsEqualBool(t, newFile.UnlimitedDownloads, false)
test.IsEqualBool(t, newFile.UnlimitedTime, false)
test.IsEqualString(t, newFile.Name, "123")
newFile, err = DuplicateFile(retrievedFile, ParamExpiry, "123", uploadRequest)
test.IsNil(t, err)
test.IsEqualInt(t, newFile.DownloadCount, 0)
test.IsEqualInt(t, newFile.DownloadsRemaining, 1)
@@ -368,8 +380,9 @@ func TestDuplicateFile(t *testing.T) {
test.IsEqualString(t, newFile.PasswordHash, "")
test.IsEqualBool(t, newFile.UnlimitedDownloads, false)
test.IsEqualBool(t, newFile.UnlimitedTime, true)
test.IsEqualString(t, newFile.Name, "test.dat")
newFile, err = DuplicateFile(retrievedFile, ParamDownloads, uploadRequest)
newFile, err = DuplicateFile(retrievedFile, ParamDownloads, "123", uploadRequest)
test.IsNil(t, err)
test.IsEqualInt(t, newFile.DownloadCount, 0)
test.IsEqualInt(t, newFile.DownloadsRemaining, 5)
@@ -377,8 +390,9 @@ func TestDuplicateFile(t *testing.T) {
test.IsEqualString(t, newFile.PasswordHash, "")
test.IsEqualBool(t, newFile.UnlimitedDownloads, true)
test.IsEqualBool(t, newFile.UnlimitedTime, false)
test.IsEqualString(t, newFile.Name, "test.dat")
newFile, err = DuplicateFile(retrievedFile, ParamPassword, uploadRequest)
newFile, err = DuplicateFile(retrievedFile, ParamPassword, "123", uploadRequest)
test.IsNil(t, err)
test.IsEqualInt(t, newFile.DownloadCount, 0)
test.IsEqualInt(t, newFile.DownloadsRemaining, 1)
@@ -386,9 +400,10 @@ func TestDuplicateFile(t *testing.T) {
test.IsNotEqualString(t, newFile.PasswordHash, "")
test.IsEqualBool(t, newFile.UnlimitedDownloads, false)
test.IsEqualBool(t, newFile.UnlimitedTime, false)
test.IsEqualString(t, newFile.Name, "test.dat")
retrievedFile.PasswordHash = "ahash"
newFile, err = DuplicateFile(retrievedFile, 0, uploadRequest)
newFile, err = DuplicateFile(retrievedFile, 0, "123", uploadRequest)
test.IsNil(t, err)
test.IsEqualInt(t, newFile.DownloadCount, 0)
test.IsEqualInt(t, newFile.DownloadsRemaining, 1)
@@ -396,9 +411,10 @@ func TestDuplicateFile(t *testing.T) {
test.IsEqualString(t, newFile.PasswordHash, "ahash")
test.IsEqualBool(t, newFile.UnlimitedDownloads, false)
test.IsEqualBool(t, newFile.UnlimitedTime, false)
test.IsEqualString(t, newFile.Name, "test.dat")
uploadRequest.Password = ""
newFile, err = DuplicateFile(retrievedFile, ParamPassword, uploadRequest)
newFile, err = DuplicateFile(retrievedFile, ParamPassword, "123", uploadRequest)
test.IsNil(t, err)
test.IsEqualInt(t, newFile.DownloadCount, 0)
test.IsEqualInt(t, newFile.DownloadsRemaining, 1)
@@ -406,9 +422,10 @@ func TestDuplicateFile(t *testing.T) {
test.IsEqualString(t, newFile.PasswordHash, "")
test.IsEqualBool(t, newFile.UnlimitedDownloads, false)
test.IsEqualBool(t, newFile.UnlimitedTime, false)
test.IsEqualString(t, newFile.Name, "test.dat")
uploadRequest.Password = "123"
newFile, err = DuplicateFile(retrievedFile, ParamExpiry|ParamPassword|ParamDownloads, uploadRequest)
newFile, err = DuplicateFile(retrievedFile, ParamExpiry|ParamPassword|ParamDownloads|ParamName, "123", uploadRequest)
test.IsNil(t, err)
test.IsEqualInt(t, newFile.DownloadCount, 0)
test.IsEqualInt(t, newFile.DownloadsRemaining, 5)
@@ -416,6 +433,7 @@ func TestDuplicateFile(t *testing.T) {
test.IsNotEqualString(t, newFile.PasswordHash, "")
test.IsEqualBool(t, newFile.UnlimitedDownloads, true)
test.IsEqualBool(t, newFile.UnlimitedTime, true)
test.IsEqualString(t, newFile.Name, "123")
}

View File

@@ -376,7 +376,7 @@ type DownloadView struct {
// UploadView contains parameters for the admin menu template
type UploadView struct {
Items []models.File
Items []models.FileApiOutput
ApiKeys []models.ApiKey
Url string
HotlinkUrl string
@@ -397,11 +397,13 @@ type UploadView struct {
// Converts the globalConfig variable to an UploadView struct to pass the infos to
// the admin template
func (u *UploadView) convertGlobalConfig(isMainView bool) *UploadView {
var result []models.File
var result []models.FileApiOutput
var resultApi []models.ApiKey
if isMainView {
for _, element := range database.GetAllMetadata() {
result = append(result, element)
fileInfo, err := element.ToFileApiOutput()
helper.Check(err)
result = append(result, fileInfo)
}
sort.Slice(result[:], func(i, j int) bool {
if result[i].ExpireAt == result[j].ExpireAt {

View File

@@ -88,11 +88,13 @@ func deleteFile(w http.ResponseWriter, request apiRequest) {
}
func list(w http.ResponseWriter) {
var validFiles []models.File
var validFiles []models.FileApiOutput
timeNow := time.Now().Unix()
for _, element := range database.GetAllMetadata() {
if !storage.IsExpiredFile(element, timeNow) {
validFiles = append(validFiles, element)
file, err := element.ToFileApiOutput()
helper.Check(err)
validFiles = append(validFiles, file)
}
}
result, err := json.Marshal(validFiles)
@@ -120,35 +122,38 @@ func duplicateFile(w http.ResponseWriter, request apiRequest) {
sendError(w, http.StatusBadRequest, "Invalid id provided.")
return
}
uploadRequest, paramsToChange, err := apiRequestToUploadRequest(request.request)
uploadRequest, paramsToChange, filename, err := apiRequestToUploadRequest(request.request)
if err != nil {
sendError(w, http.StatusBadRequest, err.Error())
return
}
newFile, err := storage.DuplicateFile(file, paramsToChange, uploadRequest)
newFile, err := storage.DuplicateFile(file, paramsToChange, filename, uploadRequest)
if err != nil {
sendError(w, http.StatusBadRequest, err.Error())
return
}
result, err := json.Marshal(newFile)
publicOutput, err := newFile.ToFileApiOutput()
helper.Check(err)
result, err := json.Marshal(publicOutput)
helper.Check(err)
_, _ = w.Write(result)
}
func apiRequestToUploadRequest(request *http.Request) (models.UploadRequest, int, error) {
func apiRequestToUploadRequest(request *http.Request) (models.UploadRequest, int, string, error) {
paramsToChange := 0
allowedDownloads := 0
daysExpiry := 0
unlimitedTime := false
unlimitedDownloads := false
password := ""
fileName := ""
var err error
if request.Form.Get("allowedDownloads") != "" {
paramsToChange = paramsToChange | storage.ParamDownloads
allowedDownloads, err = strconv.Atoi(request.Form.Get("allowedDownloads"))
if err != nil {
return models.UploadRequest{}, 0, err
return models.UploadRequest{}, 0, "", err
}
if allowedDownloads == 0 {
unlimitedDownloads = true
@@ -159,7 +164,7 @@ func apiRequestToUploadRequest(request *http.Request) (models.UploadRequest, int
paramsToChange = paramsToChange | storage.ParamExpiry
daysExpiry, err = strconv.Atoi(request.Form.Get("expiryDays"))
if err != nil {
return models.UploadRequest{}, 0, err
return models.UploadRequest{}, 0, "", err
}
if daysExpiry == 0 {
unlimitedTime = true
@@ -171,6 +176,11 @@ func apiRequestToUploadRequest(request *http.Request) (models.UploadRequest, int
password = request.Form.Get("password")
}
if request.Form.Get("filename") != "" {
paramsToChange = paramsToChange | storage.ParamName
fileName = request.Form.Get("filename")
}
return models.UploadRequest{
AllowedDownloads: allowedDownloads,
Expiry: daysExpiry,
@@ -178,7 +188,7 @@ func apiRequestToUploadRequest(request *http.Request) (models.UploadRequest, int
UnlimitedDownload: unlimitedDownloads,
Password: password,
ExpiryTimestamp: time.Now().Add(time.Duration(daysExpiry) * time.Hour * 24).Unix(),
}, paramsToChange, nil
}, paramsToChange, fileName, nil
}
func isAuthorisedForApi(w http.ResponseWriter, request apiRequest) bool {

View File

@@ -192,7 +192,7 @@ func TestUploadAndDuplication(t *testing.T) {
test.IsEqualString(t, result.Result, "OK")
test.IsEqualString(t, result.FileInfo.Size, "3 B")
test.IsEqualInt(t, result.FileInfo.DownloadsRemaining, 200)
test.IsNotEqualString(t, result.FileInfo.PasswordHash, "")
test.IsEqualBool(t, result.FileInfo.IsPasswordProtected, true)
test.IsEqualString(t, result.Url, "http://127.0.0.1:53843/d?id=")
newFileId := result.FileInfo.Id
@@ -238,7 +238,7 @@ func TestUploadAndDuplication(t *testing.T) {
{Name: "Content-type", Value: "application/x-www-form-urlencoded"}},
strings.NewReader(data.Encode()))
Process(w, r, maxMemory)
resultDuplication := models.File{}
resultDuplication := models.FileApiOutput{}
response, err = io.ReadAll(w.Result().Body)
test.IsNil(t, err)
err = json.Unmarshal(response, &resultDuplication)
@@ -247,7 +247,7 @@ func TestUploadAndDuplication(t *testing.T) {
test.IsEqualBool(t, resultDuplication.UnlimitedTime, false)
test.IsEqualBool(t, resultDuplication.UnlimitedDownloads, false)
test.IsEqualInt(t, resultDuplication.DownloadCount, 0)
test.IsEqualString(t, resultDuplication.PasswordHash, "")
test.IsEqualBool(t, resultDuplication.IsPasswordProtected, false)
data = url.Values{}
data.Set("id", newFileId)
@@ -259,7 +259,7 @@ func TestUploadAndDuplication(t *testing.T) {
{Name: "Content-type", Value: "application/x-www-form-urlencoded"}},
strings.NewReader(data.Encode()))
Process(w, r, maxMemory)
resultDuplication = models.File{}
resultDuplication = models.FileApiOutput{}
response, err = io.ReadAll(w.Result().Body)
test.IsNil(t, err)
err = json.Unmarshal(response, &resultDuplication)
@@ -277,7 +277,7 @@ func TestUploadAndDuplication(t *testing.T) {
{Name: "Content-type", Value: "application/x-www-form-urlencoded"}},
strings.NewReader(data.Encode()))
Process(w, r, maxMemory)
resultDuplication = models.File{}
resultDuplication = models.FileApiOutput{}
response, err = io.ReadAll(w.Result().Body)
test.IsNil(t, err)
err = json.Unmarshal(response, &resultDuplication)
@@ -318,7 +318,7 @@ func TestUploadAndDuplication(t *testing.T) {
{Name: "Content-type", Value: "application/x-www-form-urlencoded"}},
strings.NewReader(data.Encode()))
Process(w, r, maxMemory)
resultDuplication = models.File{}
resultDuplication = models.FileApiOutput{}
response, err = io.ReadAll(w.Result().Body)
test.IsNil(t, err)
err = json.Unmarshal(response, &resultDuplication)
@@ -335,7 +335,7 @@ func TestUploadAndDuplication(t *testing.T) {
{Name: "Content-type", Value: "application/x-www-form-urlencoded"}},
strings.NewReader(data.Encode()))
Process(w, r, maxMemory)
resultDuplication = models.File{}
resultDuplication = models.FileApiOutput{}
response, err = io.ReadAll(w.Result().Body)
test.IsNil(t, err)
err = json.Unmarshal(response, &resultDuplication)
@@ -352,12 +352,12 @@ func TestUploadAndDuplication(t *testing.T) {
{Name: "Content-type", Value: "application/x-www-form-urlencoded"}},
strings.NewReader(data.Encode()))
Process(w, r, maxMemory)
resultDuplication = models.File{}
resultDuplication = models.FileApiOutput{}
response, err = io.ReadAll(w.Result().Body)
test.IsNil(t, err)
err = json.Unmarshal(response, &resultDuplication)
test.IsNil(t, err)
test.IsNotEqualString(t, resultDuplication.PasswordHash, "")
test.IsEqualBool(t, resultDuplication.IsPasswordProtected, true)
data = url.Values{}
data.Set("id", newFileId)
@@ -368,12 +368,12 @@ func TestUploadAndDuplication(t *testing.T) {
{Name: "Content-type", Value: "application/x-www-form-urlencoded"}},
strings.NewReader(data.Encode()))
Process(w, r, maxMemory)
resultDuplication = models.File{}
resultDuplication = models.FileApiOutput{}
response, err = io.ReadAll(w.Result().Body)
test.IsNil(t, err)
err = json.Unmarshal(response, &resultDuplication)
test.IsNil(t, err)
test.IsEqualString(t, resultDuplication.PasswordHash, "")
test.IsEqualBool(t, resultDuplication.IsPasswordProtected, false)
}
func TestList(t *testing.T) {

View File

@@ -217,12 +217,23 @@
"File": {
"type": "object",
"properties": {
"ContentType": {
"Id": {
"type": "string"
},
"DownloadsRemaining": {
"type": "integer",
"format": "int64"
"Name": {
"type": "string"
},
"Size": {
"type": "string"
},
"SHA256": {
"type": "string"
},
"HotlinkId": {
"type": "string"
},
"ContentType": {
"type": "string"
},
"ExpireAt": {
"type": "integer",
@@ -231,23 +242,31 @@
"ExpireAtString": {
"type": "string"
},
"HotlinkId": {
"type": "string"
"DownloadsRemaining": {
"type": "integer",
"format": "int64"
},
"Id": {
"type": "string"
"DownloadCount": {
"type": "integer",
"format": "int64"
},
"Name": {
"type": "string"
"UnlimitedDownloads": {
"type": "boolean"
},
"PasswordHash": {
"type": "string"
"UnlimitedTime": {
"type": "boolean"
},
"SHA256": {
"type": "string"
"RequiresClientSideDecryption": {
"type": "boolean"
},
"Size": {
"type": "string"
"IsEncrypted": {
"type": "boolean"
},
"IsPasswordProtected": {
"type": "boolean"
},
"IsSavedOnLocalStorage": {
"type": "boolean"
}
},
"description": "File is a struct used for saving information about an uploaded file",
@@ -316,11 +335,15 @@
},
"password": {
"type": "string",
"description": "Password for this file to be set. No password will be used if empty-"
"description": "Password for this file to be set. No password will be used if empty."
},
"originalPassword": {
"type": "boolean",
"description": "Set to true to use original password. Field \"password\" will be ignored if set."
},
"filename": {
"type": "string",
"description": "Sets a new filename. Filename will be unchanged if empty."
}
}
}

View File

@@ -91,7 +91,7 @@ function addRow(jsonText) {
let cellButtons = row.insertCell(6);
let lockIcon = "";
if (item.PasswordHash !== "") {
if (item.IsPasswordProtected === true) {
lockIcon = " &#128274;";
}
cellFilename.innerText = item.Name;
@@ -113,7 +113,7 @@ function addRow(jsonText) {
if (item.HotlinkId !== "") {
buttons = buttons + '<button type="button" data-clipboard-text="' + jsonObject.HotlinkUrl + item.HotlinkId + '" class="copyurl btn btn-outline-light btn-sm">Copy Hotlink</button> ';
} else {
if (item.RequiresClientSideDecryption === false && item.PasswordHash === "") {
if (item.RequiresClientSideDecryption === false && item.IsPasswordProtected === false) {
buttons = buttons + '<button type="button" data-clipboard-text="' + jsonObject.GenericHotlinkUrl + item.Id + '" class="copyurl btn btn-outline-light btn-sm">Copy Hotlink</button> ';
} else {
buttons = buttons + '<button type="button"class="copyurl btn btn-outline-light btn-sm disabled">Copy Hotlink</button> ';

View File

@@ -71,12 +71,12 @@
<td scope="col">{{ .ExpireAtString }}</td>
{{ end }}
<td scope="col">{{ .DownloadCount }}</td>
<td scope="col"><a target="_blank" href="{{ $.Url }}{{ .Id }}">{{ $.Url }}{{ .Id }}</a>{{ if ne .PasswordHash "" }} &#128274;{{ end }}</td>
<td scope="col"><a target="_blank" href="{{ $.Url }}{{ .Id }}">{{ $.Url }}{{ .Id }}</a>{{ if .IsPasswordProtected }} &#128274;{{ end }}</td>
<td scope="col"><button type="button" data-clipboard-text="{{ $.Url }}{{ .Id }}" class="copyurl btn btn-outline-light btn-sm">Copy URL</button>
{{ if ne .HotlinkId "" }}
<button type="button" data-clipboard-text="{{ $.HotlinkUrl }}{{ .HotlinkId }}" class="copyurl btn btn-outline-light btn-sm">Copy Hotlink</button>
{{ else }}
{{ if and (not .RequiresClientSideDecryption) (eq .PasswordHash "") }}
{{ if and (not .IsPasswordProtected) (not .RequiresClientSideDecryption) }}
<button type="button" data-clipboard-text="{{ $.GenericHotlinkUrl }}{{ .Id }}" class="copyurl btn btn-outline-light btn-sm">Copy Hotlink</button>
{{ else }}
<button type="button"class="copyurl btn btn-outline-light btn-sm disabled">Copy Hotlink</button>
@@ -95,7 +95,7 @@
</div>
</div>
<script src="./js/admin.js?v=11"></script>
<script src="./js/admin.js?v=12"></script>
<script>
Dropzone.options.uploaddropzone["maxFilesize"] = {{ .MaxFileSize }};
Dropzone.options.uploaddropzone["dictDefaultMessage"] = "Drop files, paste or click here to upload";

View File

@@ -217,12 +217,23 @@
"File": {
"type": "object",
"properties": {
"ContentType": {
"Id": {
"type": "string"
},
"DownloadsRemaining": {
"type": "integer",
"format": "int64"
"Name": {
"type": "string"
},
"Size": {
"type": "string"
},
"SHA256": {
"type": "string"
},
"HotlinkId": {
"type": "string"
},
"ContentType": {
"type": "string"
},
"ExpireAt": {
"type": "integer",
@@ -231,23 +242,31 @@
"ExpireAtString": {
"type": "string"
},
"HotlinkId": {
"type": "string"
"DownloadsRemaining": {
"type": "integer",
"format": "int64"
},
"Id": {
"type": "string"
"DownloadCount": {
"type": "integer",
"format": "int64"
},
"Name": {
"type": "string"
"UnlimitedDownloads": {
"type": "boolean"
},
"PasswordHash": {
"type": "string"
"UnlimitedTime": {
"type": "boolean"
},
"SHA256": {
"type": "string"
"RequiresClientSideDecryption": {
"type": "boolean"
},
"Size": {
"type": "string"
"IsEncrypted": {
"type": "boolean"
},
"IsPasswordProtected": {
"type": "boolean"
},
"IsSavedOnLocalStorage": {
"type": "boolean"
}
},
"description": "File is a struct used for saving information about an uploaded file",
@@ -316,11 +335,15 @@
},
"password": {
"type": "string",
"description": "Password for this file to be set. No password will be used if empty-"
"description": "Password for this file to be set. No password will be used if empty."
},
"originalPassword": {
"type": "boolean",
"description": "Set to true to use original password. Field \"password\" will be ignored if set."
},
"filename": {
"type": "string",
"description": "Sets a new filename. Filename will be unchanged if empty."
}
}
}