diff --git a/internal/configuration/configupgrade/Upgrade.go b/internal/configuration/configupgrade/Upgrade.go
index 8282fb4..7a1a43f 100644
--- a/internal/configuration/configupgrade/Upgrade.go
+++ b/internal/configuration/configupgrade/Upgrade.go
@@ -11,7 +11,7 @@ import (
)
// CurrentConfigVersion is the version of the configuration structure. Used for upgrading
-const CurrentConfigVersion = 19
+const CurrentConfigVersion = 20
// DoUpgrade checks if an old version is present and updates it to the current version if required
func DoUpgrade(settings *models.Configuration, env *environment.Environment) bool {
@@ -65,7 +65,7 @@ func updateConfig(settings *models.Configuration, env *environment.Environment)
}
settings.Authentication.OAuthRecheckInterval = 168
}
- // < v1.8.5
+ // < v1.8.5beta
if settings.ConfigVersion < 19 {
if settings.MaxMemory == 40 {
settings.MaxMemory = 50
@@ -73,6 +73,16 @@ func updateConfig(settings *models.Configuration, env *environment.Environment)
settings.ChunkSize = env.ChunkSizeMB
settings.MaxParallelUploads = env.MaxParallelUploads
}
+ // < v1.8.5
+ if settings.ConfigVersion < 20 {
+ err := database.RawSqlite(`DROP TABLE UploadStatus; CREATE TABLE "UploadStatus" (
+ "ChunkId" TEXT NOT NULL UNIQUE,
+ "CurrentStatus" INTEGER NOT NULL,
+ "CreationDate" INTEGER NOT NULL,
+ PRIMARY KEY("ChunkId")
+) WITHOUT ROWID;`)
+ helper.Check(err)
+ }
}
// migrateToSqlite copies the content of the old bitcask database to a new sqlite database
diff --git a/internal/configuration/database/Database.go b/internal/configuration/database/Database.go
index 0bddbb4..6e35e1c 100644
--- a/internal/configuration/database/Database.go
+++ b/internal/configuration/database/Database.go
@@ -146,7 +146,6 @@ func createNewDatabase() {
CREATE TABLE "UploadStatus" (
"ChunkId" TEXT NOT NULL UNIQUE,
"CurrentStatus" INTEGER NOT NULL,
- "LastUpdate" INTEGER NOT NULL,
"CreationDate" INTEGER NOT NULL,
PRIMARY KEY("ChunkId")
) WITHOUT ROWID;
diff --git a/internal/configuration/database/Database_test.go b/internal/configuration/database/Database_test.go
index eac5eb8..15caf90 100644
--- a/internal/configuration/database/Database_test.go
+++ b/internal/configuration/database/Database_test.go
@@ -216,54 +216,44 @@ func TestGarbageCollectionUploads(t *testing.T) {
SaveUploadStatus(models.UploadStatus{
ChunkId: "ctodelete1",
CurrentStatus: 0,
- LastUpdate: time.Now().Add(-24 * time.Hour).Unix(),
})
SaveUploadStatus(models.UploadStatus{
ChunkId: "ctodelete2",
CurrentStatus: 1,
- LastUpdate: time.Now().Add(-24 * time.Hour).Unix(),
})
SaveUploadStatus(models.UploadStatus{
ChunkId: "ctodelete3",
CurrentStatus: 0,
- LastUpdate: 0,
})
SaveUploadStatus(models.UploadStatus{
ChunkId: "ctodelete4",
CurrentStatus: 0,
- LastUpdate: time.Now().Add(-20 * time.Hour).Unix(),
})
SaveUploadStatus(models.UploadStatus{
ChunkId: "ctodelete5",
CurrentStatus: 1,
- LastUpdate: time.Now().Add(40 * time.Hour).Unix(),
})
currentTime = orgiginalFunc
SaveUploadStatus(models.UploadStatus{
ChunkId: "ctokeep1",
CurrentStatus: 0,
- LastUpdate: time.Now().Add(-24 * time.Hour).Unix(),
})
SaveUploadStatus(models.UploadStatus{
ChunkId: "ctokeep2",
CurrentStatus: 1,
- LastUpdate: time.Now().Add(-24 * time.Hour).Unix(),
})
SaveUploadStatus(models.UploadStatus{
ChunkId: "ctokeep3",
CurrentStatus: 0,
- LastUpdate: 0,
})
SaveUploadStatus(models.UploadStatus{
ChunkId: "ctokeep4",
CurrentStatus: 0,
- LastUpdate: time.Now().Add(-20 * time.Hour).Unix(),
})
SaveUploadStatus(models.UploadStatus{
ChunkId: "ctokeep5",
CurrentStatus: 1,
- LastUpdate: time.Now().Add(40 * time.Hour).Unix(),
})
for _, item := range []string{"ctodelete1", "ctodelete2", "ctodelete3", "ctodelete4", "ctokeep1", "ctokeep2", "ctokeep3", "ctokeep4"} {
_, result := GetUploadStatus(item)
@@ -463,3 +453,29 @@ func TestParallelConnectionsReading(t *testing.T) {
}
wg.Wait()
}
+
+func TestUploadStatus(t *testing.T) {
+ allStatus := GetAllUploadStatus()
+ found := false
+ test.IsEqualInt(t, len(allStatus), 5)
+ for _, status := range allStatus {
+ if status.ChunkId == "ctokeep5" {
+ found = true
+ }
+ }
+ test.IsEqualBool(t, found, true)
+ newStatus := models.UploadStatus{
+ ChunkId: "testid",
+ CurrentStatus: 1,
+ }
+ retrievedStatus, ok := GetUploadStatus("testid")
+ test.IsEqualBool(t, ok, false)
+ test.IsEqualBool(t, retrievedStatus == models.UploadStatus{}, true)
+ SaveUploadStatus(newStatus)
+ retrievedStatus, ok = GetUploadStatus("testid")
+ test.IsEqualBool(t, ok, true)
+ test.IsEqualString(t, retrievedStatus.ChunkId, "testid")
+ test.IsEqualInt(t, retrievedStatus.CurrentStatus, 1)
+ allStatus = GetAllUploadStatus()
+ test.IsEqualInt(t, len(allStatus), 6)
+}
diff --git a/internal/configuration/database/uploadstatus.go b/internal/configuration/database/uploadstatus.go
index 91db70e..3bb6d7b 100644
--- a/internal/configuration/database/uploadstatus.go
+++ b/internal/configuration/database/uploadstatus.go
@@ -23,12 +23,11 @@ func GetAllUploadStatus() []models.UploadStatus {
defer rows.Close()
for rows.Next() {
rowResult := schemaUploadStatus{}
- err = rows.Scan(&rowResult.ChunkId, &rowResult.CurrentStatus, &rowResult.LastUpdate, &rowResult.CreationDate)
+ err = rows.Scan(&rowResult.ChunkId, &rowResult.CurrentStatus, &rowResult.CreationDate)
helper.Check(err)
result = append(result, models.UploadStatus{
ChunkId: rowResult.ChunkId,
CurrentStatus: rowResult.CurrentStatus,
- LastUpdate: rowResult.LastUpdate,
})
}
return result
@@ -39,12 +38,11 @@ func GetUploadStatus(id string) (models.UploadStatus, bool) {
result := models.UploadStatus{
ChunkId: id,
CurrentStatus: 0,
- LastUpdate: 0,
}
var rowResult schemaUploadStatus
row := sqliteDb.QueryRow("SELECT * FROM UploadStatus WHERE ChunkId = ?", id)
- err := row.Scan(&rowResult.ChunkId, &rowResult.CurrentStatus, &rowResult.LastUpdate, &rowResult.CreationDate)
+ err := row.Scan(&rowResult.ChunkId, &rowResult.CurrentStatus, &rowResult.CreationDate)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return models.UploadStatus{}, false
@@ -53,7 +51,6 @@ func GetUploadStatus(id string) (models.UploadStatus, bool) {
return models.UploadStatus{}, false
}
result.CurrentStatus = rowResult.CurrentStatus
- result.LastUpdate = rowResult.LastUpdate
return result, true
}
@@ -67,12 +64,11 @@ func SaveUploadStatus(status models.UploadStatus) {
newData := schemaUploadStatus{
ChunkId: status.ChunkId,
CurrentStatus: status.CurrentStatus,
- LastUpdate: status.LastUpdate,
CreationDate: currentTime().Unix(),
}
- _, err := sqliteDb.Exec("INSERT OR REPLACE INTO UploadStatus (ChunkId, CurrentStatus, LastUpdate, CreationDate) VALUES (?, ?, ?, ?)",
- newData.ChunkId, newData.CurrentStatus, newData.LastUpdate, newData.CreationDate)
+ _, err := sqliteDb.Exec("INSERT OR REPLACE INTO UploadStatus (ChunkId, CurrentStatus, CreationDate) VALUES (?, ?, ?)",
+ newData.ChunkId, newData.CurrentStatus, newData.CreationDate)
helper.Check(err)
}
diff --git a/internal/models/UploadStatus.go b/internal/models/UploadStatus.go
index 7dcedb0..1e5fc2f 100644
--- a/internal/models/UploadStatus.go
+++ b/internal/models/UploadStatus.go
@@ -12,8 +12,6 @@ type UploadStatus struct {
// hashing) or being moved/uploaded to the file storage
// See processingstatus for definition
CurrentStatus int `json:"currentstatus"`
- // LastUpdate indicates the last status change
- LastUpdate int64 `json:"lastupdate"`
// Type is the type of the message and is always "uploadstatus"
Type string `json:"type"`
}
diff --git a/internal/models/UploadStatus_test.go b/internal/models/UploadStatus_test.go
index 8e56c32..ca0be83 100644
--- a/internal/models/UploadStatus_test.go
+++ b/internal/models/UploadStatus_test.go
@@ -9,5 +9,5 @@ func TestUploadStatus_ToJson(t *testing.T) {
status := UploadStatus{}
output, err := status.ToJson()
test.IsNil(t, err)
- test.IsEqualString(t, string(output), "{\"chunkid\":\"\",\"currentstatus\":0,\"lastupdate\":0,\"type\":\"uploadstatus\"}")
+ test.IsEqualString(t, string(output), "{\"chunkid\":\"\",\"currentstatus\":0,\"type\":\"uploadstatus\"}")
}
diff --git a/internal/storage/processingstatus/ProcessingStatus.go b/internal/storage/processingstatus/ProcessingStatus.go
index 1b32eb3..7e18b6b 100644
--- a/internal/storage/processingstatus/ProcessingStatus.go
+++ b/internal/storage/processingstatus/ProcessingStatus.go
@@ -5,7 +5,6 @@ import (
"github.com/forceu/gokapi/internal/helper"
"github.com/forceu/gokapi/internal/models"
"github.com/forceu/gokapi/internal/webserver/sse"
- "time"
)
// StatusHashingOrEncrypting indicates that the file has been completely uploaded, but is now processed by Gokapi
@@ -25,7 +24,6 @@ func Set(id string, status int) {
newStatus := models.UploadStatus{
ChunkId: id,
CurrentStatus: status,
- LastUpdate: time.Now().Unix(),
}
oldStatus, ok := database.GetUploadStatus(newStatus.ChunkId)
if ok && oldStatus.CurrentStatus > newStatus.CurrentStatus {
diff --git a/internal/storage/processingstatus/ProcessingStatus_test.go b/internal/storage/processingstatus/ProcessingStatus_test.go
index e9aabf1..49d9fb3 100644
--- a/internal/storage/processingstatus/ProcessingStatus_test.go
+++ b/internal/storage/processingstatus/ProcessingStatus_test.go
@@ -38,7 +38,6 @@ func TestSetStatus(t *testing.T) {
initialStatus := models.UploadStatus{
ChunkId: chunkID,
CurrentStatus: tc.initialStatus,
- LastUpdate: time.Now().Unix(),
}
database.SaveUploadStatus(initialStatus)
diff --git a/internal/test/testconfiguration/TestConfiguration.go b/internal/test/testconfiguration/TestConfiguration.go
index f625e4e..ce3a477 100644
--- a/internal/test/testconfiguration/TestConfiguration.go
+++ b/internal/test/testconfiguration/TestConfiguration.go
@@ -11,6 +11,7 @@ import (
"github.com/forceu/gokapi/internal/storage/filesystem/s3filesystem/aws"
"github.com/johannesboyne/gofakes3"
"github.com/johannesboyne/gofakes3/backend/s3mem"
+ "log"
"net/http/httptest"
"os"
"strings"
@@ -172,20 +173,20 @@ func writeTestSessions() {
})
}
func writeTestUploadStatus() {
- database.SaveUploadStatus(models.UploadStatus{
- ChunkId: "expiredstatus",
- CurrentStatus: 0,
- LastUpdate: 100,
- })
+ err := database.RawSqlite(`INSERT OR REPLACE INTO UploadStatus
+ ("ChunkId", "CurrentStatus", "CreationDate")
+ VALUES ('expiredstatus', 0, 100);`)
+ if err != nil {
+ log.Println(err)
+ log.Fatal("Could not execute SQL")
+ }
database.SaveUploadStatus(models.UploadStatus{
ChunkId: "validstatus_0",
CurrentStatus: 0,
- LastUpdate: 2065000681,
})
database.SaveUploadStatus(models.UploadStatus{
ChunkId: "validstatus_1",
CurrentStatus: 1,
- LastUpdate: 2065000681,
})
}
@@ -342,21 +343,31 @@ var configTestFile = []byte(`{
"Username": "test",
"Password": "10340aece68aa4fb14507ae45b05506026f276cf",
"HeaderKey": "",
- "OAuthProvider": "",
+ "OauthProvider": "",
"OAuthClientId": "",
"OAuthClientSecret": "",
+ "OauthUserScope": "",
+ "OauthGroupScope": "",
+ "OAuthRecheckInterval": 12,
"HeaderUsers": null,
- "OAuthUsers": null
+ "OAuthGroups": [],
+ "OauthUsers": []
},
- "Port":"127.0.0.1:53843",
+ "Port":"127.0.0.1:53843",
"ServerUrl": "http://127.0.0.1:53843/",
"RedirectUrl": "https://test.com/",
- "ConfigVersion": 16,
+ "PublicName": "Gokapi Test Version",
+ "ConfigVersion": 20,
"LengthId": 20,
"DataDir": "test/data",
+ "MaxFileSizeMB": 25,
"MaxMemory": 10,
+ "ChunkSize": 45,
+ "MaxParallelUploads": 4,
"UseSsl": false,
- "MaxFileSizeMB": 25
+ "PicturesAlwaysLocal": false,
+ "SaveIp": false,
+ "IncludeFilename": false
}`)
var sslCertValid = []byte(`-----BEGIN CERTIFICATE-----
diff --git a/internal/webserver/sse/Sse_test.go b/internal/webserver/sse/Sse_test.go
index 55d7266..6464dbf 100644
--- a/internal/webserver/sse/Sse_test.go
+++ b/internal/webserver/sse/Sse_test.go
@@ -91,7 +91,7 @@ func TestGetStatusSSE(t *testing.T) {
body, err := io.ReadAll(rr.Body)
test.IsNil(t, err)
- test.IsEqualString(t, string(body), "{\"chunkid\":\"expiredstatus\",\"currentstatus\":0,\"lastupdate\":100,\"type\":\"uploadstatus\"}\n{\"chunkid\":\"validstatus_0\",\"currentstatus\":0,\"lastupdate\":2065000681,\"type\":\"uploadstatus\"}\n{\"chunkid\":\"validstatus_1\",\"currentstatus\":1,\"lastupdate\":2065000681,\"type\":\"uploadstatus\"}\n")
+ test.IsEqualString(t, string(body), "{\"chunkid\":\"expiredstatus\",\"currentstatus\":0,\"type\":\"uploadstatus\"}\n{\"chunkid\":\"validstatus_0\",\"currentstatus\":0,\"type\":\"uploadstatus\"}\n{\"chunkid\":\"validstatus_1\",\"currentstatus\":1,\"type\":\"uploadstatus\"}\n")
// Test ping message
time.Sleep(3 * time.Second)
diff --git a/internal/webserver/web/static/js/admin.js b/internal/webserver/web/static/js/admin.js
index 9942e6b..d89cc26 100644
--- a/internal/webserver/web/static/js/admin.js
+++ b/internal/webserver/web/static/js/admin.js
@@ -473,7 +473,7 @@ function registerChangeHandler() {
console.log("Reconnecting to SSE...");
// Attempt to reconnect after a delay
- setTimeout(registerChangeHandler, 1000);
+ setTimeout(registerChangeHandler, 5000);
};
}
diff --git a/internal/webserver/web/static/js/min/admin.min.js b/internal/webserver/web/static/js/min/admin.min.js
index 1bf099c..a3ce8c7 100644
--- a/internal/webserver/web/static/js/min/admin.min.js
+++ b/internal/webserver/web/static/js/min/admin.min.js
@@ -1 +1 @@
-var clipboard=new ClipboardJS(".btn"),dropzoneObject,calendarInstance,statusItemCount,isE2EEnabled=!1,isUploading=!1,rowCount=-1;window.addEventListener("beforeunload",e=>{isUploading&&(e.returnValue="Upload is still in progress. Do you want to close this page?")}),Dropzone.options.uploaddropzone={paramName:"file",dictDefaultMessage:"Drop files, paste or click here to upload",createImageThumbnails:!1,chunksUploaded:function(e,t){sendChunkComplete(e,t)},init:function(){dropzoneObject=this,this.on("addedfile",e=>{addFileProgress(e)}),this.on("queuecomplete",function(){isUploading=!1}),this.on("sending",function(){isUploading=!0}),this.on("error",function(e,t,n){n&&n.status===413?showError(e,"File too large to upload. If you are using a reverse proxy, make sure that the allowed body size is at least 70MB."):showError(e,"Server responded with code "+n.status)}),this.on("uploadprogress",function(e,t,n){updateProgressbar(e,t,n)}),isE2EEnabled&&(dropzoneObject.disable(),dropzoneObject.options.dictDefaultMessage="Loading end-to-end encryption...",document.getElementsByClassName("dz-button")[0].innerText="Loading end-to-end encryption...",setE2eUpload())}};function updateProgressbar(e,t,n){let o=e.upload.uuid,i=document.getElementById(`us-container-${o}`);if(i==null||i.getAttribute("data-complete")==="true")return;let s=Math.round(t);s<0&&(s=0),s>100&&(s=100);let r=Date.now()-i.getAttribute("data-starttime"),c=n/(r/1e3)/1024/1024;document.getElementById(`us-progressbar-${o}`).style.width=s+"%";let a=Math.round(c*10)/10;Number.isNaN(a)||(document.getElementById(`us-progress-info-${o}`).innerText=s+"% - "+a+"MB/s")}function setProgressStatus(e,t){let s=document.getElementById(`us-container-${e}`);if(s==null)return;s.setAttribute("data-complete","true");let n;switch(t){case 0:n="Processing file...";break;case 1:n="Uploading file...";break}document.getElementById(`us-progress-info-${e}`).innerText=n}function addFileProgress(e){addFileStatus(e.upload.uuid,e.upload.filename)}document.onpaste=function(e){if(dropzoneObject.disabled)return;var t,n=(e.clipboardData||e.originalEvent.clipboardData).items;for(index in n)t=n[index],t.kind==="file"&&dropzoneObject.addFile(t.getAsFile()),t.kind==="string"&&t.getAsString(function(e){const t=/
/gi;if(t.test(e)===!1){let t=new Blob([e],{type:"text/plain"}),n=new File([t],"Pasted Text.txt",{type:"text/plain",lastModified:new Date(0)});dropzoneObject.addFile(n)}})};function urlencodeFormData(e){let t="";function s(e){return encodeURIComponent(e).replace(/%20/g,"+")}for(var n of e.entries())typeof n[1]=="string"&&(t+=(t?"&":"")+s(n[0])+"="+s(n[1]));return t}function sendChunkComplete(e,t){var s=new XMLHttpRequest;s.open("POST","./uploadComplete",!0),s.setRequestHeader("Content-Type","application/x-www-form-urlencoded");let n=new FormData;n.append("allowedDownloads",document.getElementById("allowedDownloads").value),n.append("expiryDays",document.getElementById("expiryDays").value),n.append("password",document.getElementById("password").value),n.append("isUnlimitedDownload",!document.getElementById("enableDownloadLimit").checked),n.append("isUnlimitedTime",!document.getElementById("enableTimeLimit").checked),n.append("chunkid",e.upload.uuid),e.isEndToEndEncrypted===!0?(n.append("filesize",e.sizeEncrypted),n.append("filename","Encrypted File"),n.append("filecontenttype",""),n.append("isE2E","true"),n.append("realSize",e.size)):(n.append("filesize",e.size),n.append("filename",e.name),n.append("filecontenttype",e.type)),s.onreadystatechange=function(){if(this.readyState==4)if(this.status==200){let n=addRow(s.response);if(e.isEndToEndEncrypted===!0){try{let s=GokapiE2EAddFile(e.upload.uuid,n,e.name);if(s instanceof Error)throw s;let t=GokapiE2EInfoEncrypt();if(t instanceof Error)throw t;storeE2EInfo(t)}catch(t){e.accepted=!1,dropzoneObject._errorProcessing([e],t);return}GokapiE2EDecryptMenu()}removeFileStatus(e.upload.uuid),t()}else{e.accepted=!1;let t=getErrorMessage(s.responseText);dropzoneObject._errorProcessing([e],t),showError(e,t)}},s.send(urlencodeFormData(n))}function getErrorMessage(e){let t;try{t=JSON.parse(e)}catch{return"Unknown error: Server could not process file"}return"Error: "+t.ErrorMessage}function showError(e,t){let n=e.upload.uuid;document.getElementById(`us-progressbar-${n}`).style.width="100%",document.getElementById(`us-progressbar-${n}`).style.backgroundColor="red",document.getElementById(`us-progress-info-${n}`).innerText=t,document.getElementById(`us-progress-info-${n}`).classList.add("uploaderror")}function editFile(){const e=document.getElementById("mb_save");e.disabled=!0;let i="./api/files/modify",n=document.getElementById("mi_edit_down").value,s=document.getElementById("mi_edit_expiry").value,t=document.getElementById("mi_edit_pw").value,o=t==="(unchanged)";document.getElementById("mc_download").checked||(n=0),document.getElementById("mc_expiry").checked||(s=0),document.getElementById("mc_password").checked||(o=!1,t="");const a={method:"PUT",headers:{"Content-Type":"application/json",id:e.getAttribute("data-fileid"),allowedDownloads:n,expiryTimestamp:s,password:t,originalPassword:o}};fetch(i,a).then(e=>{if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}).then(e=>{location.reload()}).catch(t=>{alert("Unable to edit file: "+t),console.error("Error:",t),e.disabled=!1})}calendarInstance=null;function createCalendar(e){const t=new Date(e*1e3);calendarInstance=flatpickr("#mi_edit_expiry",{enableTime:!0,dateFormat:"U",altInput:!0,altFormat:"Y-m-d H:i",allowInput:!0,time_24hr:!0,defaultDate:t,minDate:"today"})}function handleEditCheckboxChange(e){var t=document.getElementById(e.getAttribute("data-toggle-target")),n=e.getAttribute("data-timestamp");e.checked?(t.classList.remove("disabled"),t.removeAttribute("disabled"),n!=null&&(calendarInstance._input.disabled=!1)):(n!=null&&(calendarInstance._input.disabled=!0),t.classList.add("disabled"),t.setAttribute("disabled",!0))}function showEditModal(e,t,n,s,o,i,a){document.getElementById("m_filenamelabel").innerHTML=e,document.getElementById("mc_expiry").setAttribute("data-timestamp",s),document.getElementById("mb_save").setAttribute("data-fileid",t),createCalendar(s),i?(document.getElementById("mi_edit_down").value="1",document.getElementById("mi_edit_down").disabled=!0,document.getElementById("mc_download").checked=!1):(document.getElementById("mi_edit_down").value=n,document.getElementById("mi_edit_down").disabled=!1,document.getElementById("mc_download").checked=!0),a?(document.getElementById("mi_edit_expiry").value=add14DaysIfBeforeCurrentTime(s),document.getElementById("mi_edit_expiry").disabled=!0,document.getElementById("mc_expiry").checked=!1,calendarInstance._input.disabled=!0):(document.getElementById("mi_edit_expiry").value=s,document.getElementById("mi_edit_expiry").disabled=!1,document.getElementById("mc_expiry").checked=!0,calendarInstance._input.disabled=!1),o?(document.getElementById("mi_edit_pw").value="(unchanged)",document.getElementById("mi_edit_pw").disabled=!1,document.getElementById("mc_password").checked=!0):(document.getElementById("mi_edit_pw").value="",document.getElementById("mi_edit_pw").disabled=!0,document.getElementById("mc_password").checked=!1),new bootstrap.Modal("#modaledit",{}).show()}function selectTextForPw(e){e.value==="(unchanged)"&&e.setSelectionRange(0,e.value.length)}function add14DaysIfBeforeCurrentTime(e){let t=Date.now(),n=e*1e3;if(n{if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}).then(e=>{o?s.classList.add("apiperm-notgranted"):s.classList.add("apiperm-granted"),s.classList.remove("apiperm-processing")}).catch(e=>{o?s.classList.add("apiperm-granted"):s.classList.add("apiperm-notgranted"),s.classList.remove("apiperm-processing"),alert("Unable to set permission: "+e),console.error("Error:",e)})}function checkBoxChanged(e,t){let n=!e.checked;n?document.getElementById(t).setAttribute("disabled",""):document.getElementById(t).removeAttribute("disabled"),t==="password"&&n&&(document.getElementById("password").value="")}function parseData(e){return e?typeof e=="object"?e:typeof e=="string"?JSON.parse(e):{Result:"error"}:{Result:"error"}}function registerChangeHandler(){const e=new EventSource("./uploadStatus");e.onmessage=e=>{try{let t=JSON.parse(e.data);setProgressStatus(t.chunkid,t.currentstatus)}catch(e){console.error("Failed to parse event data:",e)}},e.onerror=t=>{t.target.readyState!==EventSource.CLOSED&&e.close(),console.log("Reconnecting to SSE..."),setTimeout(registerChangeHandler,1e3)}}statusItemCount=0;function addFileStatus(e,t){const n=document.createElement("div");n.setAttribute("id",`us-container-${e}`),n.classList.add("us-container");const a=document.createElement("div");a.classList.add("filename"),a.textContent=t,n.appendChild(a);const s=document.createElement("div");s.classList.add("upload-progress-container"),s.setAttribute("id",`us-progress-container-${e}`);const r=document.createElement("div");r.classList.add("upload-progress-bar");const o=document.createElement("div");o.setAttribute("id",`us-progressbar-${e}`),o.classList.add("upload-progress-bar-progress"),o.style.width="0%",r.appendChild(o);const i=document.createElement("div");i.setAttribute("id",`us-progress-info-${e}`),i.classList.add("upload-progress-info"),i.textContent="0%",s.appendChild(r),s.appendChild(i),n.appendChild(s),n.setAttribute("data-starttime",Date.now()),n.setAttribute("data-complete","false");const c=document.getElementById("uploadstatus");c.appendChild(n),c.style.visibility="visible",statusItemCount++}function removeFileStatus(e){const t=document.getElementById(`us-container-${e}`);if(t==null)return;t.remove(),statusItemCount--,statusItemCount<1&&(document.getElementById("uploadstatus").style.visibility="hidden")}function addRow(e){let r=parseData(e);if(r.Result!=="OK"){alert("Failed to upload file!"),location.reload();return}let t=r.FileInfo,p=document.getElementById("downloadtable"),s=p.insertRow(0),i=s.insertCell(0),a=s.insertCell(1),o=s.insertCell(2),c=s.insertCell(3),l=s.insertCell(4),d=s.insertCell(5),u=s.insertCell(6),h="";t.IsPasswordProtected===!0&&(h=' '),i.innerText=t.Name,i.id="cell-name-"+t.Id,a.innerText=t.Size,t.UnlimitedDownloads?o.innerText="Unlimited":o.innerText=t.DownloadsRemaining,t.UnlimitedTime?c.innerText="Unlimited":c.innerText=t.ExpireAtString,l.innerHTML="0",d.innerHTML=''+t.Id+""+h;let n=' ';t.UrlHotlink===""?n=n+' ':n=n+' ',n=n+' `,n=n+` ',n=n+``,u.innerHTML=n,i.style.backgroundColor="green",a.style.backgroundColor="green",a.setAttribute("data-order",r.FileInfo.SizeBytes),o.style.backgroundColor="green",c.style.backgroundColor="green",l.style.backgroundColor="green",d.style.backgroundColor="green",u.style.backgroundColor="green";let m=$("#maintable").DataTable();rowCount==-1&&(rowCount=m.rows().count()),rowCount=rowCount+1,m.row.add(s);let f=document.getElementsByClassName("dataTables_empty")[0];return typeof f!="undefined"?f.innerText="Files stored: "+rowCount:document.getElementsByClassName("dataTables_info")[0].innerText="Files stored: "+rowCount,t.Id}function hideQrCode(){document.getElementById("qroverlay").style.display="none",document.getElementById("qrcode").innerHTML=""}function showQrCode(e){const t=document.getElementById("qroverlay");t.style.display="block",new QRCode(document.getElementById("qrcode"),{text:e,width:200,height:200,colorDark:"#000000",colorLight:"#ffffff",correctLevel:QRCode.CorrectLevel.H}),t.addEventListener("click",hideQrCode)}function showToast(){let e=document.getElementById("toastnotification");e.classList.add("show"),setTimeout(()=>{e.classList.remove("show")},1e3)}
\ No newline at end of file
+var clipboard=new ClipboardJS(".btn"),dropzoneObject,calendarInstance,statusItemCount,isE2EEnabled=!1,isUploading=!1,rowCount=-1;window.addEventListener("beforeunload",e=>{isUploading&&(e.returnValue="Upload is still in progress. Do you want to close this page?")}),Dropzone.options.uploaddropzone={paramName:"file",dictDefaultMessage:"Drop files, paste or click here to upload",createImageThumbnails:!1,chunksUploaded:function(e,t){sendChunkComplete(e,t)},init:function(){dropzoneObject=this,this.on("addedfile",e=>{addFileProgress(e)}),this.on("queuecomplete",function(){isUploading=!1}),this.on("sending",function(){isUploading=!0}),this.on("error",function(e,t,n){n&&n.status===413?showError(e,"File too large to upload. If you are using a reverse proxy, make sure that the allowed body size is at least 70MB."):showError(e,"Server responded with code "+n.status)}),this.on("uploadprogress",function(e,t,n){updateProgressbar(e,t,n)}),isE2EEnabled&&(dropzoneObject.disable(),dropzoneObject.options.dictDefaultMessage="Loading end-to-end encryption...",document.getElementsByClassName("dz-button")[0].innerText="Loading end-to-end encryption...",setE2eUpload())}};function updateProgressbar(e,t,n){let o=e.upload.uuid,i=document.getElementById(`us-container-${o}`);if(i==null||i.getAttribute("data-complete")==="true")return;let s=Math.round(t);s<0&&(s=0),s>100&&(s=100);let r=Date.now()-i.getAttribute("data-starttime"),c=n/(r/1e3)/1024/1024;document.getElementById(`us-progressbar-${o}`).style.width=s+"%";let a=Math.round(c*10)/10;Number.isNaN(a)||(document.getElementById(`us-progress-info-${o}`).innerText=s+"% - "+a+"MB/s")}function setProgressStatus(e,t){let s=document.getElementById(`us-container-${e}`);if(s==null)return;s.setAttribute("data-complete","true");let n;switch(t){case 0:n="Processing file...";break;case 1:n="Uploading file...";break}document.getElementById(`us-progress-info-${e}`).innerText=n}function addFileProgress(e){addFileStatus(e.upload.uuid,e.upload.filename)}document.onpaste=function(e){if(dropzoneObject.disabled)return;var t,n=(e.clipboardData||e.originalEvent.clipboardData).items;for(index in n)t=n[index],t.kind==="file"&&dropzoneObject.addFile(t.getAsFile()),t.kind==="string"&&t.getAsString(function(e){const t=/
/gi;if(t.test(e)===!1){let t=new Blob([e],{type:"text/plain"}),n=new File([t],"Pasted Text.txt",{type:"text/plain",lastModified:new Date(0)});dropzoneObject.addFile(n)}})};function urlencodeFormData(e){let t="";function s(e){return encodeURIComponent(e).replace(/%20/g,"+")}for(var n of e.entries())typeof n[1]=="string"&&(t+=(t?"&":"")+s(n[0])+"="+s(n[1]));return t}function sendChunkComplete(e,t){var s=new XMLHttpRequest;s.open("POST","./uploadComplete",!0),s.setRequestHeader("Content-Type","application/x-www-form-urlencoded");let n=new FormData;n.append("allowedDownloads",document.getElementById("allowedDownloads").value),n.append("expiryDays",document.getElementById("expiryDays").value),n.append("password",document.getElementById("password").value),n.append("isUnlimitedDownload",!document.getElementById("enableDownloadLimit").checked),n.append("isUnlimitedTime",!document.getElementById("enableTimeLimit").checked),n.append("chunkid",e.upload.uuid),e.isEndToEndEncrypted===!0?(n.append("filesize",e.sizeEncrypted),n.append("filename","Encrypted File"),n.append("filecontenttype",""),n.append("isE2E","true"),n.append("realSize",e.size)):(n.append("filesize",e.size),n.append("filename",e.name),n.append("filecontenttype",e.type)),s.onreadystatechange=function(){if(this.readyState==4)if(this.status==200){let n=addRow(s.response);if(e.isEndToEndEncrypted===!0){try{let s=GokapiE2EAddFile(e.upload.uuid,n,e.name);if(s instanceof Error)throw s;let t=GokapiE2EInfoEncrypt();if(t instanceof Error)throw t;storeE2EInfo(t)}catch(t){e.accepted=!1,dropzoneObject._errorProcessing([e],t);return}GokapiE2EDecryptMenu()}removeFileStatus(e.upload.uuid),t()}else{e.accepted=!1;let t=getErrorMessage(s.responseText);dropzoneObject._errorProcessing([e],t),showError(e,t)}},s.send(urlencodeFormData(n))}function getErrorMessage(e){let t;try{t=JSON.parse(e)}catch{return"Unknown error: Server could not process file"}return"Error: "+t.ErrorMessage}function showError(e,t){let n=e.upload.uuid;document.getElementById(`us-progressbar-${n}`).style.width="100%",document.getElementById(`us-progressbar-${n}`).style.backgroundColor="red",document.getElementById(`us-progress-info-${n}`).innerText=t,document.getElementById(`us-progress-info-${n}`).classList.add("uploaderror")}function editFile(){const e=document.getElementById("mb_save");e.disabled=!0;let i="./api/files/modify",n=document.getElementById("mi_edit_down").value,s=document.getElementById("mi_edit_expiry").value,t=document.getElementById("mi_edit_pw").value,o=t==="(unchanged)";document.getElementById("mc_download").checked||(n=0),document.getElementById("mc_expiry").checked||(s=0),document.getElementById("mc_password").checked||(o=!1,t="");const a={method:"PUT",headers:{"Content-Type":"application/json",id:e.getAttribute("data-fileid"),allowedDownloads:n,expiryTimestamp:s,password:t,originalPassword:o}};fetch(i,a).then(e=>{if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}).then(e=>{location.reload()}).catch(t=>{alert("Unable to edit file: "+t),console.error("Error:",t),e.disabled=!1})}calendarInstance=null;function createCalendar(e){const t=new Date(e*1e3);calendarInstance=flatpickr("#mi_edit_expiry",{enableTime:!0,dateFormat:"U",altInput:!0,altFormat:"Y-m-d H:i",allowInput:!0,time_24hr:!0,defaultDate:t,minDate:"today"})}function handleEditCheckboxChange(e){var t=document.getElementById(e.getAttribute("data-toggle-target")),n=e.getAttribute("data-timestamp");e.checked?(t.classList.remove("disabled"),t.removeAttribute("disabled"),n!=null&&(calendarInstance._input.disabled=!1)):(n!=null&&(calendarInstance._input.disabled=!0),t.classList.add("disabled"),t.setAttribute("disabled",!0))}function showEditModal(e,t,n,s,o,i,a){document.getElementById("m_filenamelabel").innerHTML=e,document.getElementById("mc_expiry").setAttribute("data-timestamp",s),document.getElementById("mb_save").setAttribute("data-fileid",t),createCalendar(s),i?(document.getElementById("mi_edit_down").value="1",document.getElementById("mi_edit_down").disabled=!0,document.getElementById("mc_download").checked=!1):(document.getElementById("mi_edit_down").value=n,document.getElementById("mi_edit_down").disabled=!1,document.getElementById("mc_download").checked=!0),a?(document.getElementById("mi_edit_expiry").value=add14DaysIfBeforeCurrentTime(s),document.getElementById("mi_edit_expiry").disabled=!0,document.getElementById("mc_expiry").checked=!1,calendarInstance._input.disabled=!0):(document.getElementById("mi_edit_expiry").value=s,document.getElementById("mi_edit_expiry").disabled=!1,document.getElementById("mc_expiry").checked=!0,calendarInstance._input.disabled=!1),o?(document.getElementById("mi_edit_pw").value="(unchanged)",document.getElementById("mi_edit_pw").disabled=!1,document.getElementById("mc_password").checked=!0):(document.getElementById("mi_edit_pw").value="",document.getElementById("mi_edit_pw").disabled=!0,document.getElementById("mc_password").checked=!1),new bootstrap.Modal("#modaledit",{}).show()}function selectTextForPw(e){e.value==="(unchanged)"&&e.setSelectionRange(0,e.value.length)}function add14DaysIfBeforeCurrentTime(e){let t=Date.now(),n=e*1e3;if(n{if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}).then(e=>{o?s.classList.add("apiperm-notgranted"):s.classList.add("apiperm-granted"),s.classList.remove("apiperm-processing")}).catch(e=>{o?s.classList.add("apiperm-granted"):s.classList.add("apiperm-notgranted"),s.classList.remove("apiperm-processing"),alert("Unable to set permission: "+e),console.error("Error:",e)})}function checkBoxChanged(e,t){let n=!e.checked;n?document.getElementById(t).setAttribute("disabled",""):document.getElementById(t).removeAttribute("disabled"),t==="password"&&n&&(document.getElementById("password").value="")}function parseData(e){return e?typeof e=="object"?e:typeof e=="string"?JSON.parse(e):{Result:"error"}:{Result:"error"}}function registerChangeHandler(){const e=new EventSource("./uploadStatus");e.onmessage=e=>{try{let t=JSON.parse(e.data);setProgressStatus(t.chunkid,t.currentstatus)}catch(e){console.error("Failed to parse event data:",e)}},e.onerror=t=>{t.target.readyState!==EventSource.CLOSED&&e.close(),console.log("Reconnecting to SSE..."),setTimeout(registerChangeHandler,5e3)}}statusItemCount=0;function addFileStatus(e,t){const n=document.createElement("div");n.setAttribute("id",`us-container-${e}`),n.classList.add("us-container");const a=document.createElement("div");a.classList.add("filename"),a.textContent=t,n.appendChild(a);const s=document.createElement("div");s.classList.add("upload-progress-container"),s.setAttribute("id",`us-progress-container-${e}`);const r=document.createElement("div");r.classList.add("upload-progress-bar");const o=document.createElement("div");o.setAttribute("id",`us-progressbar-${e}`),o.classList.add("upload-progress-bar-progress"),o.style.width="0%",r.appendChild(o);const i=document.createElement("div");i.setAttribute("id",`us-progress-info-${e}`),i.classList.add("upload-progress-info"),i.textContent="0%",s.appendChild(r),s.appendChild(i),n.appendChild(s),n.setAttribute("data-starttime",Date.now()),n.setAttribute("data-complete","false");const c=document.getElementById("uploadstatus");c.appendChild(n),c.style.visibility="visible",statusItemCount++}function removeFileStatus(e){const t=document.getElementById(`us-container-${e}`);if(t==null)return;t.remove(),statusItemCount--,statusItemCount<1&&(document.getElementById("uploadstatus").style.visibility="hidden")}function addRow(e){let r=parseData(e);if(r.Result!=="OK"){alert("Failed to upload file!"),location.reload();return}let t=r.FileInfo,p=document.getElementById("downloadtable"),s=p.insertRow(0),i=s.insertCell(0),a=s.insertCell(1),o=s.insertCell(2),c=s.insertCell(3),l=s.insertCell(4),d=s.insertCell(5),u=s.insertCell(6),h="";t.IsPasswordProtected===!0&&(h=' '),i.innerText=t.Name,i.id="cell-name-"+t.Id,a.innerText=t.Size,t.UnlimitedDownloads?o.innerText="Unlimited":o.innerText=t.DownloadsRemaining,t.UnlimitedTime?c.innerText="Unlimited":c.innerText=t.ExpireAtString,l.innerHTML="0",d.innerHTML=''+t.Id+""+h;let n=' ';t.UrlHotlink===""?n=n+' ':n=n+' ',n=n+' `,n=n+` ',n=n+``,u.innerHTML=n,i.style.backgroundColor="green",a.style.backgroundColor="green",a.setAttribute("data-order",r.FileInfo.SizeBytes),o.style.backgroundColor="green",c.style.backgroundColor="green",l.style.backgroundColor="green",d.style.backgroundColor="green",u.style.backgroundColor="green";let m=$("#maintable").DataTable();rowCount==-1&&(rowCount=m.rows().count()),rowCount=rowCount+1,m.row.add(s);let f=document.getElementsByClassName("dataTables_empty")[0];return typeof f!="undefined"?f.innerText="Files stored: "+rowCount:document.getElementsByClassName("dataTables_info")[0].innerText="Files stored: "+rowCount,t.Id}function hideQrCode(){document.getElementById("qroverlay").style.display="none",document.getElementById("qrcode").innerHTML=""}function showQrCode(e){const t=document.getElementById("qroverlay");t.style.display="block",new QRCode(document.getElementById("qrcode"),{text:e,width:200,height:200,colorDark:"#000000",colorLight:"#ffffff",correctLevel:QRCode.CorrectLevel.H}),t.addEventListener("click",hideQrCode)}function showToast(){let e=document.getElementById("toastnotification");e.classList.add("show"),setTimeout(()=>{e.classList.remove("show")},1e3)}
\ No newline at end of file
diff --git a/makefile b/makefile
index fc678f2..47c1591 100644
--- a/makefile
+++ b/makefile
@@ -9,17 +9,15 @@ all: build
# Build Gokapi binary
build:
- @echo "Generating code..."
- @echo
- go generate ./...
@echo "Building binary..."
@echo
+ go generate ./...
CGO_ENABLED=0 go build $(BUILD_FLAGS) -o $(OUTPUT_BIN) $(GOPACKAGE)/cmd/gokapi
coverage:
@echo Generating coverage
@echo
- go test ./... -parallel 8 --tags=test,awsmock -coverprofile=/tmp/coverage1.out && go tool cover -html=/tmp/coverage1.out
+ GOKAPI_AWS_BUCKET="gokapi" GOKAPI_AWS_REGION="eu-central-1" GOKAPI_AWS_KEY="keyid" GOKAPI_AWS_KEY_SECRET="secret" go test ./... -parallel 8 --tags=test,awstest -coverprofile=/tmp/coverage1.out && go tool cover -html=/tmp/coverage1.out
coverage-specific:
@echo Generating coverage for "$(TEST_PACKAGE)"