From d132930e0eac4eedbef4a4c2b8e4172d916fe4b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1igo=20Ortega?= Date: Fri, 7 Aug 2026 14:46:13 +0200 Subject: [PATCH 1/2] feat: add browser-side zip grouping for uploads Compress multiple files into a single zip archive in the browser (via JSZip) before upload. Preserves folder structure and shows compression progress and resulting size. Enabled by default; disabled for end-to-end-encrypted uploads. Naming: single-folder uploads use the folder name plus a timestamp; otherwise an editable template with strftime-style date tokens is used, falling back to GOKAPI_DEFAULT_ZIP_NAME when the field is left empty. A single loose file is never zipped and uploads as-is. Add GOKAPI_DEFAULT_ZIP_NAME env var (default gokapi-upload-%Y%m%d-%H%M%S). --- .env.dist | 2 + internal/environment/Environment.go | 4 + internal/webserver/Webserver.go | 2 + .../web/static/assets/dist/js/jszip.min.js | 14 + internal/webserver/web/static/css/cover.css | 11 + .../static/css/min/gokapi.min.ccb62a8d5b.css | 2 +- .../web/static/js/admin_ui_upload.js | 6 + .../webserver/web/static/js/admin_ui_zip.js | 459 ++++++++++++++++++ .../web/static/js/min/admin.min.ccb62a8d5b.js | 8 +- .../webserver/web/templates/html_admin.tmpl | 30 ++ .../webserver/web/templates/html_header.tmpl | 1 + 11 files changed, 534 insertions(+), 5 deletions(-) create mode 100644 internal/webserver/web/static/assets/dist/js/jszip.min.js create mode 100644 internal/webserver/web/static/js/admin_ui_zip.js diff --git a/.env.dist b/.env.dist index aa703e89..abd97a92 100644 --- a/.env.dist +++ b/.env.dist @@ -26,6 +26,8 @@ TZ=UTC #GOKAPI_MAX_FILESIZE=102400 #GOKAPI_MAX_MEMORY_UPLOAD=50 #GOKAPI_MAX_PARALLEL_UPLOADS=3 +# Default name template for browser-side zip archives (supports date tokens like %Y %m %d %H %M %S) +#GOKAPI_DEFAULT_ZIP_NAME=gokapi-upload-%Y%m%d-%H%M%S #TMPDIR=/app/data/tmp diff --git a/internal/environment/Environment.go b/internal/environment/Environment.go index 3371afc4..19b3a15b 100644 --- a/internal/environment/Environment.go +++ b/internal/environment/Environment.go @@ -77,6 +77,10 @@ type Environment struct { UseCloudFlare bool `env:"USE_CLOUDFLARE" envDefault:"false"` // Sets the webserver port WebserverPort int `env:"PORT" envDefault:"53842" onlyPositive:"true" persistent:"true"` + // Sets the default name template used when compressing an upload into a zip + // archive in the browser. Supports strftime-style date tokens, e.g. + // %Y %m %d %H %M %S. The .zip extension is added automatically. + DefaultZipName string `env:"DEFAULT_ZIP_NAME" envDefault:"gokapi-upload-%Y%m%d-%H%M%S"` // Allow hotlinking of videos. Note: Due to buffering, playing a video might count as // multiple downloads. It is only recommended to use video hotlinking for uploads with // unlimited downloads enabled diff --git a/internal/webserver/Webserver.go b/internal/webserver/Webserver.go index 6b707e5b..1c4e0947 100644 --- a/internal/webserver/Webserver.go +++ b/internal/webserver/Webserver.go @@ -761,6 +761,7 @@ type AdminView struct { IsUserTabAvailable bool EndToEndEncryption bool IncludeFilename bool + DefaultZipName string IsInternalAuth bool ShowApiMenu bool ShowDeprecationNotice bool @@ -912,6 +913,7 @@ func (u *AdminView) convertGlobalConfig(view int, user models.User) *AdminView { u.MaxParallelUploads = config.MaxParallelUploads u.ChunkSize = config.ChunkSize u.IncludeFilename = config.IncludeFilename + u.DefaultZipName = configuration.GetEnvironment().DefaultZipName return u } diff --git a/internal/webserver/web/static/assets/dist/js/jszip.min.js b/internal/webserver/web/static/assets/dist/js/jszip.min.js new file mode 100644 index 00000000..f665e0df --- /dev/null +++ b/internal/webserver/web/static/assets/dist/js/jszip.min.js @@ -0,0 +1,14 @@ +/*! JSZip v3.10.1 - https://stuk.github.io/jszip - Dual licensed under MIT or GPLv3 */ +/*! + +JSZip v3.10.1 - A JavaScript class for generating and reading zip files + + +(c) 2009-2016 Stuart Knightley +Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/main/LICENSE.markdown. + +JSZip uses the library pako released under the MIT license : +https://github.com/nodeca/pako/blob/main/LICENSE +*/ + +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).JSZip=e()}}(function(){return function s(a,o,h){function u(r,e){if(!o[r]){if(!a[r]){var t="function"==typeof require&&require;if(!e&&t)return t(r,!0);if(l)return l(r,!0);var n=new Error("Cannot find module '"+r+"'");throw n.code="MODULE_NOT_FOUND",n}var i=o[r]={exports:{}};a[r][0].call(i.exports,function(e){var t=a[r][1][e];return u(t||e)},i,i.exports,s,a,o,h)}return o[r].exports}for(var l="function"==typeof require&&require,e=0;e>2,s=(3&t)<<4|r>>4,a=1>6:64,o=2>4,r=(15&i)<<4|(s=p.indexOf(e.charAt(o++)))>>2,n=(3&s)<<6|(a=p.indexOf(e.charAt(o++))),l[h++]=t,64!==s&&(l[h++]=r),64!==a&&(l[h++]=n);return l}},{"./support":30,"./utils":32}],2:[function(e,t,r){"use strict";var n=e("./external"),i=e("./stream/DataWorker"),s=e("./stream/Crc32Probe"),a=e("./stream/DataLengthProbe");function o(e,t,r,n,i){this.compressedSize=e,this.uncompressedSize=t,this.crc32=r,this.compression=n,this.compressedContent=i}o.prototype={getContentWorker:function(){var e=new i(n.Promise.resolve(this.compressedContent)).pipe(this.compression.uncompressWorker()).pipe(new a("data_length")),t=this;return e.on("end",function(){if(this.streamInfo.data_length!==t.uncompressedSize)throw new Error("Bug : uncompressed data size mismatch")}),e},getCompressedWorker:function(){return new i(n.Promise.resolve(this.compressedContent)).withStreamInfo("compressedSize",this.compressedSize).withStreamInfo("uncompressedSize",this.uncompressedSize).withStreamInfo("crc32",this.crc32).withStreamInfo("compression",this.compression)}},o.createWorkerFrom=function(e,t,r){return e.pipe(new s).pipe(new a("uncompressedSize")).pipe(t.compressWorker(r)).pipe(new a("compressedSize")).withStreamInfo("compression",t)},t.exports=o},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(e,t,r){"use strict";var n=e("./stream/GenericWorker");r.STORE={magic:"\0\0",compressWorker:function(){return new n("STORE compression")},uncompressWorker:function(){return new n("STORE decompression")}},r.DEFLATE=e("./flate")},{"./flate":7,"./stream/GenericWorker":28}],4:[function(e,t,r){"use strict";var n=e("./utils");var o=function(){for(var e,t=[],r=0;r<256;r++){e=r;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[r]=e}return t}();t.exports=function(e,t){return void 0!==e&&e.length?"string"!==n.getTypeOf(e)?function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t[a])];return-1^e}(0|t,e,e.length,0):function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t.charCodeAt(a))];return-1^e}(0|t,e,e.length,0):0}},{"./utils":32}],5:[function(e,t,r){"use strict";r.base64=!1,r.binary=!1,r.dir=!1,r.createFolders=!0,r.date=null,r.compression=null,r.compressionOptions=null,r.comment=null,r.unixPermissions=null,r.dosPermissions=null},{}],6:[function(e,t,r){"use strict";var n=null;n="undefined"!=typeof Promise?Promise:e("lie"),t.exports={Promise:n}},{lie:37}],7:[function(e,t,r){"use strict";var n="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,i=e("pako"),s=e("./utils"),a=e("./stream/GenericWorker"),o=n?"uint8array":"array";function h(e,t){a.call(this,"FlateWorker/"+e),this._pako=null,this._pakoAction=e,this._pakoOptions=t,this.meta={}}r.magic="\b\0",s.inherits(h,a),h.prototype.processChunk=function(e){this.meta=e.meta,null===this._pako&&this._createPako(),this._pako.push(s.transformTo(o,e.data),!1)},h.prototype.flush=function(){a.prototype.flush.call(this),null===this._pako&&this._createPako(),this._pako.push([],!0)},h.prototype.cleanUp=function(){a.prototype.cleanUp.call(this),this._pako=null},h.prototype._createPako=function(){this._pako=new i[this._pakoAction]({raw:!0,level:this._pakoOptions.level||-1});var t=this;this._pako.onData=function(e){t.push({data:e,meta:t.meta})}},r.compressWorker=function(e){return new h("Deflate",e)},r.uncompressWorker=function(){return new h("Inflate",{})}},{"./stream/GenericWorker":28,"./utils":32,pako:38}],8:[function(e,t,r){"use strict";function A(e,t){var r,n="";for(r=0;r>>=8;return n}function n(e,t,r,n,i,s){var a,o,h=e.file,u=e.compression,l=s!==O.utf8encode,f=I.transformTo("string",s(h.name)),c=I.transformTo("string",O.utf8encode(h.name)),d=h.comment,p=I.transformTo("string",s(d)),m=I.transformTo("string",O.utf8encode(d)),_=c.length!==h.name.length,g=m.length!==d.length,b="",v="",y="",w=h.dir,k=h.date,x={crc32:0,compressedSize:0,uncompressedSize:0};t&&!r||(x.crc32=e.crc32,x.compressedSize=e.compressedSize,x.uncompressedSize=e.uncompressedSize);var S=0;t&&(S|=8),l||!_&&!g||(S|=2048);var z=0,C=0;w&&(z|=16),"UNIX"===i?(C=798,z|=function(e,t){var r=e;return e||(r=t?16893:33204),(65535&r)<<16}(h.unixPermissions,w)):(C=20,z|=function(e){return 63&(e||0)}(h.dosPermissions)),a=k.getUTCHours(),a<<=6,a|=k.getUTCMinutes(),a<<=5,a|=k.getUTCSeconds()/2,o=k.getUTCFullYear()-1980,o<<=4,o|=k.getUTCMonth()+1,o<<=5,o|=k.getUTCDate(),_&&(v=A(1,1)+A(B(f),4)+c,b+="up"+A(v.length,2)+v),g&&(y=A(1,1)+A(B(p),4)+m,b+="uc"+A(y.length,2)+y);var E="";return E+="\n\0",E+=A(S,2),E+=u.magic,E+=A(a,2),E+=A(o,2),E+=A(x.crc32,4),E+=A(x.compressedSize,4),E+=A(x.uncompressedSize,4),E+=A(f.length,2),E+=A(b.length,2),{fileRecord:R.LOCAL_FILE_HEADER+E+f+b,dirRecord:R.CENTRAL_FILE_HEADER+A(C,2)+E+A(p.length,2)+"\0\0\0\0"+A(z,4)+A(n,4)+f+b+p}}var I=e("../utils"),i=e("../stream/GenericWorker"),O=e("../utf8"),B=e("../crc32"),R=e("../signature");function s(e,t,r,n){i.call(this,"ZipFileWorker"),this.bytesWritten=0,this.zipComment=t,this.zipPlatform=r,this.encodeFileName=n,this.streamFiles=e,this.accumulate=!1,this.contentBuffer=[],this.dirRecords=[],this.currentSourceOffset=0,this.entriesCount=0,this.currentFile=null,this._sources=[]}I.inherits(s,i),s.prototype.push=function(e){var t=e.meta.percent||0,r=this.entriesCount,n=this._sources.length;this.accumulate?this.contentBuffer.push(e):(this.bytesWritten+=e.data.length,i.prototype.push.call(this,{data:e.data,meta:{currentFile:this.currentFile,percent:r?(t+100*(r-n-1))/r:100}}))},s.prototype.openedSource=function(e){this.currentSourceOffset=this.bytesWritten,this.currentFile=e.file.name;var t=this.streamFiles&&!e.file.dir;if(t){var r=n(e,t,!1,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);this.push({data:r.fileRecord,meta:{percent:0}})}else this.accumulate=!0},s.prototype.closedSource=function(e){this.accumulate=!1;var t=this.streamFiles&&!e.file.dir,r=n(e,t,!0,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);if(this.dirRecords.push(r.dirRecord),t)this.push({data:function(e){return R.DATA_DESCRIPTOR+A(e.crc32,4)+A(e.compressedSize,4)+A(e.uncompressedSize,4)}(e),meta:{percent:100}});else for(this.push({data:r.fileRecord,meta:{percent:0}});this.contentBuffer.length;)this.push(this.contentBuffer.shift());this.currentFile=null},s.prototype.flush=function(){for(var e=this.bytesWritten,t=0;t=this.index;t--)r=(r<<8)+this.byteAt(t);return this.index+=e,r},readString:function(e){return n.transformTo("string",this.readData(e))},readData:function(){},lastIndexOfSignature:function(){},readAndCheckSignature:function(){},readDate:function(){var e=this.readInt(4);return new Date(Date.UTC(1980+(e>>25&127),(e>>21&15)-1,e>>16&31,e>>11&31,e>>5&63,(31&e)<<1))}},t.exports=i},{"../utils":32}],19:[function(e,t,r){"use strict";var n=e("./Uint8ArrayReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./Uint8ArrayReader":21}],20:[function(e,t,r){"use strict";var n=e("./DataReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.byteAt=function(e){return this.data.charCodeAt(this.zero+e)},i.prototype.lastIndexOfSignature=function(e){return this.data.lastIndexOf(e)-this.zero},i.prototype.readAndCheckSignature=function(e){return e===this.readData(4)},i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./DataReader":18}],21:[function(e,t,r){"use strict";var n=e("./ArrayReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.readData=function(e){if(this.checkOffset(e),0===e)return new Uint8Array(0);var t=this.data.subarray(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./ArrayReader":17}],22:[function(e,t,r){"use strict";var n=e("../utils"),i=e("../support"),s=e("./ArrayReader"),a=e("./StringReader"),o=e("./NodeBufferReader"),h=e("./Uint8ArrayReader");t.exports=function(e){var t=n.getTypeOf(e);return n.checkSupport(t),"string"!==t||i.uint8array?"nodebuffer"===t?new o(e):i.uint8array?new h(n.transformTo("uint8array",e)):new s(n.transformTo("array",e)):new a(e)}},{"../support":30,"../utils":32,"./ArrayReader":17,"./NodeBufferReader":19,"./StringReader":20,"./Uint8ArrayReader":21}],23:[function(e,t,r){"use strict";r.LOCAL_FILE_HEADER="PK",r.CENTRAL_FILE_HEADER="PK",r.CENTRAL_DIRECTORY_END="PK",r.ZIP64_CENTRAL_DIRECTORY_LOCATOR="PK",r.ZIP64_CENTRAL_DIRECTORY_END="PK",r.DATA_DESCRIPTOR="PK\b"},{}],24:[function(e,t,r){"use strict";var n=e("./GenericWorker"),i=e("../utils");function s(e){n.call(this,"ConvertWorker to "+e),this.destType=e}i.inherits(s,n),s.prototype.processChunk=function(e){this.push({data:i.transformTo(this.destType,e.data),meta:e.meta})},t.exports=s},{"../utils":32,"./GenericWorker":28}],25:[function(e,t,r){"use strict";var n=e("./GenericWorker"),i=e("../crc32");function s(){n.call(this,"Crc32Probe"),this.withStreamInfo("crc32",0)}e("../utils").inherits(s,n),s.prototype.processChunk=function(e){this.streamInfo.crc32=i(e.data,this.streamInfo.crc32||0),this.push(e)},t.exports=s},{"../crc32":4,"../utils":32,"./GenericWorker":28}],26:[function(e,t,r){"use strict";var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataLengthProbe for "+e),this.propName=e,this.withStreamInfo(e,0)}n.inherits(s,i),s.prototype.processChunk=function(e){if(e){var t=this.streamInfo[this.propName]||0;this.streamInfo[this.propName]=t+e.data.length}i.prototype.processChunk.call(this,e)},t.exports=s},{"../utils":32,"./GenericWorker":28}],27:[function(e,t,r){"use strict";var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataWorker");var t=this;this.dataIsReady=!1,this.index=0,this.max=0,this.data=null,this.type="",this._tickScheduled=!1,e.then(function(e){t.dataIsReady=!0,t.data=e,t.max=e&&e.length||0,t.type=n.getTypeOf(e),t.isPaused||t._tickAndRepeat()},function(e){t.error(e)})}n.inherits(s,i),s.prototype.cleanUp=function(){i.prototype.cleanUp.call(this),this.data=null},s.prototype.resume=function(){return!!i.prototype.resume.call(this)&&(!this._tickScheduled&&this.dataIsReady&&(this._tickScheduled=!0,n.delay(this._tickAndRepeat,[],this)),!0)},s.prototype._tickAndRepeat=function(){this._tickScheduled=!1,this.isPaused||this.isFinished||(this._tick(),this.isFinished||(n.delay(this._tickAndRepeat,[],this),this._tickScheduled=!0))},s.prototype._tick=function(){if(this.isPaused||this.isFinished)return!1;var e=null,t=Math.min(this.max,this.index+16384);if(this.index>=this.max)return this.end();switch(this.type){case"string":e=this.data.substring(this.index,t);break;case"uint8array":e=this.data.subarray(this.index,t);break;case"array":case"nodebuffer":e=this.data.slice(this.index,t)}return this.index=t,this.push({data:e,meta:{percent:this.max?this.index/this.max*100:0}})},t.exports=s},{"../utils":32,"./GenericWorker":28}],28:[function(e,t,r){"use strict";function n(e){this.name=e||"default",this.streamInfo={},this.generatedError=null,this.extraStreamInfo={},this.isPaused=!0,this.isFinished=!1,this.isLocked=!1,this._listeners={data:[],end:[],error:[]},this.previous=null}n.prototype={push:function(e){this.emit("data",e)},end:function(){if(this.isFinished)return!1;this.flush();try{this.emit("end"),this.cleanUp(),this.isFinished=!0}catch(e){this.emit("error",e)}return!0},error:function(e){return!this.isFinished&&(this.isPaused?this.generatedError=e:(this.isFinished=!0,this.emit("error",e),this.previous&&this.previous.error(e),this.cleanUp()),!0)},on:function(e,t){return this._listeners[e].push(t),this},cleanUp:function(){this.streamInfo=this.generatedError=this.extraStreamInfo=null,this._listeners=[]},emit:function(e,t){if(this._listeners[e])for(var r=0;r "+e:e}},t.exports=n},{}],29:[function(e,t,r){"use strict";var h=e("../utils"),i=e("./ConvertWorker"),s=e("./GenericWorker"),u=e("../base64"),n=e("../support"),a=e("../external"),o=null;if(n.nodestream)try{o=e("../nodejs/NodejsStreamOutputAdapter")}catch(e){}function l(e,o){return new a.Promise(function(t,r){var n=[],i=e._internalType,s=e._outputType,a=e._mimeType;e.on("data",function(e,t){n.push(e),o&&o(t)}).on("error",function(e){n=[],r(e)}).on("end",function(){try{var e=function(e,t,r){switch(e){case"blob":return h.newBlob(h.transformTo("arraybuffer",t),r);case"base64":return u.encode(t);default:return h.transformTo(e,t)}}(s,function(e,t){var r,n=0,i=null,s=0;for(r=0;r>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t}(e)},s.utf8decode=function(e){return h.nodebuffer?o.transformTo("nodebuffer",e).toString("utf-8"):function(e){var t,r,n,i,s=e.length,a=new Array(2*s);for(t=r=0;t>10&1023,a[r++]=56320|1023&n)}return a.length!==r&&(a.subarray?a=a.subarray(0,r):a.length=r),o.applyFromCharCode(a)}(e=o.transformTo(h.uint8array?"uint8array":"array",e))},o.inherits(a,n),a.prototype.processChunk=function(e){var t=o.transformTo(h.uint8array?"uint8array":"array",e.data);if(this.leftOver&&this.leftOver.length){if(h.uint8array){var r=t;(t=new Uint8Array(r.length+this.leftOver.length)).set(this.leftOver,0),t.set(r,this.leftOver.length)}else t=this.leftOver.concat(t);this.leftOver=null}var n=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}(t),i=t;n!==t.length&&(h.uint8array?(i=t.subarray(0,n),this.leftOver=t.subarray(n,t.length)):(i=t.slice(0,n),this.leftOver=t.slice(n,t.length))),this.push({data:s.utf8decode(i),meta:e.meta})},a.prototype.flush=function(){this.leftOver&&this.leftOver.length&&(this.push({data:s.utf8decode(this.leftOver),meta:{}}),this.leftOver=null)},s.Utf8DecodeWorker=a,o.inherits(l,n),l.prototype.processChunk=function(e){this.push({data:s.utf8encode(e.data),meta:e.meta})},s.Utf8EncodeWorker=l},{"./nodejsUtils":14,"./stream/GenericWorker":28,"./support":30,"./utils":32}],32:[function(e,t,a){"use strict";var o=e("./support"),h=e("./base64"),r=e("./nodejsUtils"),u=e("./external");function n(e){return e}function l(e,t){for(var r=0;r>8;this.dir=!!(16&this.externalFileAttributes),0==e&&(this.dosPermissions=63&this.externalFileAttributes),3==e&&(this.unixPermissions=this.externalFileAttributes>>16&65535),this.dir||"/"!==this.fileNameStr.slice(-1)||(this.dir=!0)},parseZIP64ExtraField:function(){if(this.extraFields[1]){var e=n(this.extraFields[1].value);this.uncompressedSize===s.MAX_VALUE_32BITS&&(this.uncompressedSize=e.readInt(8)),this.compressedSize===s.MAX_VALUE_32BITS&&(this.compressedSize=e.readInt(8)),this.localHeaderOffset===s.MAX_VALUE_32BITS&&(this.localHeaderOffset=e.readInt(8)),this.diskNumberStart===s.MAX_VALUE_32BITS&&(this.diskNumberStart=e.readInt(4))}},readExtraFields:function(e){var t,r,n,i=e.index+this.extraFieldsLength;for(this.extraFields||(this.extraFields={});e.index+4>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t},r.buf2binstring=function(e){return l(e,e.length)},r.binstring2buf=function(e){for(var t=new h.Buf8(e.length),r=0,n=t.length;r>10&1023,o[n++]=56320|1023&i)}return l(o,n)},r.utf8border=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}},{"./common":41}],43:[function(e,t,r){"use strict";t.exports=function(e,t,r,n){for(var i=65535&e|0,s=e>>>16&65535|0,a=0;0!==r;){for(r-=a=2e3>>1:e>>>1;t[r]=e}return t}();t.exports=function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t[a])];return-1^e}},{}],46:[function(e,t,r){"use strict";var h,c=e("../utils/common"),u=e("./trees"),d=e("./adler32"),p=e("./crc32"),n=e("./messages"),l=0,f=4,m=0,_=-2,g=-1,b=4,i=2,v=8,y=9,s=286,a=30,o=19,w=2*s+1,k=15,x=3,S=258,z=S+x+1,C=42,E=113,A=1,I=2,O=3,B=4;function R(e,t){return e.msg=n[t],t}function T(e){return(e<<1)-(4e.avail_out&&(r=e.avail_out),0!==r&&(c.arraySet(e.output,t.pending_buf,t.pending_out,r,e.next_out),e.next_out+=r,t.pending_out+=r,e.total_out+=r,e.avail_out-=r,t.pending-=r,0===t.pending&&(t.pending_out=0))}function N(e,t){u._tr_flush_block(e,0<=e.block_start?e.block_start:-1,e.strstart-e.block_start,t),e.block_start=e.strstart,F(e.strm)}function U(e,t){e.pending_buf[e.pending++]=t}function P(e,t){e.pending_buf[e.pending++]=t>>>8&255,e.pending_buf[e.pending++]=255&t}function L(e,t){var r,n,i=e.max_chain_length,s=e.strstart,a=e.prev_length,o=e.nice_match,h=e.strstart>e.w_size-z?e.strstart-(e.w_size-z):0,u=e.window,l=e.w_mask,f=e.prev,c=e.strstart+S,d=u[s+a-1],p=u[s+a];e.prev_length>=e.good_match&&(i>>=2),o>e.lookahead&&(o=e.lookahead);do{if(u[(r=t)+a]===p&&u[r+a-1]===d&&u[r]===u[s]&&u[++r]===u[s+1]){s+=2,r++;do{}while(u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&sh&&0!=--i);return a<=e.lookahead?a:e.lookahead}function j(e){var t,r,n,i,s,a,o,h,u,l,f=e.w_size;do{if(i=e.window_size-e.lookahead-e.strstart,e.strstart>=f+(f-z)){for(c.arraySet(e.window,e.window,f,f,0),e.match_start-=f,e.strstart-=f,e.block_start-=f,t=r=e.hash_size;n=e.head[--t],e.head[t]=f<=n?n-f:0,--r;);for(t=r=f;n=e.prev[--t],e.prev[t]=f<=n?n-f:0,--r;);i+=f}if(0===e.strm.avail_in)break;if(a=e.strm,o=e.window,h=e.strstart+e.lookahead,u=i,l=void 0,l=a.avail_in,u=x)for(s=e.strstart-e.insert,e.ins_h=e.window[s],e.ins_h=(e.ins_h<=x&&(e.ins_h=(e.ins_h<=x)if(n=u._tr_tally(e,e.strstart-e.match_start,e.match_length-x),e.lookahead-=e.match_length,e.match_length<=e.max_lazy_match&&e.lookahead>=x){for(e.match_length--;e.strstart++,e.ins_h=(e.ins_h<=x&&(e.ins_h=(e.ins_h<=x&&e.match_length<=e.prev_length){for(i=e.strstart+e.lookahead-x,n=u._tr_tally(e,e.strstart-1-e.prev_match,e.prev_length-x),e.lookahead-=e.prev_length-1,e.prev_length-=2;++e.strstart<=i&&(e.ins_h=(e.ins_h<e.pending_buf_size-5&&(r=e.pending_buf_size-5);;){if(e.lookahead<=1){if(j(e),0===e.lookahead&&t===l)return A;if(0===e.lookahead)break}e.strstart+=e.lookahead,e.lookahead=0;var n=e.block_start+r;if((0===e.strstart||e.strstart>=n)&&(e.lookahead=e.strstart-n,e.strstart=n,N(e,!1),0===e.strm.avail_out))return A;if(e.strstart-e.block_start>=e.w_size-z&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):(e.strstart>e.block_start&&(N(e,!1),e.strm.avail_out),A)}),new M(4,4,8,4,Z),new M(4,5,16,8,Z),new M(4,6,32,32,Z),new M(4,4,16,16,W),new M(8,16,32,32,W),new M(8,16,128,128,W),new M(8,32,128,256,W),new M(32,128,258,1024,W),new M(32,258,258,4096,W)],r.deflateInit=function(e,t){return Y(e,t,v,15,8,0)},r.deflateInit2=Y,r.deflateReset=K,r.deflateResetKeep=G,r.deflateSetHeader=function(e,t){return e&&e.state?2!==e.state.wrap?_:(e.state.gzhead=t,m):_},r.deflate=function(e,t){var r,n,i,s;if(!e||!e.state||5>8&255),U(n,n.gzhead.time>>16&255),U(n,n.gzhead.time>>24&255),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,255&n.gzhead.os),n.gzhead.extra&&n.gzhead.extra.length&&(U(n,255&n.gzhead.extra.length),U(n,n.gzhead.extra.length>>8&255)),n.gzhead.hcrc&&(e.adler=p(e.adler,n.pending_buf,n.pending,0)),n.gzindex=0,n.status=69):(U(n,0),U(n,0),U(n,0),U(n,0),U(n,0),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,3),n.status=E);else{var a=v+(n.w_bits-8<<4)<<8;a|=(2<=n.strategy||n.level<2?0:n.level<6?1:6===n.level?2:3)<<6,0!==n.strstart&&(a|=32),a+=31-a%31,n.status=E,P(n,a),0!==n.strstart&&(P(n,e.adler>>>16),P(n,65535&e.adler)),e.adler=1}if(69===n.status)if(n.gzhead.extra){for(i=n.pending;n.gzindex<(65535&n.gzhead.extra.length)&&(n.pending!==n.pending_buf_size||(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending!==n.pending_buf_size));)U(n,255&n.gzhead.extra[n.gzindex]),n.gzindex++;n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),n.gzindex===n.gzhead.extra.length&&(n.gzindex=0,n.status=73)}else n.status=73;if(73===n.status)if(n.gzhead.name){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindexi&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.gzindex=0,n.status=91)}else n.status=91;if(91===n.status)if(n.gzhead.comment){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindexi&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.status=103)}else n.status=103;if(103===n.status&&(n.gzhead.hcrc?(n.pending+2>n.pending_buf_size&&F(e),n.pending+2<=n.pending_buf_size&&(U(n,255&e.adler),U(n,e.adler>>8&255),e.adler=0,n.status=E)):n.status=E),0!==n.pending){if(F(e),0===e.avail_out)return n.last_flush=-1,m}else if(0===e.avail_in&&T(t)<=T(r)&&t!==f)return R(e,-5);if(666===n.status&&0!==e.avail_in)return R(e,-5);if(0!==e.avail_in||0!==n.lookahead||t!==l&&666!==n.status){var o=2===n.strategy?function(e,t){for(var r;;){if(0===e.lookahead&&(j(e),0===e.lookahead)){if(t===l)return A;break}if(e.match_length=0,r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++,r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):3===n.strategy?function(e,t){for(var r,n,i,s,a=e.window;;){if(e.lookahead<=S){if(j(e),e.lookahead<=S&&t===l)return A;if(0===e.lookahead)break}if(e.match_length=0,e.lookahead>=x&&0e.lookahead&&(e.match_length=e.lookahead)}if(e.match_length>=x?(r=u._tr_tally(e,1,e.match_length-x),e.lookahead-=e.match_length,e.strstart+=e.match_length,e.match_length=0):(r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++),r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):h[n.level].func(n,t);if(o!==O&&o!==B||(n.status=666),o===A||o===O)return 0===e.avail_out&&(n.last_flush=-1),m;if(o===I&&(1===t?u._tr_align(n):5!==t&&(u._tr_stored_block(n,0,0,!1),3===t&&(D(n.head),0===n.lookahead&&(n.strstart=0,n.block_start=0,n.insert=0))),F(e),0===e.avail_out))return n.last_flush=-1,m}return t!==f?m:n.wrap<=0?1:(2===n.wrap?(U(n,255&e.adler),U(n,e.adler>>8&255),U(n,e.adler>>16&255),U(n,e.adler>>24&255),U(n,255&e.total_in),U(n,e.total_in>>8&255),U(n,e.total_in>>16&255),U(n,e.total_in>>24&255)):(P(n,e.adler>>>16),P(n,65535&e.adler)),F(e),0=r.w_size&&(0===s&&(D(r.head),r.strstart=0,r.block_start=0,r.insert=0),u=new c.Buf8(r.w_size),c.arraySet(u,t,l-r.w_size,r.w_size,0),t=u,l=r.w_size),a=e.avail_in,o=e.next_in,h=e.input,e.avail_in=l,e.next_in=0,e.input=t,j(r);r.lookahead>=x;){for(n=r.strstart,i=r.lookahead-(x-1);r.ins_h=(r.ins_h<>>=y=v>>>24,p-=y,0===(y=v>>>16&255))C[s++]=65535&v;else{if(!(16&y)){if(0==(64&y)){v=m[(65535&v)+(d&(1<>>=y,p-=y),p<15&&(d+=z[n++]<>>=y=v>>>24,p-=y,!(16&(y=v>>>16&255))){if(0==(64&y)){v=_[(65535&v)+(d&(1<>>=y,p-=y,(y=s-a)>3,d&=(1<<(p-=w<<3))-1,e.next_in=n,e.next_out=s,e.avail_in=n>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24)}function s(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new I.Buf16(320),this.work=new I.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function a(e){var t;return e&&e.state?(t=e.state,e.total_in=e.total_out=t.total=0,e.msg="",t.wrap&&(e.adler=1&t.wrap),t.mode=P,t.last=0,t.havedict=0,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new I.Buf32(n),t.distcode=t.distdyn=new I.Buf32(i),t.sane=1,t.back=-1,N):U}function o(e){var t;return e&&e.state?((t=e.state).wsize=0,t.whave=0,t.wnext=0,a(e)):U}function h(e,t){var r,n;return e&&e.state?(n=e.state,t<0?(r=0,t=-t):(r=1+(t>>4),t<48&&(t&=15)),t&&(t<8||15=s.wsize?(I.arraySet(s.window,t,r-s.wsize,s.wsize,0),s.wnext=0,s.whave=s.wsize):(n<(i=s.wsize-s.wnext)&&(i=n),I.arraySet(s.window,t,r-n,i,s.wnext),(n-=i)?(I.arraySet(s.window,t,r-n,n,0),s.wnext=n,s.whave=s.wsize):(s.wnext+=i,s.wnext===s.wsize&&(s.wnext=0),s.whave>>8&255,r.check=B(r.check,E,2,0),l=u=0,r.mode=2;break}if(r.flags=0,r.head&&(r.head.done=!1),!(1&r.wrap)||(((255&u)<<8)+(u>>8))%31){e.msg="incorrect header check",r.mode=30;break}if(8!=(15&u)){e.msg="unknown compression method",r.mode=30;break}if(l-=4,k=8+(15&(u>>>=4)),0===r.wbits)r.wbits=k;else if(k>r.wbits){e.msg="invalid window size",r.mode=30;break}r.dmax=1<>8&1),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=3;case 3:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>8&255,E[2]=u>>>16&255,E[3]=u>>>24&255,r.check=B(r.check,E,4,0)),l=u=0,r.mode=4;case 4:for(;l<16;){if(0===o)break e;o--,u+=n[s++]<>8),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=5;case 5:if(1024&r.flags){for(;l<16;){if(0===o)break e;o--,u+=n[s++]<>>8&255,r.check=B(r.check,E,2,0)),l=u=0}else r.head&&(r.head.extra=null);r.mode=6;case 6:if(1024&r.flags&&(o<(d=r.length)&&(d=o),d&&(r.head&&(k=r.head.extra_len-r.length,r.head.extra||(r.head.extra=new Array(r.head.extra_len)),I.arraySet(r.head.extra,n,s,d,k)),512&r.flags&&(r.check=B(r.check,n,d,s)),o-=d,s+=d,r.length-=d),r.length))break e;r.length=0,r.mode=7;case 7:if(2048&r.flags){if(0===o)break e;for(d=0;k=n[s+d++],r.head&&k&&r.length<65536&&(r.head.name+=String.fromCharCode(k)),k&&d>9&1,r.head.done=!0),e.adler=r.check=0,r.mode=12;break;case 10:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>=7&l,l-=7&l,r.mode=27;break}for(;l<3;){if(0===o)break e;o--,u+=n[s++]<>>=1)){case 0:r.mode=14;break;case 1:if(j(r),r.mode=20,6!==t)break;u>>>=2,l-=2;break e;case 2:r.mode=17;break;case 3:e.msg="invalid block type",r.mode=30}u>>>=2,l-=2;break;case 14:for(u>>>=7&l,l-=7&l;l<32;){if(0===o)break e;o--,u+=n[s++]<>>16^65535)){e.msg="invalid stored block lengths",r.mode=30;break}if(r.length=65535&u,l=u=0,r.mode=15,6===t)break e;case 15:r.mode=16;case 16:if(d=r.length){if(o>>=5,l-=5,r.ndist=1+(31&u),u>>>=5,l-=5,r.ncode=4+(15&u),u>>>=4,l-=4,286>>=3,l-=3}for(;r.have<19;)r.lens[A[r.have++]]=0;if(r.lencode=r.lendyn,r.lenbits=7,S={bits:r.lenbits},x=T(0,r.lens,0,19,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg="invalid code lengths set",r.mode=30;break}r.have=0,r.mode=19;case 19:for(;r.have>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=_,l-=_,r.lens[r.have++]=b;else{if(16===b){for(z=_+2;l>>=_,l-=_,0===r.have){e.msg="invalid bit length repeat",r.mode=30;break}k=r.lens[r.have-1],d=3+(3&u),u>>>=2,l-=2}else if(17===b){for(z=_+3;l>>=_)),u>>>=3,l-=3}else{for(z=_+7;l>>=_)),u>>>=7,l-=7}if(r.have+d>r.nlen+r.ndist){e.msg="invalid bit length repeat",r.mode=30;break}for(;d--;)r.lens[r.have++]=k}}if(30===r.mode)break;if(0===r.lens[256]){e.msg="invalid code -- missing end-of-block",r.mode=30;break}if(r.lenbits=9,S={bits:r.lenbits},x=T(D,r.lens,0,r.nlen,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg="invalid literal/lengths set",r.mode=30;break}if(r.distbits=6,r.distcode=r.distdyn,S={bits:r.distbits},x=T(F,r.lens,r.nlen,r.ndist,r.distcode,0,r.work,S),r.distbits=S.bits,x){e.msg="invalid distances set",r.mode=30;break}if(r.mode=20,6===t)break e;case 20:r.mode=21;case 21:if(6<=o&&258<=h){e.next_out=a,e.avail_out=h,e.next_in=s,e.avail_in=o,r.hold=u,r.bits=l,R(e,c),a=e.next_out,i=e.output,h=e.avail_out,s=e.next_in,n=e.input,o=e.avail_in,u=r.hold,l=r.bits,12===r.mode&&(r.back=-1);break}for(r.back=0;g=(C=r.lencode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,r.length=b,0===g){r.mode=26;break}if(32&g){r.back=-1,r.mode=12;break}if(64&g){e.msg="invalid literal/length code",r.mode=30;break}r.extra=15&g,r.mode=22;case 22:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra}r.was=r.length,r.mode=23;case 23:for(;g=(C=r.distcode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,64&g){e.msg="invalid distance code",r.mode=30;break}r.offset=b,r.extra=15&g,r.mode=24;case 24:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra}if(r.offset>r.dmax){e.msg="invalid distance too far back",r.mode=30;break}r.mode=25;case 25:if(0===h)break e;if(d=c-h,r.offset>d){if((d=r.offset-d)>r.whave&&r.sane){e.msg="invalid distance too far back",r.mode=30;break}p=d>r.wnext?(d-=r.wnext,r.wsize-d):r.wnext-d,d>r.length&&(d=r.length),m=r.window}else m=i,p=a-r.offset,d=r.length;for(hd?(m=R[T+a[v]],A[I+a[v]]):(m=96,0),h=1<>S)+(u-=h)]=p<<24|m<<16|_|0,0!==u;);for(h=1<>=1;if(0!==h?(E&=h-1,E+=h):E=0,v++,0==--O[b]){if(b===w)break;b=t[r+a[v]]}if(k>>7)]}function U(e,t){e.pending_buf[e.pending++]=255&t,e.pending_buf[e.pending++]=t>>>8&255}function P(e,t,r){e.bi_valid>d-r?(e.bi_buf|=t<>d-e.bi_valid,e.bi_valid+=r-d):(e.bi_buf|=t<>>=1,r<<=1,0<--t;);return r>>>1}function Z(e,t,r){var n,i,s=new Array(g+1),a=0;for(n=1;n<=g;n++)s[n]=a=a+r[n-1]<<1;for(i=0;i<=t;i++){var o=e[2*i+1];0!==o&&(e[2*i]=j(s[o]++,o))}}function W(e){var t;for(t=0;t>1;1<=r;r--)G(e,s,r);for(i=h;r=e.heap[1],e.heap[1]=e.heap[e.heap_len--],G(e,s,1),n=e.heap[1],e.heap[--e.heap_max]=r,e.heap[--e.heap_max]=n,s[2*i]=s[2*r]+s[2*n],e.depth[i]=(e.depth[r]>=e.depth[n]?e.depth[r]:e.depth[n])+1,s[2*r+1]=s[2*n+1]=i,e.heap[1]=i++,G(e,s,1),2<=e.heap_len;);e.heap[--e.heap_max]=e.heap[1],function(e,t){var r,n,i,s,a,o,h=t.dyn_tree,u=t.max_code,l=t.stat_desc.static_tree,f=t.stat_desc.has_stree,c=t.stat_desc.extra_bits,d=t.stat_desc.extra_base,p=t.stat_desc.max_length,m=0;for(s=0;s<=g;s++)e.bl_count[s]=0;for(h[2*e.heap[e.heap_max]+1]=0,r=e.heap_max+1;r<_;r++)p<(s=h[2*h[2*(n=e.heap[r])+1]+1]+1)&&(s=p,m++),h[2*n+1]=s,u>=7;n>>=1)if(1&r&&0!==e.dyn_ltree[2*t])return o;if(0!==e.dyn_ltree[18]||0!==e.dyn_ltree[20]||0!==e.dyn_ltree[26])return h;for(t=32;t>>3,(s=e.static_len+3+7>>>3)<=i&&(i=s)):i=s=r+5,r+4<=i&&-1!==t?J(e,t,r,n):4===e.strategy||s===i?(P(e,2+(n?1:0),3),K(e,z,C)):(P(e,4+(n?1:0),3),function(e,t,r,n){var i;for(P(e,t-257,5),P(e,r-1,5),P(e,n-4,4),i=0;i>>8&255,e.pending_buf[e.d_buf+2*e.last_lit+1]=255&t,e.pending_buf[e.l_buf+e.last_lit]=255&r,e.last_lit++,0===t?e.dyn_ltree[2*r]++:(e.matches++,t--,e.dyn_ltree[2*(A[r]+u+1)]++,e.dyn_dtree[2*N(t)]++),e.last_lit===e.lit_bufsize-1},r._tr_align=function(e){P(e,2,3),L(e,m,z),function(e){16===e.bi_valid?(U(e,e.bi_buf),e.bi_buf=0,e.bi_valid=0):8<=e.bi_valid&&(e.pending_buf[e.pending++]=255&e.bi_buf,e.bi_buf>>=8,e.bi_valid-=8)}(e)}},{"../utils/common":41}],53:[function(e,t,r){"use strict";t.exports=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}},{}],54:[function(e,t,r){(function(e){!function(r,n){"use strict";if(!r.setImmediate){var i,s,t,a,o=1,h={},u=!1,l=r.document,e=Object.getPrototypeOf&&Object.getPrototypeOf(r);e=e&&e.setTimeout?e:r,i="[object process]"==={}.toString.call(r.process)?function(e){process.nextTick(function(){c(e)})}:function(){if(r.postMessage&&!r.importScripts){var e=!0,t=r.onmessage;return r.onmessage=function(){e=!1},r.postMessage("","*"),r.onmessage=t,e}}()?(a="setImmediate$"+Math.random()+"$",r.addEventListener?r.addEventListener("message",d,!1):r.attachEvent("onmessage",d),function(e){r.postMessage(a+e,"*")}):r.MessageChannel?((t=new MessageChannel).port1.onmessage=function(e){c(e.data)},function(e){t.port2.postMessage(e)}):l&&"onreadystatechange"in l.createElement("script")?(s=l.documentElement,function(e){var t=l.createElement("script");t.onreadystatechange=function(){c(e),t.onreadystatechange=null,s.removeChild(t),t=null},s.appendChild(t)}):function(e){setTimeout(c,0,e)},e.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),r=0;rtd{background-color:rgba(255,255,255,8%)}.filerequest-item>td{transition:background-color .15s ease-in-out}.collapse-toggle i{display:inline-block;transition:transform .2s ease}.collapse-toggle[aria-expanded=true] i{transform:rotate(180deg)}.collapse-toggle:hover{opacity:.8}.collapse-toggle{padding:.25rem}.remove-entry-btn:hover{opacity:.8}.info-box{background-color:rgba(255,255,255,5%);border-radius:6px;padding:1rem;margin-bottom:1.5rem;text-align:left}.info-box h6{margin-bottom:.5rem}.info-box ul{margin-bottom:0;padding-left:1.2rem}.callout{padding:20px;margin:10px 20px;border:1px solid #eee;border-left-width:5px;border-radius:3px;h4{margin-top:0;margin-bottom:5px}p:last-child{margin-bottom:0}code{border-radius:3px}&+.bs-callout{margin-top:-5px}}.upload-box{background:#212529;border:2px dashed rgba(255,255,255,.2);border-radius:8px;padding:2rem;transition:all .2s ease;cursor:pointer;display:block;transition:background-color .2s ease}.upload-box.highlight,.upload-box.dz-drag-hover{border-color:#0d6efd;background-color:rgba(13,110,253,5%)}.upload-box:hover{background-color:rgba(255,255,255,5%)}.pu-file-list{margin-top:1.5rem;padding:0 1rem}.pu-file-item{display:grid;gap:.5rem;align-items:center;padding:.75rem 0;border-bottom:1px solid rgba(255,255,255,.1);font-size:.95rem;grid-template-columns:1fr auto;grid-template-areas:"name button" "bar bar" "status size"}.pu-file-item .file-name{grid-area:name;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:500;min-width:0;text-align:left}.pu-file-item .upload-status{grid-area:status;font-size:.85rem;opacity:.75;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pu-file-item progress{grid-area:bar;width:100%;height:6px;border-radius:3px;border:none;appearance:none;-webkit-appearance:none;background-color:rgba(255,255,255,.1)}.pu-file-item progress::-webkit-progress-bar{background-color:rgba(255,255,255,.1);border-radius:3px}.pu-file-item progress::-webkit-progress-value{background-color:#0d6efd;border-radius:3px;transition:width .2s ease}.pu-file-item progress::-moz-progress-bar{background-color:#0d6efd;border-radius:3px}.pu-file-item .file-size{grid-area:size;text-align:right;font-size:.85rem;opacity:.75;white-space:nowrap}.pu-file-item button{grid-area:button;padding:.25rem .5rem}@media(min-width:768px){.pu-file-item{grid-template-columns:1fr auto 100px 80px auto;grid-template-areas:"name status bar size button";gap:1rem}.pu-file-item .upload-status{text-align:right;font-size:.95rem;max-width:400px}.pu-file-item .file-size{font-size:.95rem}.pu-file-item progress{height:8px}}.btn-upload-custom{background-color:#0d6efd!important;background-image:none!important;border:none;color:#fff;padding:.8rem 2.5rem;font-weight:600;border-radius:50px;box-shadow:0 4px 12px rgba(0,0,0,.3);transition:all .2s ease-in-out;display:inline-block}.btn-upload-custom:hover:not(:disabled){background-color:#1e7eff!important;transform:translateY(-1px)}.btn-upload-custom:disabled{background-color:#212529!important;color:#6c757d!important;border:1px solid #343a40!important;box-shadow:none;cursor:not-allowed;opacity:1}.stat-card{background:#1a1d20;border:1px solid #2d3238;border-radius:12px;overflow:hidden;transition:transform .2s ease,box-shadow .2s ease}.stat-card:hover{transform:translateY(-3px);box-shadow:0 10px 20px rgba(0,0,0,.3)!important;border-color:#0d6efd}.stat-icon{opacity:.6;font-size:1.2rem}.progress-stat{height:4px;background-color:#2b3035}.log-dark-input{background-color:#2b3035!important;border-color:#444b52!important;color:#e9ecef!important}.log-dark-input:focus{background-color:#32383e!important;border-color:#0d6efd!important;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.input-group-text-dark{background-color:#1a1d20!important;border-color:#444b52!important;color:#adb5bd!important}#logviewer::-webkit-scrollbar{width:8px}#logviewer::-webkit-scrollbar-track{background:#000}#logviewer::-webkit-scrollbar-thumb{background:#333;border-radius:4px}.download-wrapper{min-height:70vh;display:flex;align-items:center;justify-content:center}.file-card{border:1px solid #333;border-radius:1rem;box-shadow:0 10px 30px rgba(0,0,0,.5);max-width:450px;width:100%;background:#1e1e1e;color:#e0e0e0}.icon-container{width:70px;height:70px;background-color:#2d2d2d;border-radius:50%;display:flex;align-items:center;justify-content:center;margin:0 auto 1.5rem;color:#0d6efd}.filename-text{word-wrap:break-word;color:#fff;font-weight:600}.dark-list-item{background-color:#252525!important;border-color:#333!important;color:#b0b0b0!important}.popover{--bs-popover-bg:#212529;--bs-popover-header-bg:#212529;--bs-popover-header-color:#dee2e6;--bs-popover-body-color:#dee2e6;--bs-popover-body-padding-y:0;--bs-popover-border-color:rgba(255, 255, 255, 0.15);--bs-popover-arrow-border:rgba(255, 255, 255, 0.15)}.popover-header{border-bottom-color:rgba(255,255,255,.15)}.upload-options-section-label{font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:#6c757d;margin-bottom:.5rem}.upload-options-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:.5rem;margin-bottom:.75rem}@media(max-width:767px){.upload-options-grid{grid-template-columns:1fr}}.upload-option-card{background-color:#212529;border:1px solid rgba(255,255,255,.1);border-radius:8px;padding:.5rem .75rem;transition:border-color .2s ease,box-shadow .2s ease}.upload-option-card:has(.upload-option-toggle:checked){border-color:rgba(13,110,253,.5);box-shadow:0 0 0 1px rgba(13,110,253,.2)}.upload-option-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:.4rem}.upload-option-icon{font-size:.85rem;color:#6ea8fe;opacity:.9}.upload-option-label{font-size:.72rem;font-weight:600;color:#adb5bd;text-transform:uppercase;letter-spacing:.06em}.upload-option-toggle{cursor:pointer}.upload-option-input-group{margin-top:0}.upload-option-card .form-control{background-color:#2b3035;border-color:rgba(255,255,255,.15);color:#dee2e6;padding:.25rem .5rem;font-size:.875rem}.upload-option-card .form-control:disabled{background-color:#262a2e;color:#555;border-color:rgba(255,255,255,8%)}.upload-option-suffix{background-color:#2b3035;border-color:rgba(255,255,255,.15);color:#adb5bd;font-size:.8rem;padding:.25rem .5rem}.upload-option-card input[type=number]::-webkit-inner-spin-button{filter:invert(1)brightness(.6)}.modal-samesize-input-edit{width:11rem}.modal-samesize-input-filerequest{width:7rem}.filename{font-weight:700;font-size:14px;margin-bottom:5px}.upload-progress-container{display:flex;align-items:center}.upload-progress-bar{position:relative;height:10px;background-color:#eee;flex:1;margin-right:10px;border-radius:4px}.upload-progress-bar-progress{position:absolute;top:0;left:0;height:100%;background-color:#0a0;border-radius:4px;transition:width .2s ease-in-out}.upload-progress-info{font-size:12px}.us-container{margin-top:10px;margin-bottom:20px}.uploaderror{font-weight:700;color:red;margin-bottom:5px}.uploads-container{background-color:#2f343a;border:2px solid rgba(0,0,0,.3);border-radius:5px;margin-left:0;margin-right:0;max-width:none;visibility:hidden} \ No newline at end of file +.btn-secondary,.btn-secondary:hover,.btn-secondary:focus{color:#333;text-shadow:none}body{background:url(../../assets/background.jpg)no-repeat 50% fixed;-webkit-background-size:cover;-moz-background-size:cover;-o-background-size:cover;background-size:cover;display:-ms-flexbox;display:-webkit-box;display:flex;-ms-flex-pack:center;-webkit-box-pack:center;justify-content:center}body::after{content:"";position:fixed;top:0;left:0;width:100%;height:100%;box-shadow:inset 0 0 5rem rgba(0,0,0,.5);pointer-events:none;z-index:10}td{vertical-align:middle;position:relative}a{color:inherit}a:hover{color:inherit;filter:brightness(80%)}.text-muted{color:#adb5bd!important}.card{margin:0 auto;float:none;margin-bottom:10px;border:2px solid #33393f}.card-body{background-color:#212529;color:#ddd}.card-title{font-weight:900}.admin-input{text-align:center}.form-control:disabled{background:#bababa}.break{flex-basis:100%;height:0}.bd-placeholder-img{font-size:1.125rem;text-anchor:middle;-webkit-user-select:none;-moz-user-select:none;user-select:none}@media(min-width:768px){.bd-placeholder-img-lg{font-size:3.5rem}.break{flex-basis:0}}.masthead{margin-bottom:2rem}.masthead-brand{margin-bottom:0}.nav-masthead .nav-link{padding:.25rem 0;font-weight:700;color:rgba(255,255,255,.5);background-color:initial;border-bottom:.25rem solid transparent}.nav-masthead .nav-link:hover,.nav-masthead .nav-link:focus{border-bottom-color:rgba(255,255,255,.25)}.nav-masthead .nav-link+.nav-link{margin-left:1rem}.nav-masthead .active{color:#fff;border-bottom-color:#fff}#qroverlay{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background-color:rgba(0,0,0,.3)}#qrcode{position:absolute;top:50%;left:50%;margin-top:-105px;margin-left:-105px;width:210px;height:210px;border:5px solid #fff}.toastnotification{pointer-events:none;position:fixed;bottom:20px;left:50%;transform:translateX(-50%);background-color:#333;color:#fff;padding:15px;border-radius:5px;box-shadow:0 2px 5px rgba(0,0,0,.3);opacity:0;transition:opacity .3s ease-in-out;z-index:9999}.toastdeprecation{background-color:#8b0000}.toastnotification.show{opacity:1;pointer-events:auto}.toast-undo{margin-left:20px;color:#4fc3f7;cursor:pointer;text-decoration:underline;font-weight:700;pointer-events:auto}.toast-undo:hover{color:#81d4fa}.toastnotification:not(.show){pointer-events:none!important}.toastnotification:not(.show) .toast-undo{pointer-events:none}.perm-granted{cursor:pointer;color:#0edf00}.perm-notgranted{cursor:pointer;color:#9f9999}.perm-unavailable{color:#525252}.perm-processing{pointer-events:none;color:#e5eb00;animation:perm-pulse 1s infinite}.perm-nochange{cursor:default}.perm-granted:not(.perm-nochange):hover{color:#ff4d4d}.perm-notgranted:not(.perm-nochange):hover{color:#4dff4d}.perm-granted:not(.perm-nochange),.perm-notgranted:not(.perm-nochange){transition:color .15s ease,transform .1s ease}@keyframes perm-pulse{0%{opacity:1}50%{opacity:.5}100%{opacity:1}}.perm-nochange:hover{transform:none}.perm-nowgranted{animation:perm-nowgranted-pulse .5s ease forwards}@keyframes perm-nowgranted-pulse{0%{transform:scale(1.15);color:#4dff4d}50%{transform:scale(1.3);color:#080}100%{transform:scale(1.15);color:#0edf00}}.perm-nownotgranted{animation:perm-nownotgranted-pulse .5s ease forwards}@keyframes perm-nownotgranted-pulse{0%{transform:scale(1.15);color:#ff4d4d}50%{transform:scale(1.3);color:red}100%{transform:scale(1.15);color:##9f9999}}.prevent-select{-webkit-user-select:none;-ms-user-select:none;user-select:none}.gokapi-dialog{background-color:#212529;color:#ddd}@keyframes subtleHighlight{0%{background-color:#444950}100%{background-color:initial}}@keyframes subtleHighlightNewJson{0%{background-color:green}100%{background-color:initial}}.updatedDownloadCount{animation:subtleHighlight .5s ease-out}.newFileRequest{animation:subtleHighlightNewJson .7s ease-out}.newApiKey{animation:subtleHighlightNewJson .7s ease-out}.newUser{animation:subtleHighlightNewJson .7s ease-out}.newItem{animation:subtleHighlightNewJson 1.5s ease-out}@keyframes fadeOut{0%{opacity:1}100%{opacity:0}}.rowDeleting{animation:fadeOut .3s ease-out forwards}.highlighted-password{background-color:#444;color:#ddd;padding:2px 6px;border-radius:4px;font-weight:700;font-family:monospace;display:inline-block;margin-left:8px;border:1px solid #555}.filelist-item{background-color:rgba(255,255,255,4%)}.filelist-item:hover{background-color:rgba(255,255,255,8%)}tr.no-bottom-border td{border-bottom:none}.filerequest-item:hover>td{background-color:rgba(255,255,255,8%)}.filerequest-item>td{transition:background-color .15s ease-in-out}.collapse-toggle i{display:inline-block;transition:transform .2s ease}.collapse-toggle[aria-expanded=true] i{transform:rotate(180deg)}.collapse-toggle:hover{opacity:.8}.collapse-toggle{padding:.25rem}.remove-entry-btn:hover{opacity:.8}.info-box{background-color:rgba(255,255,255,5%);border-radius:6px;padding:1rem;margin-bottom:1.5rem;text-align:left}.info-box h6{margin-bottom:.5rem}.info-box ul{margin-bottom:0;padding-left:1.2rem}.callout{padding:20px;margin:10px 20px;border:1px solid #eee;border-left-width:5px;border-radius:3px;h4 { margin-top: 0; margin-bottom: 5px; } p:last-child { margin-bottom: 0; } code { border-radius: 3px; } &+.bs-callout { margin-top: -5px; }}.upload-box{background:#212529;border:2px dashed rgba(255,255,255,.2);border-radius:8px;padding:2rem;transition:all .2s ease;cursor:pointer;display:block;transition:background-color .2s ease}.upload-box.highlight,.upload-box.dz-drag-hover{border-color:#0d6efd;background-color:rgba(13,110,253,5%)}.upload-box:hover{background-color:rgba(255,255,255,5%)}.pu-file-list{margin-top:1.5rem;padding:0 1rem}.pu-file-item{display:grid;gap:.5rem;align-items:center;padding:.75rem 0;border-bottom:1px solid rgba(255,255,255,.1);font-size:.95rem;grid-template-columns:1fr auto;grid-template-areas:"name button" "bar bar" "status size"}.pu-file-item .file-name{grid-area:name;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:500;min-width:0;text-align:left}.pu-file-item .upload-status{grid-area:status;font-size:.85rem;opacity:.75;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pu-file-item progress{grid-area:bar;width:100%;height:6px;border-radius:3px;border:none;appearance:none;-webkit-appearance:none;background-color:rgba(255,255,255,.1)}.pu-file-item progress::-webkit-progress-bar{background-color:rgba(255,255,255,.1);border-radius:3px}.pu-file-item progress::-webkit-progress-value{background-color:#0d6efd;border-radius:3px;transition:width .2s ease}.pu-file-item progress::-moz-progress-bar{background-color:#0d6efd;border-radius:3px}.pu-file-item .file-size{grid-area:size;text-align:right;font-size:.85rem;opacity:.75;white-space:nowrap}.pu-file-item button{grid-area:button;padding:.25rem .5rem}@media(min-width:768px){.pu-file-item{grid-template-columns:1fr auto 100px 80px auto;grid-template-areas:"name status bar size button";gap:1rem}.pu-file-item .upload-status{text-align:right;font-size:.95rem;max-width:400px}.pu-file-item .file-size{font-size:.95rem}.pu-file-item progress{height:8px}}.btn-upload-custom{background-color:#0d6efd!important;background-image:none!important;border:none;color:#fff;padding:.8rem 2.5rem;font-weight:600;border-radius:50px;box-shadow:0 4px 12px rgba(0,0,0,.3);transition:all .2s ease-in-out;display:inline-block}.btn-upload-custom:hover:not(:disabled){background-color:#1e7eff!important;transform:translateY(-1px)}.btn-upload-custom:disabled{background-color:#212529!important;color:#6c757d!important;border:1px solid #343a40!important;box-shadow:none;cursor:not-allowed;opacity:1}.stat-card{background:#1a1d20;border:1px solid #2d3238;border-radius:12px;overflow:hidden;transition:transform .2s ease,box-shadow .2s ease}.stat-card:hover{transform:translateY(-3px);box-shadow:0 10px 20px rgba(0,0,0,.3)!important;border-color:#0d6efd}.stat-icon{opacity:.6;font-size:1.2rem}.progress-stat{height:4px;background-color:#2b3035}.log-dark-input{background-color:#2b3035!important;border-color:#444b52!important;color:#e9ecef!important}.log-dark-input:focus{background-color:#32383e!important;border-color:#0d6efd!important;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.input-group-text-dark{background-color:#1a1d20!important;border-color:#444b52!important;color:#adb5bd!important}#logviewer::-webkit-scrollbar{width:8px}#logviewer::-webkit-scrollbar-track{background:#000}#logviewer::-webkit-scrollbar-thumb{background:#333;border-radius:4px}.download-wrapper{min-height:70vh;display:flex;align-items:center;justify-content:center}.file-card{border:1px solid #333;border-radius:1rem;box-shadow:0 10px 30px rgba(0,0,0,.5);max-width:450px;width:100%;background:#1e1e1e;color:#e0e0e0}.icon-container{width:70px;height:70px;background-color:#2d2d2d;border-radius:50%;display:flex;align-items:center;justify-content:center;margin:0 auto 1.5rem;color:#0d6efd}.filename-text{word-wrap:break-word;color:#fff;font-weight:600}.dark-list-item{background-color:#252525!important;border-color:#333!important;color:#b0b0b0!important}.popover{--bs-popover-bg:#212529;--bs-popover-header-bg:#212529;--bs-popover-header-color:#dee2e6;--bs-popover-body-color:#dee2e6;--bs-popover-body-padding-y:0;--bs-popover-border-color:rgba(255, 255, 255, 0.15);--bs-popover-arrow-border:rgba(255, 255, 255, 0.15)}.popover-header{border-bottom-color:rgba(255,255,255,.15)}.upload-options-section-label{font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:#6c757d;margin-bottom:.5rem}.upload-options-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:.5rem;margin-bottom:.75rem}@media(max-width:767px){.upload-options-grid{grid-template-columns:1fr}}.upload-option-card{background-color:#212529;border:1px solid rgba(255,255,255,.1);border-radius:8px;padding:.5rem .75rem;transition:border-color .2s ease,box-shadow .2s ease}.upload-option-card:has(.upload-option-toggle:checked){border-color:rgba(13,110,253,.5);box-shadow:0 0 0 1px rgba(13,110,253,.2)}.upload-option-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:.4rem}.upload-option-icon{font-size:.85rem;color:#6ea8fe;opacity:.9}.upload-option-label{font-size:.72rem;font-weight:600;color:#adb5bd;text-transform:uppercase;letter-spacing:.06em}.upload-option-toggle{cursor:pointer}.upload-option-input-group{margin-top:0}.upload-option-card .form-control{background-color:#2b3035;border-color:rgba(255,255,255,.15);color:#dee2e6;padding:.25rem .5rem;font-size:.875rem}.upload-option-card .form-control:disabled{background-color:#262a2e;color:#555;border-color:rgba(255,255,255,8%)}.upload-option-suffix{background-color:#2b3035;border-color:rgba(255,255,255,.15);color:#adb5bd;font-size:.8rem;padding:.25rem .5rem}.upload-option-card input[type=number]::-webkit-inner-spin-button{filter:invert(1)brightness(.6)}.upload-option-zip-input{font-size:.8rem}.upload-option-zip-input::placeholder{color:#6c757d;opacity:1}.modal-samesize-input-edit{width:11rem}.modal-samesize-input-filerequest{width:7rem}.filename{font-weight:700;font-size:14px;margin-bottom:5px}.upload-progress-container{display:flex;align-items:center}.upload-progress-bar{position:relative;height:10px;background-color:#eee;flex:1;margin-right:10px;border-radius:4px}.upload-progress-bar-progress{position:absolute;top:0;left:0;height:100%;background-color:#0a0;border-radius:4px;transition:width .2s ease-in-out}.upload-progress-info{font-size:12px}.us-container{margin-top:10px;margin-bottom:20px}.uploaderror{font-weight:700;color:red;margin-bottom:5px}.uploads-container{background-color:#2f343a;border:2px solid rgba(0,0,0,.3);border-radius:5px;margin-left:0;margin-right:0;max-width:none;visibility:hidden} \ No newline at end of file diff --git a/internal/webserver/web/static/js/admin_ui_upload.js b/internal/webserver/web/static/js/admin_ui_upload.js index 4232afc3..c110e7cb 100644 --- a/internal/webserver/web/static/js/admin_ui_upload.js +++ b/internal/webserver/web/static/js/admin_ui_upload.js @@ -23,6 +23,12 @@ function initDropzone() { init: function() { dropzoneObject = this; this.on("addedfile", file => { + // If zip grouping is enabled, buffer the file and compress a batch + // into a single archive instead of uploading files individually. + // handleZipAddedFile() returns true if it took ownership of the file. + if (typeof handleZipAddedFile === "function" && handleZipAddedFile(file)) { + return; + } file.upload.uuid = getUuid(); saveUploadDefaults(); addFileProgress(file); diff --git a/internal/webserver/web/static/js/admin_ui_zip.js b/internal/webserver/web/static/js/admin_ui_zip.js new file mode 100644 index 00000000..587cb67e --- /dev/null +++ b/internal/webserver/web/static/js/admin_ui_zip.js @@ -0,0 +1,459 @@ +// This file contains JS code for the browser-side zip grouping feature. +// All files named admin_*.js will be merged together and minimised by calling +// go generate ./... +// +// Feature: when the "Zip Archive" upload option is enabled (default), files +// added to the dropzone are collected into a batch, compressed into a single +// zip archive in the browser using JSZip, and then handed back to the dropzone +// as one regular file upload. The original file structure (relative paths of +// dropped folders) is preserved inside the archive. + + +// Files added within this window (ms) are grouped into a single archive. This +// lets a multi-file selection or a folder drop arrive as one archive instead of +// starting a new zip for every individual file event. +const ZIP_BATCH_DEBOUNCE_MS = 400; + +// Marker property set on the generated zip File so that the addedfile handler +// does not try to re-compress it (which would cause an infinite loop). +const ZIP_GENERATED_FLAG = "isGokapiZipArchive"; + +// Buffer of files waiting to be compressed and the timer that flushes them. +var zipPendingFiles = []; +var zipBatchTimer = null; + +// Incrementing id so that each in-progress archive gets its own status row. +var zipStatusCounter = 0; + + +/** + * isZipGroupingEnabled returns true if the user has enabled the zip option. + * The checkbox is disabled (and therefore never checked) when end-to-end + * encryption is active, so E2E uploads always take the normal per-file path. + */ +function isZipGroupingEnabled() { + let checkbox = document.getElementById("enableZip"); + return checkbox != null && checkbox.checked && !checkbox.disabled; +} + + +/** + * initZipDefaults restores the zip checkbox state and name template from a + * previous session (if any) and wires up handlers to persist changes. It is + * a no-op when the fields are absent (e.g. E2E view without the controls). + * Called once after the page loads. + */ +function initZipDefaults() { + let checkbox = document.getElementById("enableZip"); + let templateInput = document.getElementById("zipNameTemplate"); + + if (checkbox != null && !checkbox.disabled) { + // getLocalStorageWithDefault is defined in admin_ui_upload.js. + let storedEnabled = getLocalStorageWithDefault("defaultZipEnabled", "true"); + checkbox.checked = storedEnabled === "true"; + checkbox.addEventListener("change", () => { + localStorage.setItem("defaultZipEnabled", checkbox.checked); + }); + } + + if (templateInput != null) { + let storedTemplate = localStorage.getItem("defaultZipNameTemplate"); + if (storedTemplate !== null && storedTemplate !== "") { + templateInput.value = storedTemplate; + } + templateInput.addEventListener("change", () => { + localStorage.setItem("defaultZipNameTemplate", templateInput.value); + }); + } +} + + +/** + * handleZipAddedFile is called from the dropzone "addedfile" handler. + * + * Returns true if this file has been taken over by the zip feature (i.e. it was + * buffered for compression and must not follow the normal upload path). + * Returns false if the file should be uploaded normally (zip disabled, or this + * is the already-generated archive itself). + * + * @param {File} file + * @returns {boolean} + */ +function handleZipAddedFile(file) { + // The generated archive itself must upload normally. + if (file[ZIP_GENERATED_FLAG] === true) { + return false; + } + if (!isZipGroupingEnabled()) { + return false; + } + + // Remove the individual file from the dropzone queue so it is not uploaded + // on its own. Its bytes are still fully accessible via the File reference we + // keep here. + try { + dropzoneObject.removeFile(file); + } catch (e) { + console.warn("Could not remove buffered file from dropzone:", e); + } + + zipPendingFiles.push(file); + + // (Re)start the debounce timer. Once it fires, the whole batch is zipped. + if (zipBatchTimer !== null) { + clearTimeout(zipBatchTimer); + } + zipBatchTimer = setTimeout(flushZipBatch, ZIP_BATCH_DEBOUNCE_MS); + + return true; +} + + +/** + * flushZipBatch compresses all currently buffered files into a single archive + * and adds the resulting file back to the dropzone as a normal upload. + */ +function flushZipBatch() { + zipBatchTimer = null; + + if (zipPendingFiles.length === 0) { + return; + } + + // Take ownership of the current batch and reset the buffer so that files + // added while we compress start a fresh batch. + let batch = zipPendingFiles; + zipPendingFiles = []; + + // A single loose file (not part of a dropped folder) is never zipped - + // wrapping one file in an archive adds no value. It is uploaded as-is. + // Note: a single file that came from inside a folder still gets zipped, + // so that the folder upload rule (folder name + date) is preserved. + if (batch.length === 1 && getTopLevelFolder(batch) === null) { + addFilesWithoutZip(batch); + return; + } + + if (typeof JSZip === "undefined") { + // Safety net: if the library failed to load, fall back to uploading the + // files individually rather than losing them. + console.error("JSZip is not available - uploading files without compression."); + addFilesWithoutZip(batch); + return; + } + + let statusId = "zip-" + (zipStatusCounter++); + let archiveName = generateZipFilename(batch); + addZipStatus(statusId, archiveName); + + let zip = new JSZip(); + for (let file of batch) { + // Preserve the original structure: dropzone sets fullPath for files that + // came from a dropped directory. Fall back to the plain file name. + let entryName = getRelativePathForFile(file); + zip.file(entryName, file, { + date: file.lastModified ? new Date(file.lastModified) : new Date(), + }); + } + + zip.generateAsync({ + type: "blob", + compression: "DEFLATE", + compressionOptions: { + level: 6, + }, + }, (metadata) => { + updateZipStatus(statusId, "Compressing... " + Math.round(metadata.percent) + "%", metadata.percent); + }) + .then((blob) => { + let archive = new File([blob], archiveName, { + type: "application/zip", + lastModified: Date.now(), + }); + // Mark the archive so the addedfile handler lets it through. + archive[ZIP_GENERATED_FLAG] = true; + + updateZipStatus(statusId, "Compressed (" + formatZipBytes(blob.size) + ") - queued for upload", 100); + setTimeout(() => removeZipStatus(statusId), 1500); + + // Hand the finished archive to the dropzone as a normal upload. + dropzoneObject.addFile(archive); + }) + .catch((err) => { + console.error("Zip compression failed:", err); + setZipStatusError(statusId, "Compression failed: " + (err && err.message ? err.message : err)); + }); +} + + +/** + * addFilesWithoutZip is the fallback path used when JSZip is unavailable. Each + * file is re-added to the dropzone but flagged as generated so it is not + * re-intercepted, causing it to upload individually. + * @param {File[]} files + */ +function addFilesWithoutZip(files) { + for (let file of files) { + file[ZIP_GENERATED_FLAG] = true; + dropzoneObject.addFile(file); + } +} + + +/** + * getRelativePathForFile returns the path used for the file inside the archive, + * preserving any directory structure from a dropped folder. + * @param {File} file + * @returns {string} + */ +function getRelativePathForFile(file) { + // Dropzone stores the relative path (for directory drops) on fullPath. + if (file.fullPath && typeof file.fullPath === "string" && file.fullPath !== "") { + return file.fullPath.replace(/^\/+/, ""); + } + // Some browsers expose webkitRelativePath for . + if (file.webkitRelativePath && file.webkitRelativePath !== "") { + return file.webkitRelativePath; + } + return file.name; +} + + +/** + * getTopLevelFolder inspects the batch and, if every file lives under one single + * top-level directory, returns that directory's name. Otherwise returns null + * (mixed files, or files with no directory structure). + * + * This is used so that dropping a single folder produces an archive named after + * that folder plus the date/time, e.g. "myfolder-20260807-112025.zip". + * + * @param {File[]} batch + * @returns {string|null} + */ +function getTopLevelFolder(batch) { + let topFolder = null; + for (let file of batch) { + let relPath = getRelativePathForFile(file); + // A file that carries directory structure contains a slash. + let slashIndex = relPath.indexOf("/"); + if (slashIndex === -1) { + // A loose file with no folder -> this is not a single-folder upload. + return null; + } + let firstSegment = relPath.substring(0, slashIndex); + if (firstSegment === "") { + return null; + } + if (topFolder === null) { + topFolder = firstSegment; + } else if (topFolder !== firstSegment) { + // Files span more than one top-level folder. + return null; + } + } + return topFolder; +} + + +/** + * strftime renders a small subset of strftime-style tokens against a Date. + * Supported tokens: %Y %y %m %d %H %M %S %j %% . + * Unknown tokens are left as-is (minus the leading %). + * + * @param {string} pattern + * @param {Date} date + * @returns {string} + */ +function strftime(pattern, date) { + let pad = (n, len) => String(n).padStart(len || 2, "0"); + let dayOfYear = () => { + let start = new Date(date.getFullYear(), 0, 0); + let diff = date - start; + return Math.floor(diff / 86400000); + }; + return pattern.replace(/%([A-Za-z%])/g, (match, token) => { + switch (token) { + case "Y": return String(date.getFullYear()); + case "y": return pad(date.getFullYear() % 100); + case "m": return pad(date.getMonth() + 1); + case "d": return pad(date.getDate()); + case "H": return pad(date.getHours()); + case "M": return pad(date.getMinutes()); + case "S": return pad(date.getSeconds()); + case "j": return pad(dayOfYear(), 3); + case "%": return "%"; + default: return token; + } + }); +} + + +/** + * sanitizeArchiveName strips characters that are unsafe or awkward in a + * download filename and trims surrounding whitespace/dots. + * @param {string} name + * @returns {string} + */ +function sanitizeArchiveName(name) { + // Remove path separators and characters disallowed on common filesystems. + let cleaned = name.replace(/[\/\\:*?"<>|]+/g, "_").trim(); + // Collapse whitespace and strip leading/trailing dots. + cleaned = cleaned.replace(/\s+/g, " ").replace(/^\.+|\.+$/g, ""); + return cleaned; +} + + +/** + * generateZipFilename builds the archive name for a batch. + * + * Rules: + * - If the batch is a single top-level folder, the name is + * "-.zip" (ignoring the template field). + * - Otherwise the user's name template is rendered with strftime tokens. + * An empty template falls back to the default prefix + timestamp. + * - A ".zip" extension is always ensured. + * + * @param {File[]} [batch] + * @returns {string} + */ +function generateZipFilename(batch) { + let now = new Date(); + let dateStamp = strftime("%Y%m%d-%H%M%S", now); + + // Single-folder upload: name after the folder + date-time. + if (Array.isArray(batch) && batch.length > 0) { + let folder = getTopLevelFolder(batch); + if (folder !== null && folder !== "") { + let name = sanitizeArchiveName(folder) + "-" + dateStamp; + return ensureZipExtension(name); + } + } + + // Otherwise use the user-provided template. When the field is left empty, + // fall back to the server-configured default (GOKAPI_DEFAULT_ZIP_NAME, + // injected as defaultZipNameTemplate) and finally to a hardcoded default. + let templateInput = document.getElementById("zipNameTemplate"); + let template = templateInput != null ? templateInput.value.trim() : ""; + if (template === "") { + if (typeof defaultZipNameTemplate === "string" && defaultZipNameTemplate.trim() !== "") { + template = defaultZipNameTemplate.trim(); + } else { + template = "gokapi-upload-%Y%m%d-%H%M%S"; + } + } + + let rendered = sanitizeArchiveName(strftime(template, now)); + if (rendered === "") { + rendered = "gokapi-upload-" + dateStamp; + } + return ensureZipExtension(rendered); +} + + +/** + * ensureZipExtension appends ".zip" unless the name already ends with it. + * @param {string} name + * @returns {string} + */ +function ensureZipExtension(name) { + if (/\.zip$/i.test(name)) { + return name; + } + return name + ".zip"; +} + + +/** + * formatZipBytes returns a human-readable size string. + * @param {number} bytes + * @returns {string} + */ +function formatZipBytes(bytes) { + if (bytes < 1024) { + return bytes + " B"; + } + let units = ["KB", "MB", "GB", "TB"]; + let value = bytes / 1024; + let unitIndex = 0; + while (value >= 1024 && unitIndex < units.length - 1) { + value /= 1024; + unitIndex++; + } + return (Math.round(value * 10) / 10) + " " + units[unitIndex]; +} + + +// ── Compression status UI ──────────────────────────────────────────────── +// Reuses the same visual containers as the upload progress rows so the +// compression phase looks consistent with the rest of the upload UI. + +function addZipStatus(statusId, archiveName) { + const container = document.createElement("div"); + container.setAttribute("id", `us-container-${statusId}`); + container.classList.add("us-container"); + + const filenameDiv = document.createElement("div"); + filenameDiv.classList.add("filename"); + filenameDiv.textContent = archiveName; + container.appendChild(filenameDiv); + + const progressContainerDiv = document.createElement("div"); + progressContainerDiv.classList.add("upload-progress-container"); + + const progressBarDiv = document.createElement("div"); + progressBarDiv.classList.add("upload-progress-bar"); + + const progressBarProgressDiv = document.createElement("div"); + progressBarProgressDiv.setAttribute("id", `us-progressbar-${statusId}`); + progressBarProgressDiv.classList.add("upload-progress-bar-progress"); + progressBarProgressDiv.style.width = "0%"; + progressBarDiv.appendChild(progressBarProgressDiv); + + const progressInfoDiv = document.createElement("div"); + progressInfoDiv.setAttribute("id", `us-progress-info-${statusId}`); + progressInfoDiv.classList.add("upload-progress-info"); + progressInfoDiv.textContent = "Preparing archive..."; + + progressContainerDiv.appendChild(progressBarDiv); + progressContainerDiv.appendChild(progressInfoDiv); + container.appendChild(progressContainerDiv); + + const uploadstatusContainer = document.getElementById("uploadstatus"); + uploadstatusContainer.appendChild(container); + uploadstatusContainer.style.visibility = "visible"; +} + +function updateZipStatus(statusId, text, percent) { + let bar = document.getElementById(`us-progressbar-${statusId}`); + let info = document.getElementById(`us-progress-info-${statusId}`); + if (bar != null && typeof percent === "number") { + let rounded = Math.max(0, Math.min(100, Math.round(percent))); + bar.style.width = rounded + "%"; + } + if (info != null) { + info.innerText = text; + } +} + +function setZipStatusError(statusId, message) { + let bar = document.getElementById(`us-progressbar-${statusId}`); + let info = document.getElementById(`us-progress-info-${statusId}`); + if (bar != null) { + bar.style.width = "100%"; + bar.style.backgroundColor = "red"; + } + if (info != null) { + info.innerText = message; + info.classList.add("uploaderror"); + } +} + +function removeZipStatus(statusId) { + const container = document.getElementById(`us-container-${statusId}`); + if (container != null) { + container.remove(); + } + if (document.querySelectorAll("#uploadstatus .us-container").length < 1) { + document.getElementById("uploadstatus").style.visibility = "hidden"; + } +} diff --git a/internal/webserver/web/static/js/min/admin.min.ccb62a8d5b.js b/internal/webserver/web/static/js/min/admin.min.ccb62a8d5b.js index 9f4f99c8..67336b99 100644 --- a/internal/webserver/web/static/js/min/admin.min.ccb62a8d5b.js +++ b/internal/webserver/web/static/js/min/admin.min.ccb62a8d5b.js @@ -1,10 +1,10 @@ -const storedTokens=new Map;async function getToken(e,t){const n="./auth/token";if(!t){if(!storedTokens.has(e))return getToken(e,!0);let t=storedTokens.get(e);return t.expiry-Date.now()/1e3<60?getToken(e,!0):t.key}const s={method:"POST",headers:{"Content-Type":"application/json",permission:e}};try{const o=await fetch(n,s);if(!o.ok)throw new Error(`Request failed with status: ${o.status}`);const t=await o.json();if(!t.hasOwnProperty("key"))throw new Error(`Invalid response when trying to get token`);return storedTokens.set(e,{key:t.key,expiry:t.expiry}),t.key}catch(e){throw console.error("Error in getToken:",e),e}}async function apiAuthModify(e,t,n){const o="./api/auth/modify",i="PERM_API_MOD";let s;try{s=await getToken(i,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const a={method:"POST",headers:{"Content-Type":"application/json",apikey:s,targetKey:e,permission:t,permissionModifier:n}};try{const e=await fetch(o,a);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiAuthModify:",e),e}}async function apiAuthFriendlyName(e,t){const s="./api/auth/friendlyname",o="PERM_API_MOD";let n;try{n=await getToken(o,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const i={method:"PUT",headers:{"Content-Type":"application/json",apikey:n,targetKey:e,friendlyName:t}};try{const e=await fetch(s,i);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiAuthModify:",e),e}}async function apiAuthDelete(e){const n="./api/auth/delete",s="PERM_API_MOD";let t;try{t=await getToken(s,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const o={method:"POST",headers:{"Content-Type":"application/json",apikey:t,targetKey:e}};try{const e=await fetch(n,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiAuthDelete:",e),e}}async function apiAuthCreate(){const t="./api/auth/create",n="PERM_API_MOD";let e;try{e=await getToken(n,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const s={method:"POST",headers:{"Content-Type":"application/json",apikey:e,basicPermissions:"true"}};try{const e=await fetch(t,s);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const n=await e.json();return n}catch(e){throw console.error("Error in apiAuthCreate:",e),e}}async function apiChunkComplete(e,t,n,s,o,i,a,r,c,l){const u="./api/chunk/complete",h="PERM_UPLOAD";let d;try{d=await getToken(h,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const m={method:"POST",headers:{"Content-Type":"application/json",apikey:d,uuid:e,filename:"base64:"+Base64.encode(t),filesize:n,realsize:s,contenttype:o,allowedDownloads:i,expiryDays:a,password:r,isE2E:c,nonblocking:l}};try{const e=await fetch(u,m);if(!e.ok){let t;try{const n=await e.json();t=n.ErrorMessage||`Request failed with status: ${e.status}`}catch{const n=await e.text();t=n||`Request failed with status: ${e.status}`}throw new Error(t)}const t=await e.json();return t}catch(e){throw console.error("Error in apiChunkComplete:",e),e}}async function apiFilesReplace(e,t){const s="./api/files/replace",o="PERM_REPLACE";let n;try{n=await getToken(o,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const i={method:"PUT",headers:{"Content-Type":"application/json",id:e,apikey:n,idNewContent:t,deleteNewFile:!1}};try{const e=await fetch(s,i);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiFilesReplace:",e),e}}async function apiFilesListById(e){const n="./api/files/list/"+e,s="PERM_VIEW";let t;try{t=await getToken(s,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const o={method:"GET",headers:{"Content-Type":"application/json",apikey:t}};try{const e=await fetch(n,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiFilesListById:",e),e}}async function apiFilesListDownloadSingle(e){const n="./api/files/download/"+e,s="PERM_DOWNLOAD";let t;try{t=await getToken(s,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const o={method:"GET",headers:{"Content-Type":"application/json",apikey:t,presignUrl:!0}};try{const e=await fetch(n,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiFilesListDownloadSingle:",e),e}}async function apiFilesListDownloadZip(e,t){const s="./api/files/downloadzip",o="PERM_DOWNLOAD";let n;try{n=await getToken(o,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const i={method:"GET",headers:{"Content-Type":"application/json",apikey:n,ids:e,filename:"base64:"+Base64.encode(t),presignUrl:!0}};try{const e=await fetch(s,i);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiFilesListDownloadZip:",e),e}}async function apiFilesModify(e,t,n,s,o){const a="./api/files/modify",r="PERM_EDIT";let i;try{i=await getToken(r,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const c={method:"PUT",headers:{"Content-Type":"application/json",id:e,apikey:i,allowedDownloads:t,expiryTimestamp:n,password:s,originalPassword:o}};try{const e=await fetch(a,c);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiFilesModify:",e),e}}async function apiFilesDelete(e,t){const s="./api/files/delete",o="PERM_DELETE";let n;try{n=await getToken(o,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const i={method:"POST",headers:{"Content-Type":"application/json",apikey:n,id:e,delay:t}};try{const e=await fetch(s,i);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiFilesDelete:",e),e}}async function apiFilesRestore(e){const n="./api/files/restore",s="PERM_DELETE";let t;try{t=await getToken(s,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const o={method:"POST",headers:{"Content-Type":"application/json",apikey:t,id:e}};try{const e=await fetch(n,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiFilesRestore:",e),e}}async function apiUserCreate(e){const n="./api/user/create",s="PERM_MANAGE_USERS";let t;try{t=await getToken(s,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const o={method:"POST",headers:{"Content-Type":"application/json",apikey:t,username:e}};try{const e=await fetch(n,o);if(!e.ok)throw e.status==409?new Error("duplicate"):new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiUserModify:",e),e}}async function apiUserModify(e,t,n){const o="./api/user/modify",i="PERM_MANAGE_USERS";let s;try{s=await getToken(i,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const a={method:"POST",headers:{"Content-Type":"application/json",apikey:s,userid:e,userpermission:t,permissionModifier:n}};try{const e=await fetch(o,a);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiUserModify:",e),e}}async function apiUserChangeRank(e,t){const s="./api/user/changeRank",o="PERM_MANAGE_USERS";let n;try{n=await getToken(o,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const i={method:"POST",headers:{"Content-Type":"application/json",apikey:n,userid:e,newRank:t}};try{const e=await fetch(s,i);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiUserModify:",e),e}}async function apiUserDelete(e,t){const s="./api/user/delete",o="PERM_MANAGE_USERS";let n;try{n=await getToken(o,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const i={method:"POST",headers:{"Content-Type":"application/json",apikey:n,userid:e,deleteFiles:t}};try{const e=await fetch(s,i);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiUserDelete:",e),e}}async function apiUserResetPassword(e,t){const s="./api/user/resetPassword",o="PERM_MANAGE_USERS";let n;try{n=await getToken(o,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const i={method:"POST",headers:{"Content-Type":"application/json",apikey:n,userid:e,generateNewPassword:t}};try{const e=await fetch(s,i);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiUserResetPassword:",e),e}}async function apiLogSystemStatus(){const t="./api/logs/systemStatus",n="PERM_MANAGE_LOGS";let e;try{e=await getToken(n,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const s={method:"GET",headers:{"Content-Type":"application/json",apikey:e}};try{const e=await fetch(t,s);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const n=await e.json();return n}catch(e){throw console.error("Error in apiLogSystemStatus:",e),e}}async function apiLogResetTraffic(){const t="./api/logs/resetTraffic",n="PERM_MANAGE_LOGS";let e;try{e=await getToken(n,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const s={method:"GET",headers:{"Content-Type":"application/json",apikey:e}};try{const e=await fetch(t,s);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiLogResetTraffic:",e),e}}async function apiLogGet(e){const n="./api/logs/get",s="PERM_MANAGE_LOGS";let t;try{t=await getToken(s,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const o={method:"GET",headers:{"Content-Type":"application/json",apikey:t,timestamp:e}};try{const e=await fetch(n,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiLogGet:",e),e}}async function apiLogsDelete(e){const n="./api/logs/delete",s="PERM_MANAGE_LOGS";let t;try{t=await getToken(s,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const o={method:"POST",headers:{"Content-Type":"application/json",apikey:t,timestamp:e}};try{const e=await fetch(n,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiLogsDelete:",e),e}}async function apiE2eGet(){const t="./api/e2e/get",n="PERM_UPLOAD";let e;try{e=await getToken(n,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const s={method:"POST",headers:{"Content-Type":"application/json",apikey:e}};try{const e=await fetch(t,s);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);return await e.text()}catch(e){throw console.error("Error in apiE2eGet:",e),e}}async function apiE2eMutexLockUnlock(e){let t="./api/e2e/mutex/lock";e&&(t="./api/e2e/mutex/unlock");const s="PERM_UPLOAD";let n;try{n=await getToken(s,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const o={method:"GET",headers:{"Content-Type":"application/json",apikey:n}};try{const e=await fetch(t,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);return await e.text()}catch(e){throw console.error("Error in apiE2eMutexLock:",e),e}}async function apiE2eStore(e){const n="./api/e2e/set",s="PERM_UPLOAD";let t;try{t=await getToken(s,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const o={method:"POST",headers:{"Content-Type":"application/json",apikey:t},body:JSON.stringify({content:e})};try{const e=await fetch(n,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiE2eStore:",e),e}}async function apiURequestDelete(e){const n="./api/uploadrequest/delete",s="PERM_MANAGE_FILE_REQUESTS";let t;try{t=await getToken(s,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const o={method:"DELETE",headers:{"Content-Type":"application/json",apikey:t,id:e}};try{const e=await fetch(n,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiURequestDelete:",e),e}}async function apiURequestSave(e,t,n,s,o,i){const r="./api/uploadrequest/save",c="PERM_MANAGE_FILE_REQUESTS";let a;try{a=await getToken(c,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const l={method:"POST",headers:{"Content-Type":"application/json",apikey:a,id:e,name:"base64:"+Base64.encode(t),expiry:o,maxfiles:n,maxsize:s,notes:"base64:"+Base64.encode(i)}};try{const e=await fetch(r,l);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiURequestDelete:",e),e}}try{var toastId,calendarInstance,dropzoneObject,isE2EEnabled,isUploading,rowCount,sseWorkerPort,statusItemCount,clipboard=new ClipboardJS(".copyurl")}catch{}function showToast(e,t){let n=document.getElementById("toastnotification");typeof t!="undefined"?n.innerText=t:n.innerText=n.dataset.default,n.classList.add("show"),clearTimeout(toastId),toastId=setTimeout(()=>{hideToast()},e)}function hideToast(){document.getElementById("toastnotification").classList.remove("show")}calendarInstance=null;function createCalendar(e,t){const n=new Date(t*1e3);calendarInstance=flatpickr(document.getElementById(e),{enableTime:!0,dateFormat:"U",altInput:!0,altFormat:"Y-m-d H:i",allowInput:!0,time_24hr:!0,defaultDate:n,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 downloadFileWithPresign(e){apiFilesListDownloadSingle(e).then(e=>{if(!e.hasOwnProperty("downloadUrl"))throw new Error("Unable to get presigned key");const t=document.createElement("a");t.href=e.downloadUrl,t.style.display="none",document.body.appendChild(t),t.click(),t.remove()}).catch(e=>{alert("Unable to download: "+e),console.error("Error:",e)})}function downloadFilesZipWithPresign(e,t){apiFilesListDownloadZip(e,t).then(e=>{if(!e.hasOwnProperty("downloadUrl"))throw new Error("Unable to get presigned key");const t=document.createElement("a");t.href=e.downloadUrl,t.style.display="none",document.body.appendChild(t),t.click(),t.remove()}).catch(e=>{alert("Unable to download: "+e),console.error("Error:",e)})}function doLogout(){typeof sseWorkerPort!="undefined"&&sseWorkerPort!==null&&sseWorkerPort.postMessage({type:"shutdown"}),window.location.href="./logout"}function changeApiPermission(e,t,n){var o,i,s=document.getElementById(n);if(s.classList.contains("perm-processing")||s.classList.contains("perm-nochange"))return;o=s.classList.contains("perm-granted"),s.classList.add("perm-processing"),s.classList.remove("perm-granted"),s.classList.remove("perm-notgranted"),i="GRANT",o&&(i="REVOKE"),apiAuthModify(e,t,i).then(e=>{o?(s.classList.add("perm-notgranted"),s.classList.add("perm-nownotgranted")):(s.classList.add("perm-granted"),s.classList.add("perm-nowgranted")),s.classList.remove("perm-processing"),setTimeout(()=>{s.classList.remove("perm-nowgranted"),s.classList.remove("perm-nownotgranted")},1e3)}).catch(e=>{o?s.classList.add("perm-granted"):s.classList.add("perm-notgranted"),s.classList.remove("perm-processing"),alert("Unable to set permission: "+e),console.error("Error:",e)})}function deleteApiKey(e){document.getElementById("delete-"+e).disabled=!0,apiAuthDelete(e).then(t=>{document.getElementById("row-"+e).classList.add("rowDeleting"),setTimeout(()=>{document.getElementById("row-"+e).remove()},290)}).catch(e=>{alert("Unable to delete API key: "+e),console.error("Error:",e)})}function newApiKey(){document.getElementById("button-newapi").disabled=!0,apiAuthCreate().then(e=>{addRowApi(e.Id,e.PublicId),document.getElementById("button-newapi").disabled=!1}).catch(e=>{alert("Unable to create API key: "+e),console.error("Error:",e)})}function addFriendlyNameChange(e){let t=document.getElementById("friendlyname-"+e);if(t.classList.contains("isBeingEdited"))return;t.classList.add("isBeingEdited");let i=t.innerText,n=document.createElement("input");n.size=5,n.value=i;let s=!0,o=function(){if(!s)return;s=!1;let o=n.value;o==""&&(o="Unnamed key"),t.innerText=o,t.classList.remove("isBeingEdited"),apiAuthFriendlyName(e,o).catch(e=>{alert("Unable to save name: "+e),console.error("Error:",e)})};n.onblur=o,n.addEventListener("keyup",function(e){e.keyCode===13&&(e.preventDefault(),o())}),t.innerText="",t.appendChild(n),n.focus()}function addRowApi(e,t){let p=document.getElementById("apitable"),s=p.insertRow(0);s.id="row-"+t;let i=0,c=s.insertCell(i++),l=s.insertCell(i++),d=s.insertCell(i++),a=s.insertCell(i++),u;canViewOtherApiKeys&&(u=s.insertCell(i++));let h=s.insertCell(i++);canViewOtherApiKeys&&(u.classList.add("newApiKey"),u.innerText=userName),c.classList.add("newApiKey"),l.classList.add("newApiKey"),d.classList.add("newApiKey"),a.classList.add("newApiKey"),a.classList.add("prevent-select"),h.classList.add("newApiKey"),c.innerText="Unnamed key",c.id="friendlyname-"+t,c.onclick=function(){addFriendlyNameChange(t)},l.innerText=e,l.classList.add("font-monospace"),d.innerText="Never";const r=document.createElement("div");r.className="btn-group",r.setAttribute("role","group");const n=document.createElement("button");n.type="button",n.dataset.clipboardText=e,n.title="Copy API Key",n.className="copyurl btn btn-outline-light btn-sm",n.setAttribute("onclick","showToast(1000)");const m=document.createElement("i");m.className="bi bi-copy",n.appendChild(m);const o=document.createElement("button");o.type="button",o.id=`delete-${t}`,o.title="Delete",o.className="btn btn-outline-danger btn-sm",o.setAttribute("onclick",`deleteApiKey('${t}')`);const f=document.createElement("i");f.className="bi bi-trash3",o.appendChild(f),r.appendChild(n),r.appendChild(o),h.appendChild(r);const g=[{perm:"PERM_VIEW",icon:"bi-eye",granted:!0,title:"List Uploads"},{perm:"PERM_UPLOAD",icon:"bi-file-earmark-plus",granted:!0,title:"Upload"},{perm:"PERM_EDIT",icon:"bi-pencil",granted:!0,title:"Edit Uploads"},{perm:"PERM_DELETE",icon:"bi-trash3",granted:!0,title:"Delete Uploads"},{perm:"PERM_REPLACE",icon:"bi-recycle",granted:!1,title:"Replace Uploads"},{perm:"PERM_DOWNLOAD",icon:"bi-box-arrow-in-down",granted:!1,title:"Download Files"},{perm:"PERM_MANAGE_FILE_REQUESTS",icon:"bi-file-earmark-arrow-up",granted:!1,title:"Manage File Requests"},{perm:"PERM_MANAGE_USERS",icon:"bi-people",granted:!1,title:"Manage Users"},{perm:"PERM_MANAGE_LOGS",icon:"bi-card-list",granted:!1,title:"Manage System Logs"},{perm:"PERM_API_MOD",icon:"bi-sliders2",granted:!1,title:"Manage API Keys"}];if(g.forEach(({perm:e,icon:n,granted:s,title:o})=>{const i=document.createElement("i"),r=`${e.toLowerCase()}_${t}`;i.id=r,i.className=`bi ${n} ${s?"perm-granted":"perm-notgranted"}`,i.title=o,i.setAttribute("onclick",`changeApiPermission("${t}","${e}", "${r}");`),a.appendChild(i),a.appendChild(document.createTextNode(" "))}),!canReplaceFiles){let e=document.getElementById("perm_replace_"+t);e.classList.add("perm-unavailable"),e.classList.add("perm-nochange")}if(!canManageUsers){let e=document.getElementById("perm_manage_users_"+t);e.classList.add("perm-unavailable"),e.classList.add("perm-nochange")}if(!canViewSystemLog){let e=document.getElementById("perm_manage_logs_"+t);e.classList.add("perm-unavailable"),e.classList.add("perm-nochange")}if(!canCreateFileRequest){let e=document.getElementById("perm_manage_file_requests_"+t);e.classList.add("perm-unavailable"),e.classList.add("perm-nochange")}setTimeout(()=>{c.classList.remove("newApiKey"),l.classList.remove("newApiKey"),d.classList.remove("newApiKey"),a.classList.remove("newApiKey"),h.classList.remove("newApiKey")},700)}function deleteFileRequest(e){document.getElementById("delete-"+e).disabled=!0,apiURequestDelete(e).then(t=>{const s=document.getElementById("row-"+e),n=document.getElementById("filelist-"+e);s.classList.add("rowDeleting"),n!==null&&n.classList.add("rowDeleting"),setTimeout(()=>{s.remove(),n!==null&&n.remove()},290)}).catch(e=>{alert("Unable to delete file request: "+e),console.error("Error:",e)})}function deleteOrShowModal(e,t,n){n===0?deleteFileRequest(e):showDeleteFRequestModal(e,t,n)}function deleteFileFr(e,t){document.getElementById("button-delete-"+e).disabled=!0;let n=document.getElementById("cell-listupload-"+e);apiFilesDelete(e,10).then(s=>{changeFileCountFr(t,-1),removeDownloadFileReference(e,t),n.classList.add("rowDeleting"),setTimeout(()=>{n.remove()},290),showToastFileDeletionFr(e)}).catch(e=>{alert("Unable to delete file: "+e),console.error("Error:",e)})}function changeFileCountFr(e,t){let n=document.getElementById("totalFiles-fr-"+e),s=Number(n.innerText)||0,o=s+t;n.innerText=o}function removeDownloadFileReference(e,t){const n=document.getElementById(`download-${t}`);if(!n)return;const a=n.getAttribute("onclick")||"",o=a.match(/downloadFilesZipWithPresign\('([^']*)',\s*'([^']*)'\)/),r=a.match(/downloadFileWithPresign\('([^']*)'\)/);let s=[],i="";o?(s=o[1].split(",").filter(e=>e!==""),i=o[2]):r&&(s=[r[1]],i=n.dataset.recordName||""),s=s.filter(t=>t!==e),s.length===0?(n.classList.add("disabled"),n.removeAttribute("onclick")):s.length===1?(n.classList.remove("disabled"),n.setAttribute("onclick",`downloadFileWithPresign('${s[0]}');`)):(n.classList.remove("disabled"),n.setAttribute("onclick",`downloadFilesZipWithPresign('${s.join(",")}', '${i}');`))}function showToastFileDeletionFr(e){let t=document.getElementById("toastnotificationUndo"),n=document.getElementById("cell-name-"+e).innerText,s=document.getElementById("toastFilename"),o=document.getElementById("toastUndoButton");s.innerText=n,o.dataset.fileid=e,hideToast(),t.classList.add("show"),clearTimeout(toastId),toastId=setTimeout(()=>{hideFileToast()},5e3)}function handleUndoFr(e){hideFileToast(),apiFilesRestore(e.dataset.fileid).then(e=>{window.location.reload()}).catch(e=>{alert("Unable to restore file: "+e),console.error("Error:",e)})}function showDeleteFRequestModal(e,t,n){document.getElementById("deleteModalBodyName").innerText=t,document.getElementById("deleteModalBodyCount").innerText=n,$("#deleteModal").modal("show"),document.getElementById("buttonDelete").onclick=function(){$("#deleteModal").modal("hide"),deleteFileRequest(e)}}function newFileRequest(){loadFileRequestDefaults(),document.getElementById("m_urequestlabel").innerText="New File Request",$("#addEditModal").modal("show"),document.getElementById("b_fr_save").onclick=function(){saveFileRequestDefaults(),saveFileRequest(),$("#addEditModal").modal("hide")}}function saveFileRequestDefaults(){if(document.getElementById("mc_maxfiles").checked?localStorage.setItem("fr_maxfiles",document.getElementById("mi_maxfiles").value):localStorage.setItem("fr_maxfiles",0),document.getElementById("mc_maxsize").checked?localStorage.setItem("fr_maxsize",document.getElementById("mi_maxsize").value):localStorage.setItem("fr_maxsize",0),document.getElementById("mc_expiry").checked){let e=document.getElementById("mi_expiry").value-Math.round(Date.now()/1e3);localStorage.setItem("fr_expiry",e)}else localStorage.setItem("fr_expiry",0)}function loadFileRequestDefaults(){const t=localStorage.getItem("fr_maxfiles"),n=localStorage.getItem("fr_maxsize");let e=localStorage.getItem("fr_expiry");if(e!=="0"&&e!==null){let t=new Date(Date.now()+Number(e*1e3));t.setHours(12,0,0,0),e=Math.floor(t.getTime()/1e3)}setModalValues("","",t,n,e,"")}function setModalValues(e,t,n,s,o,i){if(document.getElementById("freqId").value=e,t===null?document.getElementById("mFriendlyName").value="":document.getElementById("mFriendlyName").value=t,limitMaxFiles!=0){let e=document.getElementById("mc_maxfiles");(n===null||n==0)&&(n=limitMaxFiles),e.checked=!0,e.disabled=!0,e.title="Only admins can set this to unlimited",e.value="1",document.getElementById("mi_maxfiles").setAttribute("max",limitMaxFiles)}else{let e=document.getElementById("mc_maxfiles");e.disabled=!1,e.title="",document.getElementById("mi_maxfiles").setAttribute("max","")}if(limitMaxSize!=0){let e=document.getElementById("mc_maxsize");(s===null||s==0)&&(s=limitMaxSize),e.checked=!0,e.disabled=!0,e.title="Only admins can set this to unlimited",e.value="1",document.getElementById("mi_maxsize").setAttribute("max",limitMaxSize)}else{let e=document.getElementById("mc_maxsize");e.disabled=!1,e.title="",document.getElementById("mi_maxsize").setAttribute("max","")}if(n===null||n==0?(document.getElementById("mi_maxfiles").value="1",document.getElementById("mi_maxfiles").disabled=!0,document.getElementById("mc_maxfiles").checked=!1):(document.getElementById("mi_maxfiles").value=n,document.getElementById("mi_maxfiles").disabled=!1,document.getElementById("mc_maxfiles").checked=!0),s===null||s==0?(document.getElementById("mi_maxsize").value="10",document.getElementById("mi_maxsize").disabled=!0,document.getElementById("mc_maxsize").checked=!1):(document.getElementById("mi_maxsize").value=s,document.getElementById("mi_maxsize").disabled=!1,document.getElementById("mc_maxsize").checked=!0),o===null||o==0){const e=Math.floor(new Date(Date.now()+14*24*60*60*1e3).getTime()/1e3);document.getElementById("mi_expiry").disabled=!0,document.getElementById("mc_expiry").checked=!1,document.getElementById("mi_expiry").value=e,createCalendar("mi_expiry",e)}else document.getElementById("mi_expiry").value=o,document.getElementById("mi_expiry").disabled=!1,document.getElementById("mc_expiry").checked=!0,createCalendar("mi_expiry",o);document.getElementById("mNotes").value=i}function editFileRequest(e,t,n,s,o,i){setModalValues(e,t,n,s,o,i),document.getElementById("m_urequestlabel").innerText="Edit File Request",$("#addEditModal").modal("show"),document.getElementById("b_fr_save").onclick=function(){saveFileRequest(),$("#addEditModal").modal("hide")}}function saveFileRequest(){const s=document.getElementById("b_fr_save"),o=document.getElementById("freqId").value,i=document.getElementById("mFriendlyName").value,a=document.getElementById("mNotes").value;let e=0,t=0,n=0;document.getElementById("mc_maxfiles").checked&&(e=document.getElementById("mi_maxfiles").value),document.getElementById("mc_maxsize").checked&&(t=document.getElementById("mi_maxsize").value),document.getElementById("mc_expiry").checked&&(n=document.getElementById("mi_expiry").value),s.disabled=!0,apiURequestSave(o,i,e,t,n,a).then(e=>{document.getElementById("b_fr_save").disabled=!1,insertOrReplaceFileRequest(e)}).catch(e=>{alert("Unable to save file request: "+e),console.error("Error:",e),document.getElementById("b_fr_save").disabled=!1})}function checkMaxNumber(e){if(e.value==""){e.value="1";return}let t=e.getAttribute("max");if(t=="")return;e.value>t&&(e.value=t)}function insertOrReplaceFileRequest(e){const n=document.getElementById("filerequesttable");let t=document.getElementById(`row-${e.id}`);if(t){const n=document.getElementById(`cell-username-${e.id}`).innerText;t.replaceWith(createFileRequestRow(e,n))}else{let t=createFileRequestRow(e,userName);t.querySelectorAll("td").forEach(e=>{e.classList.add("newFileRequest"),setTimeout(()=>{e.classList.remove("newFileRequest")},700)}),n.prepend(t)}}function createFileRequestRow(e,t){function r(e){const t=document.createElement("td");return t.textContent=e,t}function h(e,t){const s=document.createElement("td"),n=document.createElement("a");return n.textContent=e,n.href=t,n.target="_blank",s.appendChild(n),s}function c(e){const t=document.createElement("i");return t.className=`bi ${e}`,t}const d=`${baseUrl}publicUpload?id=${e.id}&key=${e.apikey}`,n=document.createElement("tr");if(n.id=`row-${e.id}`,n.className="filerequest-item",n.appendChild(h(e.name,d)),e.maxfiles==0?n.appendChild(r(e.uploadedfiles)):n.appendChild(r(`${e.uploadedfiles} / ${e.maxfiles}`)),n.appendChild(r(getReadableSize(e.totalfilesize))),n.appendChild(r(formatTimestampWithNegative(e.lastupload,"None"))),n.appendChild(r(formatFileRequestExpiry(e.expiry))),canViewOtherRequests){let s=r(t);s.id=`cell-username-${e.id}`,n.appendChild(s)}const u=document.createElement("td"),l=document.createElement("div");l.className="btn-group",l.role="group";const o=document.createElement("button");o.id=`download-${e.id}`,o.type="button",o.className="btn btn-outline-light btn-sm",o.title="Download all",e.uploadedfiles==0&&o.classList.add("disabled"),o.appendChild(c("bi-download"));const s=document.createElement("button");s.id=`copy-${e.id}`,s.type="button",s.className="copyurl btn btn-outline-light btn-sm",s.title="Copy URL",s.setAttribute("data-clipboard-text",d),s.onclick=()=>showToast(1e3),s.appendChild(c("bi-copy"));const i=document.createElement("button");i.id=`edit-${e.id}`,i.type="button",i.className="btn btn-outline-light btn-sm",i.title="Edit request",i.onclick=()=>editFileRequest(e.id,e.name,e.maxfiles,e.maxsize,e.expiry,e.notes),i.appendChild(c("bi-pencil"));const a=document.createElement("button");return a.id=`delete-${e.id}`,a.type="button",a.className="btn btn-outline-danger btn-sm",a.title="Delete",a.onclick=()=>deleteOrShowModal(e.id,e.name,e.uploadedfiles),a.appendChild(c("bi-trash3")),l.append(o,s,i,a),u.appendChild(l),n.appendChild(u),n}function filterLogs(e){const t=document.getElementById("logviewer");e=="all"?t.value=logContent:t.value=logContent.split(` +const storedTokens=new Map;async function getToken(e,t){const n="./auth/token";if(!t){if(!storedTokens.has(e))return getToken(e,!0);let t=storedTokens.get(e);return t.expiry-Date.now()/1e3<60?getToken(e,!0):t.key}const s={method:"POST",headers:{"Content-Type":"application/json",permission:e}};try{const o=await fetch(n,s);if(!o.ok)throw new Error(`Request failed with status: ${o.status}`);const t=await o.json();if(!t.hasOwnProperty("key"))throw new Error(`Invalid response when trying to get token`);return storedTokens.set(e,{key:t.key,expiry:t.expiry}),t.key}catch(e){throw console.error("Error in getToken:",e),e}}async function apiAuthModify(e,t,n){const o="./api/auth/modify",i="PERM_API_MOD";let s;try{s=await getToken(i,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const a={method:"POST",headers:{"Content-Type":"application/json",apikey:s,targetKey:e,permission:t,permissionModifier:n}};try{const e=await fetch(o,a);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiAuthModify:",e),e}}async function apiAuthFriendlyName(e,t){const s="./api/auth/friendlyname",o="PERM_API_MOD";let n;try{n=await getToken(o,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const i={method:"PUT",headers:{"Content-Type":"application/json",apikey:n,targetKey:e,friendlyName:t}};try{const e=await fetch(s,i);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiAuthModify:",e),e}}async function apiAuthDelete(e){const n="./api/auth/delete",s="PERM_API_MOD";let t;try{t=await getToken(s,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const o={method:"POST",headers:{"Content-Type":"application/json",apikey:t,targetKey:e}};try{const e=await fetch(n,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiAuthDelete:",e),e}}async function apiAuthCreate(){const t="./api/auth/create",n="PERM_API_MOD";let e;try{e=await getToken(n,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const s={method:"POST",headers:{"Content-Type":"application/json",apikey:e,basicPermissions:"true"}};try{const e=await fetch(t,s);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const n=await e.json();return n}catch(e){throw console.error("Error in apiAuthCreate:",e),e}}async function apiChunkComplete(e,t,n,s,o,i,a,r,c,l){const u="./api/chunk/complete",h="PERM_UPLOAD";let d;try{d=await getToken(h,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const m={method:"POST",headers:{"Content-Type":"application/json",apikey:d,uuid:e,filename:"base64:"+Base64.encode(t),filesize:n,realsize:s,contenttype:o,allowedDownloads:i,expiryDays:a,password:r,isE2E:c,nonblocking:l}};try{const e=await fetch(u,m);if(!e.ok){let t;try{const n=await e.json();t=n.ErrorMessage||`Request failed with status: ${e.status}`}catch{const n=await e.text();t=n||`Request failed with status: ${e.status}`}throw new Error(t)}const t=await e.json();return t}catch(e){throw console.error("Error in apiChunkComplete:",e),e}}async function apiFilesReplace(e,t){const s="./api/files/replace",o="PERM_REPLACE";let n;try{n=await getToken(o,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const i={method:"PUT",headers:{"Content-Type":"application/json",id:e,apikey:n,idNewContent:t,deleteNewFile:!1}};try{const e=await fetch(s,i);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiFilesReplace:",e),e}}async function apiFilesListById(e){const n="./api/files/list/"+e,s="PERM_VIEW";let t;try{t=await getToken(s,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const o={method:"GET",headers:{"Content-Type":"application/json",apikey:t}};try{const e=await fetch(n,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiFilesListById:",e),e}}async function apiFilesListDownloadSingle(e){const n="./api/files/download/"+e,s="PERM_DOWNLOAD";let t;try{t=await getToken(s,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const o={method:"GET",headers:{"Content-Type":"application/json",apikey:t,presignUrl:!0}};try{const e=await fetch(n,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiFilesListDownloadSingle:",e),e}}async function apiFilesListDownloadZip(e,t){const s="./api/files/downloadzip",o="PERM_DOWNLOAD";let n;try{n=await getToken(o,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const i={method:"GET",headers:{"Content-Type":"application/json",apikey:n,ids:e,filename:"base64:"+Base64.encode(t),presignUrl:!0}};try{const e=await fetch(s,i);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiFilesListDownloadZip:",e),e}}async function apiFilesModify(e,t,n,s,o){const a="./api/files/modify",r="PERM_EDIT";let i;try{i=await getToken(r,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const c={method:"PUT",headers:{"Content-Type":"application/json",id:e,apikey:i,allowedDownloads:t,expiryTimestamp:n,password:s,originalPassword:o}};try{const e=await fetch(a,c);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiFilesModify:",e),e}}async function apiFilesDelete(e,t){const s="./api/files/delete",o="PERM_DELETE";let n;try{n=await getToken(o,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const i={method:"POST",headers:{"Content-Type":"application/json",apikey:n,id:e,delay:t}};try{const e=await fetch(s,i);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiFilesDelete:",e),e}}async function apiFilesRestore(e){const n="./api/files/restore",s="PERM_DELETE";let t;try{t=await getToken(s,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const o={method:"POST",headers:{"Content-Type":"application/json",apikey:t,id:e}};try{const e=await fetch(n,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiFilesRestore:",e),e}}async function apiUserCreate(e){const n="./api/user/create",s="PERM_MANAGE_USERS";let t;try{t=await getToken(s,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const o={method:"POST",headers:{"Content-Type":"application/json",apikey:t,username:e}};try{const e=await fetch(n,o);if(!e.ok)throw e.status==409?new Error("duplicate"):new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiUserModify:",e),e}}async function apiUserModify(e,t,n){const o="./api/user/modify",i="PERM_MANAGE_USERS";let s;try{s=await getToken(i,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const a={method:"POST",headers:{"Content-Type":"application/json",apikey:s,userid:e,userpermission:t,permissionModifier:n}};try{const e=await fetch(o,a);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiUserModify:",e),e}}async function apiUserChangeRank(e,t){const s="./api/user/changeRank",o="PERM_MANAGE_USERS";let n;try{n=await getToken(o,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const i={method:"POST",headers:{"Content-Type":"application/json",apikey:n,userid:e,newRank:t}};try{const e=await fetch(s,i);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiUserModify:",e),e}}async function apiUserDelete(e,t){const s="./api/user/delete",o="PERM_MANAGE_USERS";let n;try{n=await getToken(o,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const i={method:"POST",headers:{"Content-Type":"application/json",apikey:n,userid:e,deleteFiles:t}};try{const e=await fetch(s,i);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiUserDelete:",e),e}}async function apiUserResetPassword(e,t){const s="./api/user/resetPassword",o="PERM_MANAGE_USERS";let n;try{n=await getToken(o,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const i={method:"POST",headers:{"Content-Type":"application/json",apikey:n,userid:e,generateNewPassword:t}};try{const e=await fetch(s,i);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiUserResetPassword:",e),e}}async function apiLogSystemStatus(){const t="./api/logs/systemStatus",n="PERM_MANAGE_LOGS";let e;try{e=await getToken(n,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const s={method:"GET",headers:{"Content-Type":"application/json",apikey:e}};try{const e=await fetch(t,s);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const n=await e.json();return n}catch(e){throw console.error("Error in apiLogSystemStatus:",e),e}}async function apiLogResetTraffic(){const t="./api/logs/resetTraffic",n="PERM_MANAGE_LOGS";let e;try{e=await getToken(n,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const s={method:"GET",headers:{"Content-Type":"application/json",apikey:e}};try{const e=await fetch(t,s);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiLogResetTraffic:",e),e}}async function apiLogGet(e){const n="./api/logs/get",s="PERM_MANAGE_LOGS";let t;try{t=await getToken(s,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const o={method:"GET",headers:{"Content-Type":"application/json",apikey:t,timestamp:e}};try{const e=await fetch(n,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiLogGet:",e),e}}async function apiLogsDelete(e){const n="./api/logs/delete",s="PERM_MANAGE_LOGS";let t;try{t=await getToken(s,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const o={method:"POST",headers:{"Content-Type":"application/json",apikey:t,timestamp:e}};try{const e=await fetch(n,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiLogsDelete:",e),e}}async function apiE2eGet(){const t="./api/e2e/get",n="PERM_UPLOAD";let e;try{e=await getToken(n,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const s={method:"POST",headers:{"Content-Type":"application/json",apikey:e}};try{const e=await fetch(t,s);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);return await e.text()}catch(e){throw console.error("Error in apiE2eGet:",e),e}}async function apiE2eMutexLockUnlock(e){let t="./api/e2e/mutex/lock";e&&(t="./api/e2e/mutex/unlock");const s="PERM_UPLOAD";let n;try{n=await getToken(s,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const o={method:"GET",headers:{"Content-Type":"application/json",apikey:n}};try{const e=await fetch(t,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);return await e.text()}catch(e){throw console.error("Error in apiE2eMutexLock:",e),e}}async function apiE2eStore(e){const n="./api/e2e/set",s="PERM_UPLOAD";let t;try{t=await getToken(s,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const o={method:"POST",headers:{"Content-Type":"application/json",apikey:t},body:JSON.stringify({content:e})};try{const e=await fetch(n,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiE2eStore:",e),e}}async function apiURequestDelete(e){const n="./api/uploadrequest/delete",s="PERM_MANAGE_FILE_REQUESTS";let t;try{t=await getToken(s,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const o={method:"DELETE",headers:{"Content-Type":"application/json",apikey:t,id:e}};try{const e=await fetch(n,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiURequestDelete:",e),e}}async function apiURequestSave(e,t,n,s,o,i){const r="./api/uploadrequest/save",c="PERM_MANAGE_FILE_REQUESTS";let a;try{a=await getToken(c,!1)}catch(e){throw console.error("Unable to gain permission token:",e),e}const l={method:"POST",headers:{"Content-Type":"application/json",apikey:a,id:e,name:"base64:"+Base64.encode(t),expiry:o,maxfiles:n,maxsize:s,notes:"base64:"+Base64.encode(i)}};try{const e=await fetch(r,l);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiURequestDelete:",e),e}}try{var toastId,calendarInstance,dropzoneObject,isE2EEnabled,isUploading,rowCount,sseWorkerPort,statusItemCount,zipPendingFiles,zipBatchTimer,zipStatusCounter,clipboard=new ClipboardJS(".copyurl")}catch{}function showToast(e,t){let n=document.getElementById("toastnotification");typeof t!="undefined"?n.innerText=t:n.innerText=n.dataset.default,n.classList.add("show"),clearTimeout(toastId),toastId=setTimeout(()=>{hideToast()},e)}function hideToast(){document.getElementById("toastnotification").classList.remove("show")}calendarInstance=null;function createCalendar(e,t){const n=new Date(t*1e3);calendarInstance=flatpickr(document.getElementById(e),{enableTime:!0,dateFormat:"U",altInput:!0,altFormat:"Y-m-d H:i",allowInput:!0,time_24hr:!0,defaultDate:n,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 downloadFileWithPresign(e){apiFilesListDownloadSingle(e).then(e=>{if(!e.hasOwnProperty("downloadUrl"))throw new Error("Unable to get presigned key");const t=document.createElement("a");t.href=e.downloadUrl,t.style.display="none",document.body.appendChild(t),t.click(),t.remove()}).catch(e=>{alert("Unable to download: "+e),console.error("Error:",e)})}function downloadFilesZipWithPresign(e,t){apiFilesListDownloadZip(e,t).then(e=>{if(!e.hasOwnProperty("downloadUrl"))throw new Error("Unable to get presigned key");const t=document.createElement("a");t.href=e.downloadUrl,t.style.display="none",document.body.appendChild(t),t.click(),t.remove()}).catch(e=>{alert("Unable to download: "+e),console.error("Error:",e)})}function doLogout(){typeof sseWorkerPort!="undefined"&&sseWorkerPort!==null&&sseWorkerPort.postMessage({type:"shutdown"}),window.location.href="./logout"}function changeApiPermission(e,t,n){var o,i,s=document.getElementById(n);if(s.classList.contains("perm-processing")||s.classList.contains("perm-nochange"))return;o=s.classList.contains("perm-granted"),s.classList.add("perm-processing"),s.classList.remove("perm-granted"),s.classList.remove("perm-notgranted"),i="GRANT",o&&(i="REVOKE"),apiAuthModify(e,t,i).then(e=>{o?(s.classList.add("perm-notgranted"),s.classList.add("perm-nownotgranted")):(s.classList.add("perm-granted"),s.classList.add("perm-nowgranted")),s.classList.remove("perm-processing"),setTimeout(()=>{s.classList.remove("perm-nowgranted"),s.classList.remove("perm-nownotgranted")},1e3)}).catch(e=>{o?s.classList.add("perm-granted"):s.classList.add("perm-notgranted"),s.classList.remove("perm-processing"),alert("Unable to set permission: "+e),console.error("Error:",e)})}function deleteApiKey(e){document.getElementById("delete-"+e).disabled=!0,apiAuthDelete(e).then(t=>{document.getElementById("row-"+e).classList.add("rowDeleting"),setTimeout(()=>{document.getElementById("row-"+e).remove()},290)}).catch(e=>{alert("Unable to delete API key: "+e),console.error("Error:",e)})}function newApiKey(){document.getElementById("button-newapi").disabled=!0,apiAuthCreate().then(e=>{addRowApi(e.Id,e.PublicId),document.getElementById("button-newapi").disabled=!1}).catch(e=>{alert("Unable to create API key: "+e),console.error("Error:",e)})}function addFriendlyNameChange(e){let t=document.getElementById("friendlyname-"+e);if(t.classList.contains("isBeingEdited"))return;t.classList.add("isBeingEdited");let i=t.innerText,n=document.createElement("input");n.size=5,n.value=i;let s=!0,o=function(){if(!s)return;s=!1;let o=n.value;o==""&&(o="Unnamed key"),t.innerText=o,t.classList.remove("isBeingEdited"),apiAuthFriendlyName(e,o).catch(e=>{alert("Unable to save name: "+e),console.error("Error:",e)})};n.onblur=o,n.addEventListener("keyup",function(e){e.keyCode===13&&(e.preventDefault(),o())}),t.innerText="",t.appendChild(n),n.focus()}function addRowApi(e,t){let p=document.getElementById("apitable"),s=p.insertRow(0);s.id="row-"+t;let i=0,c=s.insertCell(i++),l=s.insertCell(i++),d=s.insertCell(i++),a=s.insertCell(i++),u;canViewOtherApiKeys&&(u=s.insertCell(i++));let h=s.insertCell(i++);canViewOtherApiKeys&&(u.classList.add("newApiKey"),u.innerText=userName),c.classList.add("newApiKey"),l.classList.add("newApiKey"),d.classList.add("newApiKey"),a.classList.add("newApiKey"),a.classList.add("prevent-select"),h.classList.add("newApiKey"),c.innerText="Unnamed key",c.id="friendlyname-"+t,c.onclick=function(){addFriendlyNameChange(t)},l.innerText=e,l.classList.add("font-monospace"),d.innerText="Never";const r=document.createElement("div");r.className="btn-group",r.setAttribute("role","group");const n=document.createElement("button");n.type="button",n.dataset.clipboardText=e,n.title="Copy API Key",n.className="copyurl btn btn-outline-light btn-sm",n.setAttribute("onclick","showToast(1000)");const m=document.createElement("i");m.className="bi bi-copy",n.appendChild(m);const o=document.createElement("button");o.type="button",o.id=`delete-${t}`,o.title="Delete",o.className="btn btn-outline-danger btn-sm",o.setAttribute("onclick",`deleteApiKey('${t}')`);const f=document.createElement("i");f.className="bi bi-trash3",o.appendChild(f),r.appendChild(n),r.appendChild(o),h.appendChild(r);const g=[{perm:"PERM_VIEW",icon:"bi-eye",granted:!0,title:"List Uploads"},{perm:"PERM_UPLOAD",icon:"bi-file-earmark-plus",granted:!0,title:"Upload"},{perm:"PERM_EDIT",icon:"bi-pencil",granted:!0,title:"Edit Uploads"},{perm:"PERM_DELETE",icon:"bi-trash3",granted:!0,title:"Delete Uploads"},{perm:"PERM_REPLACE",icon:"bi-recycle",granted:!1,title:"Replace Uploads"},{perm:"PERM_DOWNLOAD",icon:"bi-box-arrow-in-down",granted:!1,title:"Download Files"},{perm:"PERM_MANAGE_FILE_REQUESTS",icon:"bi-file-earmark-arrow-up",granted:!1,title:"Manage File Requests"},{perm:"PERM_MANAGE_USERS",icon:"bi-people",granted:!1,title:"Manage Users"},{perm:"PERM_MANAGE_LOGS",icon:"bi-card-list",granted:!1,title:"Manage System Logs"},{perm:"PERM_API_MOD",icon:"bi-sliders2",granted:!1,title:"Manage API Keys"}];if(g.forEach(({perm:e,icon:n,granted:s,title:o})=>{const i=document.createElement("i"),r=`${e.toLowerCase()}_${t}`;i.id=r,i.className=`bi ${n} ${s?"perm-granted":"perm-notgranted"}`,i.title=o,i.setAttribute("onclick",`changeApiPermission("${t}","${e}", "${r}");`),a.appendChild(i),a.appendChild(document.createTextNode(" "))}),!canReplaceFiles){let e=document.getElementById("perm_replace_"+t);e.classList.add("perm-unavailable"),e.classList.add("perm-nochange")}if(!canManageUsers){let e=document.getElementById("perm_manage_users_"+t);e.classList.add("perm-unavailable"),e.classList.add("perm-nochange")}if(!canViewSystemLog){let e=document.getElementById("perm_manage_logs_"+t);e.classList.add("perm-unavailable"),e.classList.add("perm-nochange")}if(!canCreateFileRequest){let e=document.getElementById("perm_manage_file_requests_"+t);e.classList.add("perm-unavailable"),e.classList.add("perm-nochange")}setTimeout(()=>{c.classList.remove("newApiKey"),l.classList.remove("newApiKey"),d.classList.remove("newApiKey"),a.classList.remove("newApiKey"),h.classList.remove("newApiKey")},700)}function deleteFileRequest(e){document.getElementById("delete-"+e).disabled=!0,apiURequestDelete(e).then(t=>{const s=document.getElementById("row-"+e),n=document.getElementById("filelist-"+e);s.classList.add("rowDeleting"),n!==null&&n.classList.add("rowDeleting"),setTimeout(()=>{s.remove(),n!==null&&n.remove()},290)}).catch(e=>{alert("Unable to delete file request: "+e),console.error("Error:",e)})}function deleteOrShowModal(e,t,n){n===0?deleteFileRequest(e):showDeleteFRequestModal(e,t,n)}function deleteFileFr(e,t){document.getElementById("button-delete-"+e).disabled=!0;let n=document.getElementById("cell-listupload-"+e);apiFilesDelete(e,10).then(s=>{changeFileCountFr(t,-1),removeDownloadFileReference(e,t),n.classList.add("rowDeleting"),setTimeout(()=>{n.remove()},290),showToastFileDeletionFr(e)}).catch(e=>{alert("Unable to delete file: "+e),console.error("Error:",e)})}function changeFileCountFr(e,t){let n=document.getElementById("totalFiles-fr-"+e),s=Number(n.innerText)||0,o=s+t;n.innerText=o}function removeDownloadFileReference(e,t){const n=document.getElementById(`download-${t}`);if(!n)return;const a=n.getAttribute("onclick")||"",o=a.match(/downloadFilesZipWithPresign\('([^']*)',\s*'([^']*)'\)/),r=a.match(/downloadFileWithPresign\('([^']*)'\)/);let s=[],i="";o?(s=o[1].split(",").filter(e=>e!==""),i=o[2]):r&&(s=[r[1]],i=n.dataset.recordName||""),s=s.filter(t=>t!==e),s.length===0?(n.classList.add("disabled"),n.removeAttribute("onclick")):s.length===1?(n.classList.remove("disabled"),n.setAttribute("onclick",`downloadFileWithPresign('${s[0]}');`)):(n.classList.remove("disabled"),n.setAttribute("onclick",`downloadFilesZipWithPresign('${s.join(",")}', '${i}');`))}function showToastFileDeletionFr(e){let t=document.getElementById("toastnotificationUndo"),n=document.getElementById("cell-name-"+e).innerText,s=document.getElementById("toastFilename"),o=document.getElementById("toastUndoButton");s.innerText=n,o.dataset.fileid=e,hideToast(),t.classList.add("show"),clearTimeout(toastId),toastId=setTimeout(()=>{hideFileToast()},5e3)}function handleUndoFr(e){hideFileToast(),apiFilesRestore(e.dataset.fileid).then(e=>{window.location.reload()}).catch(e=>{alert("Unable to restore file: "+e),console.error("Error:",e)})}function showDeleteFRequestModal(e,t,n){document.getElementById("deleteModalBodyName").innerText=t,document.getElementById("deleteModalBodyCount").innerText=n,$("#deleteModal").modal("show"),document.getElementById("buttonDelete").onclick=function(){$("#deleteModal").modal("hide"),deleteFileRequest(e)}}function newFileRequest(){loadFileRequestDefaults(),document.getElementById("m_urequestlabel").innerText="New File Request",$("#addEditModal").modal("show"),document.getElementById("b_fr_save").onclick=function(){saveFileRequestDefaults(),saveFileRequest(),$("#addEditModal").modal("hide")}}function saveFileRequestDefaults(){if(document.getElementById("mc_maxfiles").checked?localStorage.setItem("fr_maxfiles",document.getElementById("mi_maxfiles").value):localStorage.setItem("fr_maxfiles",0),document.getElementById("mc_maxsize").checked?localStorage.setItem("fr_maxsize",document.getElementById("mi_maxsize").value):localStorage.setItem("fr_maxsize",0),document.getElementById("mc_expiry").checked){let e=document.getElementById("mi_expiry").value-Math.round(Date.now()/1e3);localStorage.setItem("fr_expiry",e)}else localStorage.setItem("fr_expiry",0)}function loadFileRequestDefaults(){const t=localStorage.getItem("fr_maxfiles"),n=localStorage.getItem("fr_maxsize");let e=localStorage.getItem("fr_expiry");if(e!=="0"&&e!==null){let t=new Date(Date.now()+Number(e*1e3));t.setHours(12,0,0,0),e=Math.floor(t.getTime()/1e3)}setModalValues("","",t,n,e,"")}function setModalValues(e,t,n,s,o,i){if(document.getElementById("freqId").value=e,t===null?document.getElementById("mFriendlyName").value="":document.getElementById("mFriendlyName").value=t,limitMaxFiles!=0){let e=document.getElementById("mc_maxfiles");(n===null||n==0)&&(n=limitMaxFiles),e.checked=!0,e.disabled=!0,e.title="Only admins can set this to unlimited",e.value="1",document.getElementById("mi_maxfiles").setAttribute("max",limitMaxFiles)}else{let e=document.getElementById("mc_maxfiles");e.disabled=!1,e.title="",document.getElementById("mi_maxfiles").setAttribute("max","")}if(limitMaxSize!=0){let e=document.getElementById("mc_maxsize");(s===null||s==0)&&(s=limitMaxSize),e.checked=!0,e.disabled=!0,e.title="Only admins can set this to unlimited",e.value="1",document.getElementById("mi_maxsize").setAttribute("max",limitMaxSize)}else{let e=document.getElementById("mc_maxsize");e.disabled=!1,e.title="",document.getElementById("mi_maxsize").setAttribute("max","")}if(n===null||n==0?(document.getElementById("mi_maxfiles").value="1",document.getElementById("mi_maxfiles").disabled=!0,document.getElementById("mc_maxfiles").checked=!1):(document.getElementById("mi_maxfiles").value=n,document.getElementById("mi_maxfiles").disabled=!1,document.getElementById("mc_maxfiles").checked=!0),s===null||s==0?(document.getElementById("mi_maxsize").value="10",document.getElementById("mi_maxsize").disabled=!0,document.getElementById("mc_maxsize").checked=!1):(document.getElementById("mi_maxsize").value=s,document.getElementById("mi_maxsize").disabled=!1,document.getElementById("mc_maxsize").checked=!0),o===null||o==0){const e=Math.floor(new Date(Date.now()+14*24*60*60*1e3).getTime()/1e3);document.getElementById("mi_expiry").disabled=!0,document.getElementById("mc_expiry").checked=!1,document.getElementById("mi_expiry").value=e,createCalendar("mi_expiry",e)}else document.getElementById("mi_expiry").value=o,document.getElementById("mi_expiry").disabled=!1,document.getElementById("mc_expiry").checked=!0,createCalendar("mi_expiry",o);document.getElementById("mNotes").value=i}function editFileRequest(e,t,n,s,o,i){setModalValues(e,t,n,s,o,i),document.getElementById("m_urequestlabel").innerText="Edit File Request",$("#addEditModal").modal("show"),document.getElementById("b_fr_save").onclick=function(){saveFileRequest(),$("#addEditModal").modal("hide")}}function saveFileRequest(){const s=document.getElementById("b_fr_save"),o=document.getElementById("freqId").value,i=document.getElementById("mFriendlyName").value,a=document.getElementById("mNotes").value;let e=0,t=0,n=0;document.getElementById("mc_maxfiles").checked&&(e=document.getElementById("mi_maxfiles").value),document.getElementById("mc_maxsize").checked&&(t=document.getElementById("mi_maxsize").value),document.getElementById("mc_expiry").checked&&(n=document.getElementById("mi_expiry").value),s.disabled=!0,apiURequestSave(o,i,e,t,n,a).then(e=>{document.getElementById("b_fr_save").disabled=!1,insertOrReplaceFileRequest(e)}).catch(e=>{alert("Unable to save file request: "+e),console.error("Error:",e),document.getElementById("b_fr_save").disabled=!1})}function checkMaxNumber(e){if(e.value==""){e.value="1";return}let t=e.getAttribute("max");if(t=="")return;e.value>t&&(e.value=t)}function insertOrReplaceFileRequest(e){const n=document.getElementById("filerequesttable");let t=document.getElementById(`row-${e.id}`);if(t){const n=document.getElementById(`cell-username-${e.id}`).innerText;t.replaceWith(createFileRequestRow(e,n))}else{let t=createFileRequestRow(e,userName);t.querySelectorAll("td").forEach(e=>{e.classList.add("newFileRequest"),setTimeout(()=>{e.classList.remove("newFileRequest")},700)}),n.prepend(t)}}function createFileRequestRow(e,t){function r(e){const t=document.createElement("td");return t.textContent=e,t}function h(e,t){const s=document.createElement("td"),n=document.createElement("a");return n.textContent=e,n.href=t,n.target="_blank",s.appendChild(n),s}function c(e){const t=document.createElement("i");return t.className=`bi ${e}`,t}const d=`${baseUrl}publicUpload?id=${e.id}&key=${e.apikey}`,n=document.createElement("tr");if(n.id=`row-${e.id}`,n.className="filerequest-item",n.appendChild(h(e.name,d)),e.maxfiles==0?n.appendChild(r(e.uploadedfiles)):n.appendChild(r(`${e.uploadedfiles} / ${e.maxfiles}`)),n.appendChild(r(getReadableSize(e.totalfilesize))),n.appendChild(r(formatTimestampWithNegative(e.lastupload,"None"))),n.appendChild(r(formatFileRequestExpiry(e.expiry))),canViewOtherRequests){let s=r(t);s.id=`cell-username-${e.id}`,n.appendChild(s)}const u=document.createElement("td"),l=document.createElement("div");l.className="btn-group",l.role="group";const o=document.createElement("button");o.id=`download-${e.id}`,o.type="button",o.className="btn btn-outline-light btn-sm",o.title="Download all",e.uploadedfiles==0&&o.classList.add("disabled"),o.appendChild(c("bi-download"));const s=document.createElement("button");s.id=`copy-${e.id}`,s.type="button",s.className="copyurl btn btn-outline-light btn-sm",s.title="Copy URL",s.setAttribute("data-clipboard-text",d),s.onclick=()=>showToast(1e3),s.appendChild(c("bi-copy"));const i=document.createElement("button");i.id=`edit-${e.id}`,i.type="button",i.className="btn btn-outline-light btn-sm",i.title="Edit request",i.onclick=()=>editFileRequest(e.id,e.name,e.maxfiles,e.maxsize,e.expiry,e.notes),i.appendChild(c("bi-pencil"));const a=document.createElement("button");return a.id=`delete-${e.id}`,a.type="button",a.className="btn btn-outline-danger btn-sm",a.title="Delete",a.onclick=()=>deleteOrShowModal(e.id,e.name,e.uploadedfiles),a.appendChild(c("bi-trash3")),l.append(o,s,i,a),u.appendChild(l),n.appendChild(u),n}function filterLogs(e){const t=document.getElementById("logviewer");e=="all"?t.value=logContent:t.value=logContent.split(` `).filter(t=>t.includes("["+e+"]")).join(` -`),t.scrollTop=t.scrollHeight}function setTrafficInfo(e,t){insertReadableSizeTwoOutputs(e,"totalTraffic","totalTrafficUnit"),document.getElementById("cardTraffic").title="Traffic since "+formatUnixTimestamp(t)}function setMemoryUsage(e,t){insertReadableSizeTwoOutputs(t,"totalMemory","memoryUnit");let n=document.getElementById("memoryUnit").innerText;insertReadableSizeForcedUnit(e,"usedMemory",n)}function setDiskUsage(e,t){insertReadableSizeTwoOutputs(t,"totalDisk","diskUnit");let n=document.getElementById("diskUnit").innerText;insertReadableSizeForcedUnit(e,"usedDisk",n)}function formatDuration(e){const t=[{label:"y",value:31536e3},{label:"d",value:86400},{label:"h",value:3600},{label:"m",value:60},{label:"s",value:1}];let n=t.findIndex(t=>e>=t.value);(n===-1||t[n].label==="s")&&(n=t.findIndex(e=>e.label==="m"));const s=t[n],o=t[n+1],i=Math.floor(e/s.value),a=e%s.value,r=Math.floor(a/o.value);return`${i}${s.label} ${r}${o.label}`}function addUptime(){if(currentUptime>3600)return;setTimeout(()=>{++currentUptime,document.getElementById("uptime").innerText=formatDuration(currentUptime),addUptime()},1e3)}function setPercentageBar(e,t,n){let o=t;n!==0[0]&&(o=t/n*100);const s=document.getElementById(e);s.classList.remove("bg-success"),s.classList.remove("bg-warning"),s.classList.remove("bg-danger"),o<70&&s.classList.add("bg-success"),o>=70&&o<90&&s.classList.add("bg-warning"),o>=90&&s.classList.add("bg-danger"),s.style.width=o+"%"}async function loadLogs(e){const t=document.getElementById("logviewer");try{const n=await apiLogGet(e);lastLogUpdate=n.timestamp;let s=!0;if(e!=0){if(n.logEntries=="")return;s=allowScroll(),logContent=logContent+n.logEntries}else logContent=n.logEntries;filterLogs(document.getElementById("logFilter").value),s&&(t.scrollTop=t.scrollHeight)}catch(e){lastLogUpdate=0,console.error("Failed to load logs:",e),t.value="Error loading logs. See console for details."}}async function loadStatus(){try{const e=await apiLogSystemStatus();currentUptime=e.uptime,document.getElementById("labelCpu").innerText=e.cpuLoad+"%",document.getElementById("labelActiveFiles").innerText=e.activeFiles,setPercentageBar("barCpu",e.cpuLoad),setPercentageBar("barDisk",e.diskUsagePercentage),setPercentageBar("barMemory",e.memoryUsagePercentage),setMemoryUsage(e.memoryUsed,e.memoryTotal),setDiskUsage(e.diskUsed,e.diskTotal),setTrafficInfo(e.dataServed,e.trafficRecordingSince)}catch(e){console.error("Failed to server status:",e)}}async function pollInfo(){for(firstStart=!0;!0;)await loadLogs(lastLogUpdate),firstStart?firstStart=!1:await loadStatus(),await new Promise(e=>setTimeout(e,POLL_INTERVAL_S*1e3))}function allowScroll(){const e=document.getElementById("logviewer");return e.scrollTop+e.clientHeight>=e.scrollHeight-5}function deleteLogs(){const n=document.getElementById("deleteLogsSel");if(!n)return;const t=n.value;if(t=="none"||t=="")return;if(!confirm("Do you want to delete the selected logs?")){document.getElementById("deleteLogs").selectedIndex=0;return}let e=Math.floor(Date.now()/1e3);switch(t){case"all":e=0;break;case"2":e=e-2*24*60*60;break;case"7":e=e-7*24*60*60;break;case"14":e=e-14*24*60*60;break;case"30":e=e-30*24*60*60;break;default:return}apiLogsDelete(e).then(e=>{location.reload()}).catch(e=>{alert("Unable to delete logs: "+e),console.error("Error:",e)})}function resetTrafficStat(){if(!confirm("Do you want to reset the traffic statistics?"))return;apiLogResetTraffic().then(e=>{location.reload()}).catch(e=>{alert("Unable to reset stats: "+e),console.error("Error:",e)})}isE2EEnabled=!1,isUploading=!1,rowCount=-1;function initDropzone(){Dropzone.options.uploaddropzone={paramName:"file",dictDefaultMessage:"",createImageThumbnails:!1,chunksUploaded:function(e,t){sendChunkComplete(e,t)},init:function(){dropzoneObject=this,this.on("addedfile",e=>{e.upload.uuid=getUuid(),saveUploadDefaults(),addFileProgress(e)}),this.on("queuecomplete",function(){isUploading=!1}),this.on("sending",function(){isUploading=!0}),this.on("error",function(e,t,n){if(console.log(t),n){if(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.");return}try{console.log(n),errInfo=JSON.parse(n.responseText),showError(e,"Error: "+errInfo.ErrorMessage)}catch{showError(e,"Error: "+n.responseText)}}else showError(e,"Error: "+t)}),this.on("uploadprogress",function(e,t,n){updateProgressbar(e,t,n)}),isE2EEnabled&&(dropzoneObject.disable(),setE2eUpload())}},document.onpaste=function(e){if(dropzoneObject.disabled)return;const n=document.activeElement;if(n&&(n.hasAttribute("data-allow-regular-paste")||n.hasAttribute("placeholder")))return;var t,s=(e.clipboardData||e.originalEvent.clipboardData).items;for(let e in s)t=s[e],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)}})},window.addEventListener("beforeunload",e=>{isUploading&&(e.returnValue="Upload is still in progress. Do you want to close this page?")})}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 addFileProgress(e){addFileStatus(e.upload.uuid,e.upload.filename)}function setUploadDefaults(){let s=getLocalStorageWithDefault("defaultDownloads",1),o=getLocalStorageWithDefault("defaultExpiry",14),e=getLocalStorageWithDefault("defaultPassword",""),t=getLocalStorageWithDefault("defaultUnlimitedDownloads",!1)==="true",n=getLocalStorageWithDefault("defaultUnlimitedTime",!1)==="true";document.getElementById("allowedDownloads").value=s,document.getElementById("expiryDays").value=o,document.getElementById("password").value=e,document.getElementById("enableDownloadLimit").checked=!t,document.getElementById("enableTimeLimit").checked=!n,e===""?(document.getElementById("enablePassword").checked=!1,document.getElementById("password").disabled=!0):(document.getElementById("enablePassword").checked=!0,document.getElementById("password").disabled=!1),t&&(document.getElementById("allowedDownloads").disabled=!0),n&&(document.getElementById("expiryDays").disabled=!0)}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(e,t){var n=localStorage.getItem(e);return n===null?t: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){let c=e.upload.uuid,n=e.name,s=e.size,l=e.size,o=e.type,i=document.getElementById("allowedDownloads").value,a=document.getElementById("expiryDays").value,d=document.getElementById("password").value,r=e.isEndToEndEncrypted===!0,u=!0;document.getElementById("enableDownloadLimit").checked||(i=0),document.getElementById("enableTimeLimit").checked||(a=0),r&&(s=e.sizeEncrypted,n="Encrypted File",o=""),apiChunkComplete(c,n,s,l,o,i,a,d,r,u).then(n=>{t();let s=document.getElementById(`us-progress-info-${e.upload.uuid}`);s!=null&&(s.innerText="In Queue...")}).catch(t=>{console.error("Error:",t),dropzoneUploadError(e,t)})}function dropzoneUploadError(e,t){e.accepted=!1,dropzoneObject._errorProcessing([e],t),showError(e,t)}function dropzoneGetFile(e){for(let t=0;t{addRow(n),notifyWorker({type:"fileAdded",item:n});let s=dropzoneGetFile(t);if(s==null)return;s.isEndToEndEncrypted===!0?apiE2eMutexLockUnlock(!1).then(()=>apiE2eGet()).then(n=>{let i=GokapiE2EInfoParse(n);if(i instanceof Error)throw i;let a=GokapiE2EAddFile(t,e,s.name);if(a instanceof Error)throw a;let o=GokapiE2EInfoEncrypt();if(o instanceof Error)throw o;return apiE2eStore(o)}).then(()=>{GokapiE2EDecryptMenu(),removeFileStatus(t)}).catch(e=>{s.accepted=!1,dropzoneObject._errorProcessing([s],e),console.error("Error:",e)}).finally(()=>{apiE2eMutexLockUnlock(!0).catch(e=>{console.error("Failed to release E2E mutex after write: "+e)})}):removeFileStatus(t)}).catch(e=>{let n=dropzoneGetFile(t);n!=null&&dropzoneUploadError(n,e),console.error("Error:",e)})}function parseProgressStatus(e){let n=document.getElementById(`us-container-${e.chunk_id}`);if(n==null)return;n.setAttribute("data-complete","true");let t;switch(e.upload_status){case 0:t="Processing file...";break;case 1:t="Saving file...";break;case 2:t="Finalising...",requestFileInfo(e.file_id,e.chunk_id);break;case 3:t="Error";let n=dropzoneGetFile(e.chunk_id);e.error_message==""&&(e.error_message="Server Error"),n!=null&&dropzoneUploadError(n,e.error_message);return;default:t="Unknown status";break}document.getElementById(`us-progress-info-${e.chunk_id}`).innerText=t}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 s=e.getAttribute("data-fileid"),o=document.getElementById("mi_edit_down").value,i=document.getElementById("mi_edit_expiry").value,t=document.getElementById("mi_edit_pw").value,a=t==="(unchanged)";document.getElementById("mc_download").checked||(o=0),document.getElementById("mc_expiry").checked||(i=0),document.getElementById("mc_password").checked||(a=!1,t="");let r=!1,n="";document.getElementById("mc_replace").checked&&(n=document.getElementById("mi_edit_replace").value,r=n!=""),apiFilesModify(s,o,i,t,a).then(t=>{if(!r){location.reload();return}apiFilesReplace(s,n).then(e=>{location.reload()}).catch(t=>{alert("Unable to edit file: "+t),console.error("Error:",t),e.disabled=!1})}).catch(t=>{alert("Unable to edit file: "+t),console.error("Error:",t),e.disabled=!1})}function showEditModal(e,t,n,s,o,i,a,r,c){let d=$("#modaledit").clone();$("#modaledit").on("hide.bs.modal",function(){$("#modaledit").remove();let e=d.clone();$("body").append(e)}),document.getElementById("m_filenamelabel").innerText=e,document.getElementById("mc_expiry").setAttribute("data-timestamp",s),document.getElementById("mb_save").setAttribute("data-fileid",t),createCalendar("mi_edit_expiry",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);let l=document.getElementById("mi_edit_replace");if(c)if(document.getElementById("replaceGroup").style.display="flex",r)document.getElementById("mc_replace").disabled=!0,document.getElementById("mc_replace").title="Replacing content is not available for end-to-end encrypted files",l.add(new Option("Unavailable",0)),l.title="Replacing content is not available for end-to-end encrypted files",l.value="0";else{let e=getAllAvailableFiles();for(let n=0;n{changeRowCount(!1,document.getElementById("row-"+e)),showToastFileDeletion(e),notifyWorker({type:"fileDeleted",id:e})}).catch(e=>{alert("Unable to delete file: "+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 parseSseData(e){let t;try{t=JSON.parse(e)}catch(e){console.error("Failed to parse event data:",e);return}switch(t.event){case"download":setNewDownloadCount(t.file_id,t.download_count,t.downloads_remaining);return;case"uploadStatus":parseProgressStatus(t);return;default:console.error("Unknown event",t)}}function setNewDownloadCount(e,t,n){let s=document.getElementById("cell-downloads-"+e);if(s!=null&&(s.innerText=t,s.classList.add("updatedDownloadCount"),setTimeout(()=>s.classList.remove("updatedDownloadCount"),500)),n!=-1){let t=document.getElementById("cell-downloadsRemaining-"+e);t!=null&&(t.innerText=n,t.classList.add("updatedDownloadCount"),setTimeout(()=>t.classList.remove("updatedDownloadCount"),500))}}sseWorkerPort=null;function notifyWorker(e){sseWorkerPort!==null&&sseWorkerPort.postMessage(e)}function registerChangeHandler(){if(typeof SharedWorker!="undefined")try{const e=new SharedWorker("./js/sse-worker.js");e.port.onmessage=e=>{if(e.data.type==="message")parseSseData(e.data.data);else if(e.data.type==="error")console.error("SSE worker connection error:",e.data.detail);else if(e.data.type==="shutdown")setTimeout(function(){window.location.href="./login"},1e3);else if(e.data.type==="fileAdded")document.getElementById("row-"+sanitizeId(e.data.item.Id))==null&&addRow(e.data.item);else if(e.data.type==="fileDeleted"){let t=document.getElementById("row-"+sanitizeId(e.data.id));t!=null&&changeRowCount(!1,t)}else if(e.data.type==="log"){const{level:t,message:n,detail:s}=e.data;s?console[t](n,s):console[t](n)}},e.onerror=e=>{console.warn("SharedWorker failed, falling back to direct SSE:",e),sseWorkerPort=null,_registerDirectSSE()},e.port.start(),sseWorkerPort=e.port;return}catch(e){console.warn("SharedWorker unavailable, falling back to direct SSE:",e)}_registerDirectSSE()}function _registerDirectSSE(){const e=new EventSource("./uploadStatus");e.onmessage=e=>{parseSseData(e.data)},e.onerror=t=>{t.target.readyState!==EventSource.CLOSED&&e.close(),console.log("Reconnecting to SSE (direct)..."),setTimeout(_registerDirectSSE,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 d=document.getElementById("downloadtable"),t=d.insertRow(0);e.Id=sanitizeId(e.Id),t.id="row-"+e.Id;let i=t.insertCell(0),a=t.insertCell(1),s=t.insertCell(2),r=t.insertCell(3),c=t.insertCell(4),o=t.insertCell(5),l=t.insertCell(6);i.innerText=e.Name,i.id="cell-name-"+e.Id,c.id="cell-downloads-"+e.Id,a.innerText=e.Size,e.UnlimitedDownloads?s.innerText="Unlimited":(s.innerText=e.DownloadsRemaining,s.id="cell-downloadsRemaining-"+e.Id),e.UnlimitedTime?r.innerText="Unlimited":r.innerText=formatUnixTimestamp(e.ExpireAt),c.innerText=e.DownloadCount;const n=document.createElement("a");if(n.href=e.UrlDownload,n.target="_blank",n.style.color="inherit",n.id="url-href-"+e.Id,n.textContent=e.Id,o.appendChild(n),e.IsPasswordProtected===!0){const e=document.createElement("i");e.className="bi bi-key",e.title="Password protected",o.appendChild(document.createTextNode(" ")),o.appendChild(e)}return l.appendChild(createButtonGroup(e)),i.classList.add("newItem"),a.classList.add("newItem"),s.classList.add("newItem"),r.classList.add("newItem"),c.classList.add("newItem"),o.classList.add("newItem"),l.classList.add("newItem"),a.setAttribute("data-order",e.SizeBytes),changeRowCount(!0,t),e.Id}function createButtonGroup(e){const m=document.createElement("div");m.className="btn-toolbar justify-content-end",m.setAttribute("role","toolbar");const t=document.createElement("div");t.className="btn-group me-2",t.setAttribute("role","group");const n=document.createElement("button");n.type="button",n.className="copyurl btn btn-outline-light btn-sm",n.dataset.clipboardText=e.UrlDownload,n.id="url-button-"+e.Id,n.title="Copy URL";const _=document.createElement("i");_.className="bi bi-copy",n.appendChild(_),n.appendChild(document.createTextNode(" URL")),n.addEventListener("click",()=>{showToast(1e3)}),t.appendChild(n);const f=document.createElement("button");f.type="button",f.className="btn btn-outline-light btn-sm dropdown-toggle dropdown-toggle-split",f.setAttribute("data-bs-toggle","dropdown"),f.setAttribute("aria-expanded","false"),t.appendChild(f);const p=document.createElement("ul");p.className="dropdown-menu dropdown-menu-end",p.setAttribute("data-bs-theme","dark");const b=document.createElement("li"),s=document.createElement("a");e.UrlHotlink!==""?(s.className="dropdown-item copyurl",s.title="Copy hotlink",s.style.cursor="pointer",s.setAttribute("data-clipboard-text",e.UrlHotlink),s.onclick=()=>showToast(1e3),s.innerHTML=` Hotlink`):(s.className="dropdown-item",s.innerText="Hotlink not available"),b.appendChild(s),p.appendChild(b),t.appendChild(p);const d=document.createElement("button");d.type="button",d.className="btn btn-outline-light btn-sm",d.title="Share",d.onclick=()=>shareUrl(event,e.Id),d.innerHTML=` +`),t.scrollTop=t.scrollHeight}function setTrafficInfo(e,t){insertReadableSizeTwoOutputs(e,"totalTraffic","totalTrafficUnit"),document.getElementById("cardTraffic").title="Traffic since "+formatUnixTimestamp(t)}function setMemoryUsage(e,t){insertReadableSizeTwoOutputs(t,"totalMemory","memoryUnit");let n=document.getElementById("memoryUnit").innerText;insertReadableSizeForcedUnit(e,"usedMemory",n)}function setDiskUsage(e,t){insertReadableSizeTwoOutputs(t,"totalDisk","diskUnit");let n=document.getElementById("diskUnit").innerText;insertReadableSizeForcedUnit(e,"usedDisk",n)}function formatDuration(e){const t=[{label:"y",value:31536e3},{label:"d",value:86400},{label:"h",value:3600},{label:"m",value:60},{label:"s",value:1}];let n=t.findIndex(t=>e>=t.value);(n===-1||t[n].label==="s")&&(n=t.findIndex(e=>e.label==="m"));const s=t[n],o=t[n+1],i=Math.floor(e/s.value),a=e%s.value,r=Math.floor(a/o.value);return`${i}${s.label} ${r}${o.label}`}function addUptime(){if(currentUptime>3600)return;setTimeout(()=>{currentUptime=currentUptime+1,document.getElementById("uptime").innerText=formatDuration(currentUptime),addUptime()},1e3)}function setPercentageBar(e,t,n){let o=t;n!==void 0&&(o=t/n*100);const s=document.getElementById(e);s.classList.remove("bg-success"),s.classList.remove("bg-warning"),s.classList.remove("bg-danger"),o<70&&s.classList.add("bg-success"),o>=70&&o<90&&s.classList.add("bg-warning"),o>=90&&s.classList.add("bg-danger"),s.style.width=o+"%"}async function loadLogs(e){const t=document.getElementById("logviewer");try{const n=await apiLogGet(e);lastLogUpdate=n.timestamp;let s=!0;if(e!=0){if(n.logEntries=="")return;s=allowScroll(),logContent=logContent+n.logEntries}else logContent=n.logEntries;filterLogs(document.getElementById("logFilter").value),s&&(t.scrollTop=t.scrollHeight)}catch(e){lastLogUpdate=0,console.error("Failed to load logs:",e),t.value="Error loading logs. See console for details."}}async function loadStatus(){try{const e=await apiLogSystemStatus();currentUptime=e.uptime,document.getElementById("labelCpu").innerText=e.cpuLoad+"%",document.getElementById("labelActiveFiles").innerText=e.activeFiles,setPercentageBar("barCpu",e.cpuLoad),setPercentageBar("barDisk",e.diskUsagePercentage),setPercentageBar("barMemory",e.memoryUsagePercentage),setMemoryUsage(e.memoryUsed,e.memoryTotal),setDiskUsage(e.diskUsed,e.diskTotal),setTrafficInfo(e.dataServed,e.trafficRecordingSince)}catch(e){console.error("Failed to server status:",e)}}async function pollInfo(){for(firstStart=!0;!0;)await loadLogs(lastLogUpdate),firstStart?firstStart=!1:await loadStatus(),await new Promise(e=>setTimeout(e,POLL_INTERVAL_S*1e3))}function allowScroll(){const e=document.getElementById("logviewer");return e.scrollTop+e.clientHeight>=e.scrollHeight-5}function deleteLogs(){const n=document.getElementById("deleteLogsSel");if(!n)return;const t=n.value;if(t=="none"||t=="")return;if(!confirm("Do you want to delete the selected logs?")){document.getElementById("deleteLogs").selectedIndex=0;return}let e=Math.floor(Date.now()/1e3);switch(t){case"all":e=0;break;case"2":e=e-2*24*60*60;break;case"7":e=e-7*24*60*60;break;case"14":e=e-14*24*60*60;break;case"30":e=e-30*24*60*60;break;default:return}apiLogsDelete(e).then(e=>{location.reload()}).catch(e=>{alert("Unable to delete logs: "+e),console.error("Error:",e)})}function resetTrafficStat(){if(!confirm("Do you want to reset the traffic statistics?"))return;apiLogResetTraffic().then(e=>{location.reload()}).catch(e=>{alert("Unable to reset stats: "+e),console.error("Error:",e)})}isE2EEnabled=!1,isUploading=!1,rowCount=-1;function initDropzone(){Dropzone.options.uploaddropzone={paramName:"file",dictDefaultMessage:"",createImageThumbnails:!1,chunksUploaded:function(e,t){sendChunkComplete(e,t)},init:function(){dropzoneObject=this,this.on("addedfile",e=>{if(typeof handleZipAddedFile=="function"&&handleZipAddedFile(e))return;e.upload.uuid=getUuid(),saveUploadDefaults(),addFileProgress(e)}),this.on("queuecomplete",function(){isUploading=!1}),this.on("sending",function(){isUploading=!0}),this.on("error",function(e,t,n){if(console.log(t),n){if(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.");return}try{console.log(n),errInfo=JSON.parse(n.responseText),showError(e,"Error: "+errInfo.ErrorMessage)}catch{showError(e,"Error: "+n.responseText)}}else showError(e,"Error: "+t)}),this.on("uploadprogress",function(e,t,n){updateProgressbar(e,t,n)}),isE2EEnabled&&(dropzoneObject.disable(),setE2eUpload())}},document.onpaste=function(e){if(dropzoneObject.disabled)return;const n=document.activeElement;if(n&&(n.hasAttribute("data-allow-regular-paste")||n.hasAttribute("placeholder")))return;var t,s=(e.clipboardData||e.originalEvent.clipboardData).items;for(let e in s)t=s[e],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)}})},window.addEventListener("beforeunload",e=>{isUploading&&(e.returnValue="Upload is still in progress. Do you want to close this page?")})}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 addFileProgress(e){addFileStatus(e.upload.uuid,e.upload.filename)}function setUploadDefaults(){let s=getLocalStorageWithDefault("defaultDownloads",1),o=getLocalStorageWithDefault("defaultExpiry",14),e=getLocalStorageWithDefault("defaultPassword",""),t=getLocalStorageWithDefault("defaultUnlimitedDownloads",!1)==="true",n=getLocalStorageWithDefault("defaultUnlimitedTime",!1)==="true";document.getElementById("allowedDownloads").value=s,document.getElementById("expiryDays").value=o,document.getElementById("password").value=e,document.getElementById("enableDownloadLimit").checked=!t,document.getElementById("enableTimeLimit").checked=!n,e===""?(document.getElementById("enablePassword").checked=!1,document.getElementById("password").disabled=!0):(document.getElementById("enablePassword").checked=!0,document.getElementById("password").disabled=!1),t&&(document.getElementById("allowedDownloads").disabled=!0),n&&(document.getElementById("expiryDays").disabled=!0)}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(e,t){var n=localStorage.getItem(e);return n===null?t: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){let c=e.upload.uuid,n=e.name,s=e.size,l=e.size,o=e.type,i=document.getElementById("allowedDownloads").value,a=document.getElementById("expiryDays").value,d=document.getElementById("password").value,r=e.isEndToEndEncrypted===!0,u=!0;document.getElementById("enableDownloadLimit").checked||(i=0),document.getElementById("enableTimeLimit").checked||(a=0),r&&(s=e.sizeEncrypted,n="Encrypted File",o=""),apiChunkComplete(c,n,s,l,o,i,a,d,r,u).then(n=>{t();let s=document.getElementById(`us-progress-info-${e.upload.uuid}`);s!=null&&(s.innerText="In Queue...")}).catch(t=>{console.error("Error:",t),dropzoneUploadError(e,t)})}function dropzoneUploadError(e,t){e.accepted=!1,dropzoneObject._errorProcessing([e],t),showError(e,t)}function dropzoneGetFile(e){for(let t=0;t{addRow(n),notifyWorker({type:"fileAdded",item:n});let s=dropzoneGetFile(t);if(s==null)return;s.isEndToEndEncrypted===!0?apiE2eMutexLockUnlock(!1).then(()=>apiE2eGet()).then(n=>{let i=GokapiE2EInfoParse(n);if(i instanceof Error)throw i;let a=GokapiE2EAddFile(t,e,s.name);if(a instanceof Error)throw a;let o=GokapiE2EInfoEncrypt();if(o instanceof Error)throw o;return apiE2eStore(o)}).then(()=>{GokapiE2EDecryptMenu(),removeFileStatus(t)}).catch(e=>{s.accepted=!1,dropzoneObject._errorProcessing([s],e),console.error("Error:",e)}).finally(()=>{apiE2eMutexLockUnlock(!0).catch(e=>{console.error("Failed to release E2E mutex after write: "+e)})}):removeFileStatus(t)}).catch(e=>{let n=dropzoneGetFile(t);n!=null&&dropzoneUploadError(n,e),console.error("Error:",e)})}function parseProgressStatus(e){let n=document.getElementById(`us-container-${e.chunk_id}`);if(n==null)return;n.setAttribute("data-complete","true");let t;switch(e.upload_status){case 0:t="Processing file...";break;case 1:t="Saving file...";break;case 2:t="Finalising...",requestFileInfo(e.file_id,e.chunk_id);break;case 3:t="Error";let n=dropzoneGetFile(e.chunk_id);e.error_message==""&&(e.error_message="Server Error"),n!=null&&dropzoneUploadError(n,e.error_message);return;default:t="Unknown status";break}document.getElementById(`us-progress-info-${e.chunk_id}`).innerText=t}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 s=e.getAttribute("data-fileid"),o=document.getElementById("mi_edit_down").value,i=document.getElementById("mi_edit_expiry").value,t=document.getElementById("mi_edit_pw").value,a=t==="(unchanged)";document.getElementById("mc_download").checked||(o=0),document.getElementById("mc_expiry").checked||(i=0),document.getElementById("mc_password").checked||(a=!1,t="");let r=!1,n="";document.getElementById("mc_replace").checked&&(n=document.getElementById("mi_edit_replace").value,r=n!=""),apiFilesModify(s,o,i,t,a).then(t=>{if(!r){location.reload();return}apiFilesReplace(s,n).then(e=>{location.reload()}).catch(t=>{alert("Unable to edit file: "+t),console.error("Error:",t),e.disabled=!1})}).catch(t=>{alert("Unable to edit file: "+t),console.error("Error:",t),e.disabled=!1})}function showEditModal(e,t,n,s,o,i,a,r,c){let d=$("#modaledit").clone();$("#modaledit").on("hide.bs.modal",function(){$("#modaledit").remove();let e=d.clone();$("body").append(e)}),document.getElementById("m_filenamelabel").innerText=e,document.getElementById("mc_expiry").setAttribute("data-timestamp",s),document.getElementById("mb_save").setAttribute("data-fileid",t),createCalendar("mi_edit_expiry",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);let l=document.getElementById("mi_edit_replace");if(c)if(document.getElementById("replaceGroup").style.display="flex",r)document.getElementById("mc_replace").disabled=!0,document.getElementById("mc_replace").title="Replacing content is not available for end-to-end encrypted files",l.add(new Option("Unavailable",0)),l.title="Replacing content is not available for end-to-end encrypted files",l.value="0";else{let e=getAllAvailableFiles();for(let n=0;n{changeRowCount(!1,document.getElementById("row-"+e)),showToastFileDeletion(e),notifyWorker({type:"fileDeleted",id:e})}).catch(e=>{alert("Unable to delete file: "+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 parseSseData(e){let t;try{t=JSON.parse(e)}catch(e){console.error("Failed to parse event data:",e);return}switch(t.event){case"download":setNewDownloadCount(t.file_id,t.download_count,t.downloads_remaining);return;case"uploadStatus":parseProgressStatus(t);return;default:console.error("Unknown event",t)}}function setNewDownloadCount(e,t,n){let s=document.getElementById("cell-downloads-"+e);if(s!=null&&(s.innerText=t,s.classList.add("updatedDownloadCount"),setTimeout(()=>s.classList.remove("updatedDownloadCount"),500)),n!=-1){let t=document.getElementById("cell-downloadsRemaining-"+e);t!=null&&(t.innerText=n,t.classList.add("updatedDownloadCount"),setTimeout(()=>t.classList.remove("updatedDownloadCount"),500))}}sseWorkerPort=null;function notifyWorker(e){sseWorkerPort!==null&&sseWorkerPort.postMessage(e)}function registerChangeHandler(){if(typeof SharedWorker!="undefined")try{const e=new SharedWorker("./js/sse-worker.js");e.port.onmessage=e=>{if(e.data.type==="message")parseSseData(e.data.data);else if(e.data.type==="error")console.error("SSE worker connection error:",e.data.detail);else if(e.data.type==="shutdown")setTimeout(function(){window.location.href="./login"},1e3);else if(e.data.type==="fileAdded")document.getElementById("row-"+sanitizeId(e.data.item.Id))==null&&addRow(e.data.item);else if(e.data.type==="fileDeleted"){let t=document.getElementById("row-"+sanitizeId(e.data.id));t!=null&&changeRowCount(!1,t)}else if(e.data.type==="log"){const{level:t,message:n,detail:s}=e.data;s?console[t](n,s):console[t](n)}},e.onerror=e=>{console.warn("SharedWorker failed, falling back to direct SSE:",e),sseWorkerPort=null,_registerDirectSSE()},e.port.start(),sseWorkerPort=e.port;return}catch(e){console.warn("SharedWorker unavailable, falling back to direct SSE:",e)}_registerDirectSSE()}function _registerDirectSSE(){const e=new EventSource("./uploadStatus");e.onmessage=e=>{parseSseData(e.data)},e.onerror=t=>{t.target.readyState!==EventSource.CLOSED&&e.close(),console.log("Reconnecting to SSE (direct)..."),setTimeout(_registerDirectSSE,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 d=document.getElementById("downloadtable"),t=d.insertRow(0);e.Id=sanitizeId(e.Id),t.id="row-"+e.Id;let i=t.insertCell(0),a=t.insertCell(1),s=t.insertCell(2),r=t.insertCell(3),c=t.insertCell(4),o=t.insertCell(5),l=t.insertCell(6);i.innerText=e.Name,i.id="cell-name-"+e.Id,c.id="cell-downloads-"+e.Id,a.innerText=e.Size,e.UnlimitedDownloads?s.innerText="Unlimited":(s.innerText=e.DownloadsRemaining,s.id="cell-downloadsRemaining-"+e.Id),e.UnlimitedTime?r.innerText="Unlimited":r.innerText=formatUnixTimestamp(e.ExpireAt),c.innerText=e.DownloadCount;const n=document.createElement("a");if(n.href=e.UrlDownload,n.target="_blank",n.style.color="inherit",n.id="url-href-"+e.Id,n.textContent=e.Id,o.appendChild(n),e.IsPasswordProtected===!0){const e=document.createElement("i");e.className="bi bi-key",e.title="Password protected",o.appendChild(document.createTextNode(" ")),o.appendChild(e)}return l.appendChild(createButtonGroup(e)),i.classList.add("newItem"),a.classList.add("newItem"),s.classList.add("newItem"),r.classList.add("newItem"),c.classList.add("newItem"),o.classList.add("newItem"),l.classList.add("newItem"),a.setAttribute("data-order",e.SizeBytes),changeRowCount(!0,t),e.Id}function createButtonGroup(e){const m=document.createElement("div");m.className="btn-toolbar justify-content-end",m.setAttribute("role","toolbar");const t=document.createElement("div");t.className="btn-group me-2",t.setAttribute("role","group");const n=document.createElement("button");n.type="button",n.className="copyurl btn btn-outline-light btn-sm",n.dataset.clipboardText=e.UrlDownload,n.id="url-button-"+e.Id,n.title="Copy URL";const _=document.createElement("i");_.className="bi bi-copy",n.appendChild(_),n.appendChild(document.createTextNode(" URL")),n.addEventListener("click",()=>{showToast(1e3)}),t.appendChild(n);const f=document.createElement("button");f.type="button",f.className="btn btn-outline-light btn-sm dropdown-toggle dropdown-toggle-split",f.setAttribute("data-bs-toggle","dropdown"),f.setAttribute("aria-expanded","false"),t.appendChild(f);const p=document.createElement("ul");p.className="dropdown-menu dropdown-menu-end",p.setAttribute("data-bs-theme","dark");const b=document.createElement("li"),s=document.createElement("a");e.UrlHotlink!==""?(s.className="dropdown-item copyurl",s.title="Copy hotlink",s.style.cursor="pointer",s.setAttribute("data-clipboard-text",e.UrlHotlink),s.onclick=()=>showToast(1e3),s.innerHTML=` Hotlink`):(s.className="dropdown-item",s.innerText="Hotlink not available"),b.appendChild(s),p.appendChild(b),t.appendChild(p);const d=document.createElement("button");d.type="button",d.className="btn btn-outline-light btn-sm",d.title="Share",d.onclick=()=>shareUrl(event,e.Id),d.innerHTML=` - `,t.appendChild(d);const u=document.createElement("button");u.type="button",u.className="btn btn-outline-light btn-sm dropdown-toggle dropdown-toggle-split",u.setAttribute("data-bs-toggle","dropdown"),u.setAttribute("aria-expanded","false"),u.id=`shareDropdown-${e.Id}`,t.appendChild(u);const h=document.createElement("ul");h.className="dropdown-menu dropdown-menu-end",h.setAttribute("data-bs-theme","dark");const g=document.createElement("li"),o=document.createElement("a");o.className="dropdown-item",o.id=`qrcode-${e.Id}`,o.style.cursor="pointer",o.title="Open QR Code",o.onclick=()=>showQrCode(e.UrlDownload),o.innerHTML=` QR Code`,g.appendChild(o),h.appendChild(g);const v=document.createElement("li"),r=document.createElement("a");r.className="dropdown-item",r.title="Share via email",r.id=`email-${e.Id}`,r.target="_blank",r.href=`mailto:?body=${encodeURIComponent(e.UrlDownload)}`,r.innerHTML=` Email`,v.appendChild(r),h.appendChild(v),t.appendChild(h);const l=document.createElement("div");l.className="btn-group",l.setAttribute("role","group");const a=document.createElement("button");a.type="button",a.className="btn btn-outline-light btn-sm",a.title="Download",e.RequiresClientSideDecryption&&a.classList.add("disabled");const j=document.createElement("i");j.className="bi bi-download",a.appendChild(j),a.addEventListener("click",()=>{downloadFileWithPresign(e.Id)}),l.appendChild(a);const c=document.createElement("button");c.type="button",c.className="btn btn-outline-light btn-sm",c.title="Edit";const y=document.createElement("i");y.className="bi bi-pencil",c.appendChild(y),c.addEventListener("click",()=>{showEditModal(e.Name,e.Id,e.DownloadsRemaining,e.ExpireAt,e.IsPasswordProtected,e.UnlimitedDownloads,e.UnlimitedTime,e.IsEndToEndEncrypted,canReplaceOwnFiles)}),l.appendChild(c);const i=document.createElement("button");i.type="button",i.className="btn btn-outline-danger btn-sm",i.title="Delete",i.id="button-delete-"+e.Id;const w=document.createElement("i");return w.className="bi bi-trash3",i.appendChild(w),i.addEventListener("click",()=>{deleteFile(e.Id)}),l.appendChild(i),m.appendChild(t),m.appendChild(l),m}function sanitizeId(e){return e.replace(/[^a-zA-Z0-9]/g,"")}function changeRowCount(e,t){let n=$("#maintable").DataTable();rowCount==-1&&(rowCount=n.rows().count()),e?(++rowCount,n.row.add(t)):(--rowCount,t.classList.add("rowDeleting"),setTimeout(()=>{n.row(t).remove(),t.remove()},290));let s=document.getElementsByClassName("dataTables_empty")[0];typeof s!="undefined"?s.innerText="Files stored: "+rowCount:document.getElementsByClassName("dataTables_info")[0].innerText="Files stored: "+rowCount}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 showToastFileDeletion(e){let t=document.getElementById("toastnotificationUndo"),n=document.getElementById("cell-name-"+e).innerText,s=document.getElementById("toastFilename"),o=document.getElementById("toastUndoButton");s.innerText=n,o.dataset.fileid=e,hideToast(),t.classList.add("show"),clearTimeout(toastId),toastId=setTimeout(()=>{hideFileToast()},5e3)}function hideFileToast(){document.getElementById("toastnotificationUndo").classList.remove("show")}function handleUndo(e){hideFileToast(),apiFilesRestore(e.dataset.fileid).then(e=>{addRow(e.FileInfo),notifyWorker({type:"fileAdded",item:e.FileInfo}),isE2EEnabled&&GokapiE2EDecryptMenu()}).catch(e=>{alert("Unable to restore file: "+e),console.error("Error:",e)})}function shareUrl(e,t){if(!navigator.share){e.stopPropagation(),bootstrap.Dropdown.getOrCreateInstance(document.getElementById(`shareDropdown-${t}`)).toggle();return}let n=document.getElementById("cell-name-"+t).innerText,s=document.getElementById("url-href-"+t).getAttribute("href");navigator.share({title:n,url:s})}function showDeprecationNotice(){let e=document.getElementById("toastDeprecation");e.classList.add("show"),setTimeout(()=>{e.classList.remove("show")},5e3)}function changeUserPermission(e,t,n){let s=document.getElementById(n);if(s.classList.contains("perm-processing")||s.classList.contains("perm-nochange"))return;let o=s.classList.contains("perm-granted");s.classList.add("perm-processing"),s.classList.remove("perm-granted"),s.classList.remove("perm-notgranted");let i="GRANT";o&&(i="REVOKE"),t=="PERM_REPLACE_OTHER"&&!o&&(hasNotPermissionReplace=document.getElementById("perm_replace_"+e).classList.contains("perm-notgranted"),hasNotPermissionReplace&&(showToast(2e3,"Also granting permission to replace own files"),changeUserPermission(e,"PERM_REPLACE","perm_replace_"+e))),t=="PERM_REPLACE"&&o&&(hasPermissionReplaceOthers=document.getElementById("perm_replace_other_"+e).classList.contains("perm-granted"),hasPermissionReplaceOthers&&(showToast(2e3,"Also revoking permission to replace files of other users"),changeUserPermission(e,"PERM_REPLACE_OTHER","perm_replace_other_"+e))),apiUserModify(e,t,i).then(e=>{o?s.classList.add("perm-notgranted"):s.classList.add("perm-granted"),s.classList.remove("perm-processing")}).catch(e=>{o?s.classList.add("perm-granted"):s.classList.add("perm-notgranted"),s.classList.remove("perm-processing"),alert("Unable to set permission: "+e),console.error("Error:",e)})}function changeRank(e,t,n){let s=document.getElementById(n);if(s.disabled)return;s.disabled=!0,apiUserChangeRank(e,t).then(e=>{location.reload()}).catch(e=>{s.disabled=!1,alert("Unable to change rank: "+e),console.error("Error:",e)})}function showDeleteUserModal(e,t){let n=document.getElementById("checkboxDelete");n.checked=!1,document.getElementById("deleteModalBody").innerText=t,$("#deleteModal").modal("show"),document.getElementById("buttonDelete").onclick=function(){apiUserDelete(e,n.checked).then(t=>{$("#deleteModal").modal("hide"),document.getElementById("row-"+e).classList.add("rowDeleting"),setTimeout(()=>{document.getElementById("row-"+e).remove()},290)}).catch(e=>{alert("Unable to delete user: "+e),console.error("Error:",e)})}}function showAddUserModal(){let e=$("#newUserModal").clone();$("#newUserModal").on("hide.bs.modal",function(){$("#newUserModal").remove();let t=e.clone();$("body").append(t)}),$("#newUserModal").modal("show")}function showResetPwModal(e,t){let n=$("#resetPasswordModal").clone();$("#resetPasswordModal").on("hide.bs.modal",function(){$("#resetPasswordModal").remove();let e=n.clone();$("body").append(e)}),document.getElementById("l_userpwreset").innerText=t;let s=document.getElementById("resetPasswordButton");s.onclick=function(){resetPw(e,document.getElementById("generateRandomPassword").checked)},$("#resetPasswordModal").modal("show")}function resetPw(e,t){let n=document.getElementById("resetPasswordButton");document.getElementById("resetPasswordButton").disabled=!0,apiUserResetPassword(e,t).then(e=>{if(!t){$("#resetPasswordModal").modal("hide"),showToast(1e3,"Password change requirement set successfully");return}n.style.display="none",document.getElementById("cancelPasswordButton").style.display="none",document.getElementById("formentryReset").style.display="none",document.getElementById("randomPasswordContainer").style.display="block",document.getElementById("closeModalResetPw").style.display="block",document.getElementById("l_returnedPw").innerText=e.password,document.getElementById("copypwclip").onclick=function(){navigator.clipboard.writeText(e.password),showToast(1e3,"Password copied to clipboard")}}).catch(e=>{alert("Unable to reset user password: "+e),console.error("Error:",e),n.disabled=!1})}function addNewUser(){let e=document.getElementById("mb_addUser");e.disabled=!0;let t=document.getElementById("newUserForm");if(t.checkValidity()){let t=document.getElementById("e_userName");apiUserCreate(t.value.trim()).then(e=>{$("#newUserModal").modal("hide"),addRowUser(e.id,e.name,e.permissions),console.log(e)}).catch(t=>{t.message=="duplicate"?(alert("A user already exists with that name"),e.disabled=!1):(alert("Unable to create user: "+t),console.error("Error:",t),e.disabled=!1)})}else t.classList.add("was-validated"),e.disabled=!1}const PermissionDefinitions=[{key:"UserPermGuestUploads",bit:1<<8,icon:"bi bi-box-arrow-in-down",title:"Create file requests",htmlId:e=>`perm_guest_upload_${e}`,apiName:"PERM_GUEST_UPLOAD"},{key:"UserPermReplaceUploads",bit:1<<0,icon:"bi bi-recycle",title:"Replace own uploads",htmlId:e=>`perm_replace_${e}`,apiName:"PERM_REPLACE"},{key:"UserPermListOtherUploads",bit:1<<1,icon:"bi bi-eye",title:"List other uploads",htmlId:e=>`perm_list_${e}`,apiName:"PERM_LIST"},{key:"UserPermEditOtherUploads",bit:1<<2,icon:"bi bi-pencil",title:"Edit other uploads",htmlId:e=>`perm_edit_${e}`,apiName:"PERM_EDIT"},{key:"UserPermDeleteOtherUploads",bit:1<<4,icon:"bi bi-trash3",title:"Delete other uploads",htmlId:e=>`perm_delete_${e}`,apiName:"PERM_DELETE"},{key:"UserPermReplaceOtherUploads",bit:1<<3,icon:"bi bi-arrow-left-right",title:"Replace other uploads",htmlId:e=>`perm_replace_other_${e}`,apiName:"PERM_REPLACE_OTHER"},{key:"UserPermManageLogs",bit:1<<5,icon:"bi bi-card-list",title:"Manage system logs",htmlId:e=>`perm_logs_${e}`,apiName:"PERM_LOGS"},{key:"UserPermManageUsers",bit:1<<7,icon:"bi bi-people",title:"Manage users",htmlId:e=>`perm_users_${e}`,apiName:"PERM_USERS"},{key:"UserPermManageApiKeys",bit:1<<6,icon:"bi bi-sliders2",title:"Manage all API keys",htmlId:e=>`perm_api_${e}`,apiName:"PERM_API"}];function hasPermission(e,t){return(e&t)!==0}function addRowUser(e,t,n){e=sanitizeUserId(e);let m=document.getElementById("usertable"),o=m.insertRow(1);o.id="row-"+e;let c=o.insertCell(0),l=o.insertCell(1),d=o.insertCell(2),u=o.insertCell(3),h=o.insertCell(4),r=o.insertCell(5);c.classList.add("newUser"),l.classList.add("newUser"),d.classList.add("newUser"),u.classList.add("newUser"),h.classList.add("newUser"),r.classList.add("newUser"),c.innerText=t,l.innerText="User",d.innerText="Never",u.innerText="0";const a=document.createElement("div");if(a.className="btn-group",a.setAttribute("role","group"),isInternalAuth){const n=document.createElement("button");n.id=`pwchange-${e}`,n.type="button",n.className="btn btn-outline-light btn-sm",n.title="Reset Password",n.onclick=()=>showResetPwModal(e,t),n.innerHTML=``,a.appendChild(n)}const s=document.createElement("button");s.id=`changeRank_${e}`,s.type="button",s.className="btn btn-outline-light btn-sm",s.title="Promote User",isAdmin?s.onclick=()=>changeRank(e,"ADMIN",`changeRank_${e}`):s.disabled=!0,s.innerHTML=``,a.appendChild(s);const i=document.createElement("button");i.id=`delete-${e}`,i.type="button",i.className="btn btn-outline-danger btn-sm",i.title="Delete",i.onclick=()=>showDeleteUserModal(e,t),i.innerHTML=``,a.appendChild(i),r.innerHTML="",r.appendChild(a),h.innerHTML=PermissionDefinitions.map(t=>{let s="perm-notgranted";hasPermission(n,t.bit)&&(s="perm-granted");const o=t.htmlId(e);let i="";return hasPermission(userPermissions,t.bit)||(i="perm-nochange"),` + `,t.appendChild(d);const u=document.createElement("button");u.type="button",u.className="btn btn-outline-light btn-sm dropdown-toggle dropdown-toggle-split",u.setAttribute("data-bs-toggle","dropdown"),u.setAttribute("aria-expanded","false"),u.id=`shareDropdown-${e.Id}`,t.appendChild(u);const h=document.createElement("ul");h.className="dropdown-menu dropdown-menu-end",h.setAttribute("data-bs-theme","dark");const g=document.createElement("li"),o=document.createElement("a");o.className="dropdown-item",o.id=`qrcode-${e.Id}`,o.style.cursor="pointer",o.title="Open QR Code",o.onclick=()=>showQrCode(e.UrlDownload),o.innerHTML=` QR Code`,g.appendChild(o),h.appendChild(g);const v=document.createElement("li"),r=document.createElement("a");r.className="dropdown-item",r.title="Share via email",r.id=`email-${e.Id}`,r.target="_blank",r.href=`mailto:?body=${encodeURIComponent(e.UrlDownload)}`,r.innerHTML=` Email`,v.appendChild(r),h.appendChild(v),t.appendChild(h);const l=document.createElement("div");l.className="btn-group",l.setAttribute("role","group");const a=document.createElement("button");a.type="button",a.className="btn btn-outline-light btn-sm",a.title="Download",e.RequiresClientSideDecryption&&a.classList.add("disabled");const j=document.createElement("i");j.className="bi bi-download",a.appendChild(j),a.addEventListener("click",()=>{downloadFileWithPresign(e.Id)}),l.appendChild(a);const c=document.createElement("button");c.type="button",c.className="btn btn-outline-light btn-sm",c.title="Edit";const y=document.createElement("i");y.className="bi bi-pencil",c.appendChild(y),c.addEventListener("click",()=>{showEditModal(e.Name,e.Id,e.DownloadsRemaining,e.ExpireAt,e.IsPasswordProtected,e.UnlimitedDownloads,e.UnlimitedTime,e.IsEndToEndEncrypted,canReplaceOwnFiles)}),l.appendChild(c);const i=document.createElement("button");i.type="button",i.className="btn btn-outline-danger btn-sm",i.title="Delete",i.id="button-delete-"+e.Id;const w=document.createElement("i");return w.className="bi bi-trash3",i.appendChild(w),i.addEventListener("click",()=>{deleteFile(e.Id)}),l.appendChild(i),m.appendChild(t),m.appendChild(l),m}function sanitizeId(e){return e.replace(/[^a-zA-Z0-9]/g,"")}function changeRowCount(e,t){let n=$("#maintable").DataTable();rowCount==-1&&(rowCount=n.rows().count()),e?(rowCount=rowCount+1,n.row.add(t)):(rowCount=rowCount-1,t.classList.add("rowDeleting"),setTimeout(()=>{n.row(t).remove(),t.remove()},290));let s=document.getElementsByClassName("dataTables_empty")[0];typeof s!="undefined"?s.innerText="Files stored: "+rowCount:document.getElementsByClassName("dataTables_info")[0].innerText="Files stored: "+rowCount}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 showToastFileDeletion(e){let t=document.getElementById("toastnotificationUndo"),n=document.getElementById("cell-name-"+e).innerText,s=document.getElementById("toastFilename"),o=document.getElementById("toastUndoButton");s.innerText=n,o.dataset.fileid=e,hideToast(),t.classList.add("show"),clearTimeout(toastId),toastId=setTimeout(()=>{hideFileToast()},5e3)}function hideFileToast(){document.getElementById("toastnotificationUndo").classList.remove("show")}function handleUndo(e){hideFileToast(),apiFilesRestore(e.dataset.fileid).then(e=>{addRow(e.FileInfo),notifyWorker({type:"fileAdded",item:e.FileInfo}),isE2EEnabled&&GokapiE2EDecryptMenu()}).catch(e=>{alert("Unable to restore file: "+e),console.error("Error:",e)})}function shareUrl(e,t){if(!navigator.share){e.stopPropagation(),bootstrap.Dropdown.getOrCreateInstance(document.getElementById(`shareDropdown-${t}`)).toggle();return}let n=document.getElementById("cell-name-"+t).innerText,s=document.getElementById("url-href-"+t).getAttribute("href");navigator.share({title:n,url:s})}function showDeprecationNotice(){let e=document.getElementById("toastDeprecation");e.classList.add("show"),setTimeout(()=>{e.classList.remove("show")},5e3)}function changeUserPermission(e,t,n){let s=document.getElementById(n);if(s.classList.contains("perm-processing")||s.classList.contains("perm-nochange"))return;let o=s.classList.contains("perm-granted");s.classList.add("perm-processing"),s.classList.remove("perm-granted"),s.classList.remove("perm-notgranted");let i="GRANT";o&&(i="REVOKE"),t=="PERM_REPLACE_OTHER"&&!o&&(hasNotPermissionReplace=document.getElementById("perm_replace_"+e).classList.contains("perm-notgranted"),hasNotPermissionReplace&&(showToast(2e3,"Also granting permission to replace own files"),changeUserPermission(e,"PERM_REPLACE","perm_replace_"+e))),t=="PERM_REPLACE"&&o&&(hasPermissionReplaceOthers=document.getElementById("perm_replace_other_"+e).classList.contains("perm-granted"),hasPermissionReplaceOthers&&(showToast(2e3,"Also revoking permission to replace files of other users"),changeUserPermission(e,"PERM_REPLACE_OTHER","perm_replace_other_"+e))),apiUserModify(e,t,i).then(e=>{o?s.classList.add("perm-notgranted"):s.classList.add("perm-granted"),s.classList.remove("perm-processing")}).catch(e=>{o?s.classList.add("perm-granted"):s.classList.add("perm-notgranted"),s.classList.remove("perm-processing"),alert("Unable to set permission: "+e),console.error("Error:",e)})}function changeRank(e,t,n){let s=document.getElementById(n);if(s.disabled)return;s.disabled=!0,apiUserChangeRank(e,t).then(e=>{location.reload()}).catch(e=>{s.disabled=!1,alert("Unable to change rank: "+e),console.error("Error:",e)})}function showDeleteUserModal(e,t){let n=document.getElementById("checkboxDelete");n.checked=!1,document.getElementById("deleteModalBody").innerText=t,$("#deleteModal").modal("show"),document.getElementById("buttonDelete").onclick=function(){apiUserDelete(e,n.checked).then(t=>{$("#deleteModal").modal("hide"),document.getElementById("row-"+e).classList.add("rowDeleting"),setTimeout(()=>{document.getElementById("row-"+e).remove()},290)}).catch(e=>{alert("Unable to delete user: "+e),console.error("Error:",e)})}}function showAddUserModal(){let e=$("#newUserModal").clone();$("#newUserModal").on("hide.bs.modal",function(){$("#newUserModal").remove();let t=e.clone();$("body").append(t)}),$("#newUserModal").modal("show")}function showResetPwModal(e,t){let n=$("#resetPasswordModal").clone();$("#resetPasswordModal").on("hide.bs.modal",function(){$("#resetPasswordModal").remove();let e=n.clone();$("body").append(e)}),document.getElementById("l_userpwreset").innerText=t;let s=document.getElementById("resetPasswordButton");s.onclick=function(){resetPw(e,document.getElementById("generateRandomPassword").checked)},$("#resetPasswordModal").modal("show")}function resetPw(e,t){let n=document.getElementById("resetPasswordButton");document.getElementById("resetPasswordButton").disabled=!0,apiUserResetPassword(e,t).then(e=>{if(!t){$("#resetPasswordModal").modal("hide"),showToast(1e3,"Password change requirement set successfully");return}n.style.display="none",document.getElementById("cancelPasswordButton").style.display="none",document.getElementById("formentryReset").style.display="none",document.getElementById("randomPasswordContainer").style.display="block",document.getElementById("closeModalResetPw").style.display="block",document.getElementById("l_returnedPw").innerText=e.password,document.getElementById("copypwclip").onclick=function(){navigator.clipboard.writeText(e.password),showToast(1e3,"Password copied to clipboard")}}).catch(e=>{alert("Unable to reset user password: "+e),console.error("Error:",e),n.disabled=!1})}function addNewUser(){let e=document.getElementById("mb_addUser");e.disabled=!0;let t=document.getElementById("newUserForm");if(t.checkValidity()){let t=document.getElementById("e_userName");apiUserCreate(t.value.trim()).then(e=>{$("#newUserModal").modal("hide"),addRowUser(e.id,e.name,e.permissions),console.log(e)}).catch(t=>{t.message=="duplicate"?(alert("A user already exists with that name"),e.disabled=!1):(alert("Unable to create user: "+t),console.error("Error:",t),e.disabled=!1)})}else t.classList.add("was-validated"),e.disabled=!1}const PermissionDefinitions=[{key:"UserPermGuestUploads",bit:1<<8,icon:"bi bi-box-arrow-in-down",title:"Create file requests",htmlId:e=>`perm_guest_upload_${e}`,apiName:"PERM_GUEST_UPLOAD"},{key:"UserPermReplaceUploads",bit:1<<0,icon:"bi bi-recycle",title:"Replace own uploads",htmlId:e=>`perm_replace_${e}`,apiName:"PERM_REPLACE"},{key:"UserPermListOtherUploads",bit:1<<1,icon:"bi bi-eye",title:"List other uploads",htmlId:e=>`perm_list_${e}`,apiName:"PERM_LIST"},{key:"UserPermEditOtherUploads",bit:1<<2,icon:"bi bi-pencil",title:"Edit other uploads",htmlId:e=>`perm_edit_${e}`,apiName:"PERM_EDIT"},{key:"UserPermDeleteOtherUploads",bit:1<<4,icon:"bi bi-trash3",title:"Delete other uploads",htmlId:e=>`perm_delete_${e}`,apiName:"PERM_DELETE"},{key:"UserPermReplaceOtherUploads",bit:1<<3,icon:"bi bi-arrow-left-right",title:"Replace other uploads",htmlId:e=>`perm_replace_other_${e}`,apiName:"PERM_REPLACE_OTHER"},{key:"UserPermManageLogs",bit:1<<5,icon:"bi bi-card-list",title:"Manage system logs",htmlId:e=>`perm_logs_${e}`,apiName:"PERM_LOGS"},{key:"UserPermManageUsers",bit:1<<7,icon:"bi bi-people",title:"Manage users",htmlId:e=>`perm_users_${e}`,apiName:"PERM_USERS"},{key:"UserPermManageApiKeys",bit:1<<6,icon:"bi bi-sliders2",title:"Manage all API keys",htmlId:e=>`perm_api_${e}`,apiName:"PERM_API"}];function hasPermission(e,t){return(e&t)!==0}function addRowUser(e,t,n){e=sanitizeUserId(e);let m=document.getElementById("usertable"),o=m.insertRow(1);o.id="row-"+e;let c=o.insertCell(0),l=o.insertCell(1),d=o.insertCell(2),u=o.insertCell(3),h=o.insertCell(4),r=o.insertCell(5);c.classList.add("newUser"),l.classList.add("newUser"),d.classList.add("newUser"),u.classList.add("newUser"),h.classList.add("newUser"),r.classList.add("newUser"),c.innerText=t,l.innerText="User",d.innerText="Never",u.innerText="0";const a=document.createElement("div");if(a.className="btn-group",a.setAttribute("role","group"),isInternalAuth){const n=document.createElement("button");n.id=`pwchange-${e}`,n.type="button",n.className="btn btn-outline-light btn-sm",n.title="Reset Password",n.onclick=()=>showResetPwModal(e,t),n.innerHTML=``,a.appendChild(n)}const s=document.createElement("button");s.id=`changeRank_${e}`,s.type="button",s.className="btn btn-outline-light btn-sm",s.title="Promote User",isAdmin?s.onclick=()=>changeRank(e,"ADMIN",`changeRank_${e}`):s.disabled=!0,s.innerHTML=``,a.appendChild(s);const i=document.createElement("button");i.id=`delete-${e}`,i.type="button",i.className="btn btn-outline-danger btn-sm",i.title="Delete",i.onclick=()=>showDeleteUserModal(e,t),i.innerHTML=``,a.appendChild(i),r.innerHTML="",r.appendChild(a),h.innerHTML=PermissionDefinitions.map(t=>{let s="perm-notgranted";hasPermission(n,t.bit)&&(s="perm-granted");const o=t.htmlId(e);let i="";return hasPermission(userPermissions,t.bit)||(i="perm-nochange"),` - `}).join(""),setTimeout(()=>{c.classList.remove("newUser"),l.classList.remove("newUser"),d.classList.remove("newUser"),u.classList.remove("newUser"),h.classList.remove("newUser"),r.classList.remove("newUser")},700)}function sanitizeUserId(e){const t=e.toString().trim();if(!/^\d+$/.test(t))throw new Error("Invalid ID: must contain only digits.");return t} \ No newline at end of file + `}).join(""),setTimeout(()=>{c.classList.remove("newUser"),l.classList.remove("newUser"),d.classList.remove("newUser"),u.classList.remove("newUser"),h.classList.remove("newUser"),r.classList.remove("newUser")},700)}function sanitizeUserId(e){const t=e.toString().trim();if(!/^\d+$/.test(t))throw new Error("Invalid ID: must contain only digits.");return t}const ZIP_BATCH_DEBOUNCE_MS=400,ZIP_GENERATED_FLAG="isGokapiZipArchive";zipPendingFiles=[],zipBatchTimer=null,zipStatusCounter=0;function isZipGroupingEnabled(){let e=document.getElementById("enableZip");return e!=null&&e.checked&&!e.disabled}function initZipDefaults(){let e=document.getElementById("enableZip"),t=document.getElementById("zipNameTemplate");if(e!=null&&!e.disabled){let t=getLocalStorageWithDefault("defaultZipEnabled","true");e.checked=t==="true",e.addEventListener("change",()=>{localStorage.setItem("defaultZipEnabled",e.checked)})}if(t!=null){let e=localStorage.getItem("defaultZipNameTemplate");e!==null&&e!==""&&(t.value=e),t.addEventListener("change",()=>{localStorage.setItem("defaultZipNameTemplate",t.value)})}}function handleZipAddedFile(e){if(e[ZIP_GENERATED_FLAG]===!0)return!1;if(!isZipGroupingEnabled())return!1;try{dropzoneObject.removeFile(e)}catch(e){console.warn("Could not remove buffered file from dropzone:",e)}return zipPendingFiles.push(e),zipBatchTimer!==null&&clearTimeout(zipBatchTimer),zipBatchTimer=setTimeout(flushZipBatch,ZIP_BATCH_DEBOUNCE_MS),!0}function flushZipBatch(){if(zipBatchTimer=null,zipPendingFiles.length===0)return;let e=zipPendingFiles;if(zipPendingFiles=[],e.length===1&&getTopLevelFolder(e)===null){addFilesWithoutZip(e);return}if(typeof JSZip=="undefined"){console.error("JSZip is not available - uploading files without compression."),addFilesWithoutZip(e);return}let t="zip-"+zipStatusCounter++,n=generateZipFilename(e);addZipStatus(t,n);let s=new JSZip;for(let t of e){let n=getRelativePathForFile(t);s.file(n,t,{date:t.lastModified?new Date(t.lastModified):new Date})}s.generateAsync({type:"blob",compression:"DEFLATE",compressionOptions:{level:6}},e=>{updateZipStatus(t,"Compressing... "+Math.round(e.percent)+"%",e.percent)}).then(e=>{let s=new File([e],n,{type:"application/zip",lastModified:Date.now()});s[ZIP_GENERATED_FLAG]=!0,updateZipStatus(t,"Compressed ("+formatZipBytes(e.size)+") - queued for upload",100),setTimeout(()=>removeZipStatus(t),1500),dropzoneObject.addFile(s)}).catch(e=>{console.error("Zip compression failed:",e),setZipStatusError(t,"Compression failed: "+(e&&e.message?e.message:e))})}function addFilesWithoutZip(e){for(let t of e)t[ZIP_GENERATED_FLAG]=!0,dropzoneObject.addFile(t)}function getRelativePathForFile(e){return e.fullPath&&typeof e.fullPath=="string"&&e.fullPath!==""?e.fullPath.replace(/^\/+/,""):e.webkitRelativePath&&e.webkitRelativePath!==""?e.webkitRelativePath:e.name}function getTopLevelFolder(e){let t=null;for(let i of e){let s=getRelativePathForFile(i),o=s.indexOf("/");if(o===-1)return null;let n=s.substring(0,o);if(n==="")return null;if(t===null)t=n;else if(t!==n)return null}return t}function strftime(e,t){let n=(e,t)=>String(e).padStart(t||2,"0"),s=()=>{let e=new Date(t.getFullYear(),0,0),n=t-e;return Math.floor(n/864e5)};return e.replace(/%([A-Za-z%])/g,(e,o)=>{switch(o){case"Y":return String(t.getFullYear());case"y":return n(t.getFullYear()%100);case"m":return n(t.getMonth()+1);case"d":return n(t.getDate());case"H":return n(t.getHours());case"M":return n(t.getMinutes());case"S":return n(t.getSeconds());case"j":return n(s(),3);case"%":return"%";default:return o}})}function sanitizeArchiveName(e){let t=e.replace(/[/\\:*?"<>|]+/g,"_").trim();return t=t.replace(/\s+/g," ").replace(/^\.+|\.+$/g,""),t}function generateZipFilename(e){let s=new Date,o=strftime("%Y%m%d-%H%M%S",s);if(Array.isArray(e)&&e.length>0){let t=getTopLevelFolder(e);if(t!==null&&t!==""){let e=sanitizeArchiveName(t)+"-"+o;return ensureZipExtension(e)}}let i=document.getElementById("zipNameTemplate"),t=i!=null?i.value.trim():"";t===""&&(typeof defaultZipNameTemplate=="string"&&defaultZipNameTemplate.trim()!==""?t=defaultZipNameTemplate.trim():t="gokapi-upload-%Y%m%d-%H%M%S");let n=sanitizeArchiveName(strftime(t,s));return n===""&&(n="gokapi-upload-"+o),ensureZipExtension(n)}function ensureZipExtension(e){return/\.zip$/i.test(e)?e:e+".zip"}function formatZipBytes(e){if(e<1024)return e+" B";let s=["KB","MB","GB","TB"],t=e/1024,n=0;for(;t>=1024&&n +
+
+
+ + Zip Archive +
+
+{{ if .EndToEndEncryption }} + +{{ else }} + +{{ end }} +
+
+ +
+