mirror of
https://git.v0l.io/Kieran/void.cat.git
synced 2025-03-18 05:43:00 +01:00
prep version 1.2.0
This commit is contained in:
parent
196bec4412
commit
7846a85415
4
.gitignore
vendored
4
.gitignore
vendored
@ -7,4 +7,6 @@ obj/
|
||||
dist/
|
||||
.vscode/
|
||||
Debug/
|
||||
Release/
|
||||
Release/
|
||||
node_modules/
|
||||
package-lock.json
|
12
build.bat
12
build.bat
@ -1,12 +0,0 @@
|
||||
@echo off
|
||||
call sass src/css/style.scss dist/style.css
|
||||
call google-closure-compiler ^
|
||||
src/js/Const.js ^
|
||||
src/js/Util.js ^
|
||||
src/js/App.js ^
|
||||
src/js/DropzoneManager.js ^
|
||||
src/js/FileDownloader.js ^
|
||||
src/js/FileUpload.js ^
|
||||
src/js/VBF.js ^
|
||||
src/js/ViewManager.js ^
|
||||
--js_output_file dist/script.min.js --language_out ECMASCRIPT_NEXT
|
29
index.html
29
index.html
@ -6,33 +6,6 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="dist/style.css" />
|
||||
<script async type="text/javascript">
|
||||
var _paq = _paq || [];
|
||||
_paq.push(['trackPageView']);
|
||||
_paq.push(['enableLinkTracking']);
|
||||
(function () {
|
||||
var u = "//matomo.trash.lol/";
|
||||
_paq.push(['setTrackerUrl', u + 'piwik.php']);
|
||||
_paq.push(['setSiteId', '3']);
|
||||
var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
|
||||
g.type = 'text/javascript'; g.async = true; g.defer = true; g.src = u + 'piwik.js'; s.parentNode.insertBefore(g, s);
|
||||
})();
|
||||
</script>
|
||||
<!-- Fathom - simple website analytics - https://github.com/usefathom/fathom -->
|
||||
<script>
|
||||
(function(f, a, t, h, o, m){
|
||||
a[h]=a[h]||function(){
|
||||
(a[h].q=a[h].q||[]).push(arguments)
|
||||
};
|
||||
o=f.createElement('script'),
|
||||
m=f.getElementsByTagName('script')[0];
|
||||
o.async=1; o.src=t; o.id='fathom-script';
|
||||
m.parentNode.insertBefore(o,m)
|
||||
})(document, window, '//fathom.void.cat/tracker.js', 'fathom');
|
||||
fathom('set', 'siteId', 'ACJOO');
|
||||
fathom('trackPageview');
|
||||
</script>
|
||||
<!-- / Fathom -->
|
||||
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
|
||||
<script>
|
||||
(adsbygoogle = window.adsbygoogle || []).push({
|
||||
@ -187,7 +160,7 @@
|
||||
(adsbygoogle = window.adsbygoogle || []).push({});
|
||||
</script>
|
||||
</div>
|
||||
<script type="text/javascript" src="dist/script.min.js" async></script>
|
||||
<script type="text/javascript" src="./dist/bundle.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
31
package.json
Normal file
31
package.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "void.cat",
|
||||
"version": "1.2.0-beta1",
|
||||
"description": "Free file hosting website",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/v0l/void.cat.git"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "sass src/css/style.scss dist/style.css && webpack-cli --entry ./src/js/index.js --mode production --output ./dist/bundle.js"
|
||||
},
|
||||
"keywords": [
|
||||
"upload",
|
||||
"host",
|
||||
"files",
|
||||
"pomf"
|
||||
],
|
||||
"author": "v0l",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/v0l/void.cat/issues"
|
||||
},
|
||||
"homepage": "https://github.com/v0l/void.cat#readme",
|
||||
"dependencies": {
|
||||
"asmcrypto.js": "^2.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"saas": "^1.0.0",
|
||||
"webpack-cli": "^3.3.2"
|
||||
}
|
||||
}
|
222
src/js/App.js
222
src/js/App.js
@ -1,222 +0,0 @@
|
||||
/**
|
||||
* @constant {Object}
|
||||
*/
|
||||
const App = {
|
||||
Loaded: false,
|
||||
CharJsLoaded: false,
|
||||
|
||||
Elements: {
|
||||
get Dropzone() { return $('#dropzone') },
|
||||
get Uploads() { return $('#uploads') },
|
||||
get PageView() { return $('#page-view') },
|
||||
get PageUpload() { return $('#page-upload') },
|
||||
get PageFaq() { return $('#page-faq') },
|
||||
get PageStats() { return $('#page-stats') },
|
||||
get PageDonate() { return $('#page-donate') }
|
||||
},
|
||||
|
||||
Templates: {
|
||||
get Upload() { return $("template[id='tmpl-upload']") }
|
||||
},
|
||||
|
||||
get IsEdge() {
|
||||
return /Edge/.test(navigator.userAgent);
|
||||
},
|
||||
|
||||
get IsChrome() {
|
||||
return !App.IsEdge && /^Mozilla.*Chrome/.test(navigator.userAgent);
|
||||
},
|
||||
|
||||
get IsFirefox() {
|
||||
return !App.IsEdge && /^Mozilla.*Firefox/.test(navigator.userAgent);
|
||||
},
|
||||
|
||||
/**
|
||||
* Uploads the files as selected by the input form
|
||||
* @param {Element} ctx
|
||||
* @returns {Promise}
|
||||
*/
|
||||
UploadFiles: async function (ctx) {
|
||||
let files = ctx.files;
|
||||
let proc_files = [];
|
||||
|
||||
for (let x = 0; x < files.length; x++) {
|
||||
let fu = new FileUpload(files[x]);
|
||||
proc_files[proc_files.length] = fu.ProcessUpload();
|
||||
}
|
||||
|
||||
await Promise.all(proc_files);
|
||||
},
|
||||
|
||||
ResetView: function () {
|
||||
App.Elements.PageView.style.display = "none";
|
||||
App.Elements.PageUpload.style.display = "none";
|
||||
App.Elements.PageFaq.style.display = "none";
|
||||
App.Elements.PageStats.style.display = "none";
|
||||
App.Elements.PageDonate.style.display = "none";
|
||||
},
|
||||
|
||||
ShowStats: async function () {
|
||||
location.hash = "#stats";
|
||||
App.ResetView();
|
||||
|
||||
if (!App.CharJsLoaded) {
|
||||
await App.InsertScript("//cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js", () => {
|
||||
return typeof moment !== "undefined";
|
||||
});
|
||||
await App.InsertScript("//cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.min.js", () => {
|
||||
return typeof Chart !== "undefined";
|
||||
});
|
||||
}
|
||||
|
||||
let api_rsp = await Api.GetTxChart();
|
||||
if (api_rsp.ok) {
|
||||
let ctx = $('#weektxgraph').getContext('2d');
|
||||
new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: api_rsp.data,
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'time',
|
||||
time: {
|
||||
source: 'data'
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
callback: function(label, index, labels) {
|
||||
return Utils.FormatBytes(label);
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
App.Elements.PageStats.style.display = "block";
|
||||
},
|
||||
|
||||
ShowFAQ: function () {
|
||||
location.hash = "#faq";
|
||||
App.ResetView();
|
||||
App.Elements.PageFaq.style.display = "block";
|
||||
},
|
||||
|
||||
ShowDonate: function () {
|
||||
location.hash = "#donate";
|
||||
App.ResetView();
|
||||
App.Elements.PageDonate.style.display = "block";
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets up the page
|
||||
*/
|
||||
Init: async function () {
|
||||
App.CheckBrowserSupport();
|
||||
App.MakePolyfills();
|
||||
|
||||
App.ResetView();
|
||||
|
||||
if (location.hash !== "") {
|
||||
if (location.hash == "#faq") {
|
||||
App.ShowFAQ();
|
||||
} else if (location.hash == "#stats") {
|
||||
App.ShowStats();
|
||||
} else if (location.hash == "#donate") {
|
||||
App.ShowDonate();
|
||||
} else {
|
||||
App.Elements.PageView.style.display = "block";
|
||||
new ViewManager();
|
||||
}
|
||||
window.site_info = await Api.GetSiteInfo();
|
||||
} else {
|
||||
window.site_info = await Api.GetSiteInfo();
|
||||
App.Elements.PageUpload.style.display = "block";
|
||||
$('#dropzone').innerHTML = `Click me!<br><small>(${Utils.FormatBytes(window.site_info.data.max_upload_size)} max)</small>`;
|
||||
new DropzoneManager(App.Elements.Dropzone);
|
||||
}
|
||||
|
||||
if (window.site_info.ok) {
|
||||
let elms = document.querySelectorAll("#footer-stats div span");
|
||||
elms[0].textContent = window.site_info.data.basic_stats.Files;
|
||||
elms[1].textContent = Utils.FormatBytes(window.site_info.data.basic_stats.Size, 2);
|
||||
elms[2].textContent = Utils.FormatBytes(window.site_info.data.basic_stats.Transfer_24h, 2);
|
||||
}
|
||||
|
||||
let faq_headers = document.querySelectorAll('#page-faq .faq-header');
|
||||
for (let x = 0; x < faq_headers.length; x++) {
|
||||
faq_headers[x].addEventListener('click', function () {
|
||||
this.nextElementSibling.classList.toggle("show");
|
||||
}.bind(faq_headers[x]));
|
||||
}
|
||||
|
||||
App.Loaded = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds in polyfills for this browser
|
||||
*/
|
||||
MakePolyfills: function () {
|
||||
if (typeof TextEncoder === "undefined" || typeof TextDecoder === "undefined") {
|
||||
App.InsertScript("//unpkg.com/text-encoding@0.6.4/lib/encoding-indexes.js");
|
||||
App.InsertScript("//unpkg.com/text-encoding@0.6.4/lib/encoding.js");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a script tag at the top of the header
|
||||
* @param {string} src - The script src url
|
||||
* @param {function} fnWait - Function to use in promise to test if the script is loaded
|
||||
*/
|
||||
InsertScript: async function (src, fnWait) {
|
||||
var before = document.head.getElementsByTagName('script')[0];
|
||||
var newlink = document.createElement('script');
|
||||
newlink.src = src;
|
||||
document.head.insertBefore(newlink, before);
|
||||
|
||||
if (typeof fnWait === "function") {
|
||||
await new Promise((resolve, reject) => {
|
||||
let timer = setInterval(() => {
|
||||
if (fnWait()) {
|
||||
clearInterval(timer);
|
||||
resolve();
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks browser version
|
||||
*/
|
||||
CheckBrowserSupport: function () {
|
||||
if (!App.IsFirefox) {
|
||||
if (App.IsChrome) {
|
||||
App.AddNoticeItem("Uploads bigger then 100MiB usually crash Chrome when uploading. Please upload with Firefox. Or check <a target=\"_blank\" href=\"https://github.com/v0l/void.cat/releases\">GitHub</a> for tools.");
|
||||
}
|
||||
if (App.IsEdge) {
|
||||
let edge_version = /Edge\/([0-9]{1,3}\.[0-9]{1,5})/.exec(navigator.userAgent)[1];
|
||||
Log.I(`Edge version is: ${edge_version}`);
|
||||
if (parseFloat(edge_version) < 18.18218) {
|
||||
App.AddNoticeItem("Upload progress isn't reported in the version of Edge you are using, see <a target=\"_blank\" href=\"https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12224510/\">here for more info</a>.");
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelector('#page-notice').style.display = "block";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a notice to the UI notice box
|
||||
* @param {string} txt - Message to add to notice list
|
||||
*/
|
||||
AddNoticeItem: function (txt) {
|
||||
let ne = document.createElement('li');
|
||||
ne.innerHTML = txt;
|
||||
document.querySelector('#page-notice ul').appendChild(ne);
|
||||
}
|
||||
};
|
||||
|
||||
setTimeout(App.Init);
|
@ -1,52 +0,0 @@
|
||||
/**
|
||||
* @constant {string} - Stores the current app version
|
||||
*/
|
||||
const AppVersion = "1.0";
|
||||
/**
|
||||
* @constant {string} - The hashing algo to use to verify the file
|
||||
*/
|
||||
const HashingAlgo = 'SHA-256';
|
||||
/**
|
||||
* @constant {string} - The encryption algoritm to use for file uploads
|
||||
*/
|
||||
const EncryptionAlgo = 'AES-CBC';
|
||||
/**
|
||||
* @constant {object} - The 'algo' argument for importing/exporting/generating keys
|
||||
*/
|
||||
const EncryptionKeyDetails = { name: EncryptionAlgo, length: 128 };
|
||||
/**
|
||||
* @constant {object} - The 'algo' argument for importing/exporting/generating hmac keys
|
||||
*/
|
||||
const HMACKeyDetails = { name: 'HMAC', hash: HashingAlgo };
|
||||
/**
|
||||
* @constant {number} - Size of 1 kiB
|
||||
*/
|
||||
const kiB = Math.pow(1024, 1);
|
||||
/**
|
||||
* @constant {number} - Size of 1 MiB
|
||||
*/
|
||||
const MiB = Math.pow(1024, 2);
|
||||
/**
|
||||
* @constant {number} - Size of 1 GiB
|
||||
*/
|
||||
const GiB = Math.pow(1024, 3);
|
||||
/**
|
||||
* @constant {number} - Size of 1 TiB
|
||||
*/
|
||||
const TiB = Math.pow(1024, 4);
|
||||
/**
|
||||
* @constant {number} - Size of 1 PiB
|
||||
*/
|
||||
const PiB = Math.pow(1024, 5);
|
||||
/**
|
||||
* @constant {number} - Size of 1 EiB
|
||||
*/
|
||||
const EiB = Math.pow(1024, 6);
|
||||
/**
|
||||
* @constant {number} - Size of 1 ZiB
|
||||
*/
|
||||
const ZiB = Math.pow(1024, 7);
|
||||
/**
|
||||
* @constant {number} - Size of 1 YiB
|
||||
*/
|
||||
const YiB = Math.pow(1024, 8);
|
3
src/js/index.js
Normal file
3
src/js/index.js
Normal file
@ -0,0 +1,3 @@
|
||||
import * as App from './modules/App.js';
|
||||
|
||||
App.Init();
|
222
src/js/modules/App.js
Normal file
222
src/js/modules/App.js
Normal file
@ -0,0 +1,222 @@
|
||||
import { Api, Utils, Log, $ } from './Util.js';
|
||||
import { ViewManager } from './ViewManager.js';
|
||||
import { DropzoneManager } from './DropzoneManager.js';
|
||||
import { FileUpload } from './FileUpload.js';
|
||||
|
||||
let ChartJsLoaded = false;
|
||||
|
||||
const Elements = {
|
||||
get Dropzone() { return $('#dropzone') },
|
||||
get Uploads() { return $('#uploads') },
|
||||
get PageView() { return $('#page-view') },
|
||||
get PageUpload() { return $('#page-upload') },
|
||||
get PageFaq() { return $('#page-faq') },
|
||||
get PageStats() { return $('#page-stats') },
|
||||
get PageDonate() { return $('#page-donate') }
|
||||
};
|
||||
|
||||
const Templates = {
|
||||
get Upload() { return $("template[id='tmpl-upload']") }
|
||||
};
|
||||
|
||||
const Browser = {
|
||||
get IsEdge() {
|
||||
return /Edge/.test(navigator.userAgent);
|
||||
},
|
||||
|
||||
get IsChrome() {
|
||||
return !Browser.IsEdge && /^Mozilla.*Chrome/.test(navigator.userAgent);
|
||||
},
|
||||
|
||||
get IsFirefox() {
|
||||
return !Browser.IsEdge && /^Mozilla.*Firefox/.test(navigator.userAgent);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Uploads the files as selected by the input form
|
||||
* @param {Element} ctx
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function UploadFiles(ctx) {
|
||||
let files = ctx.files;
|
||||
let proc_files = [];
|
||||
|
||||
for (let x = 0; x < files.length; x++) {
|
||||
let fu = new FileUpload(files[x]);
|
||||
proc_files[proc_files.length] = fu.ProcessUpload();
|
||||
}
|
||||
|
||||
await Promise.all(proc_files);
|
||||
}
|
||||
|
||||
function ResetView() {
|
||||
Elements.PageView.style.display = "none";
|
||||
Elements.PageUpload.style.display = "none";
|
||||
Elements.PageFaq.style.display = "none";
|
||||
Elements.PageStats.style.display = "none";
|
||||
Elements.PageDonate.style.display = "none";
|
||||
}
|
||||
|
||||
async function ShowStats() {
|
||||
location.hash = "#stats";
|
||||
ResetView();
|
||||
|
||||
if (!ChartJsLoaded) {
|
||||
await InsertScript("//cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js", () => {
|
||||
return typeof moment !== "undefined";
|
||||
});
|
||||
await InsertScript("//cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.min.js", () => {
|
||||
return typeof Chart !== "undefined";
|
||||
});
|
||||
}
|
||||
|
||||
let api_rsp = await Api.GetTxChart();
|
||||
if (api_rsp.ok) {
|
||||
let ctx = $('#weektxgraph').getContext('2d');
|
||||
new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: api_rsp.data,
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'time',
|
||||
time: {
|
||||
source: 'data'
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
callback: function(label, index, labels) {
|
||||
return Utils.FormatBytes(label);
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Elements.PageStats.style.display = "block";
|
||||
}
|
||||
|
||||
function ShowFAQ() {
|
||||
location.hash = "#faq";
|
||||
ResetView();
|
||||
Elements.PageFaq.style.display = "block";
|
||||
}
|
||||
|
||||
function ShowDonate() {
|
||||
location.hash = "#donate";
|
||||
ResetView();
|
||||
Elements.PageDonate.style.display = "block";
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the page
|
||||
*/
|
||||
async function Init() {
|
||||
CheckBrowserSupport();
|
||||
MakePolyfills();
|
||||
|
||||
ResetView();
|
||||
|
||||
if (location.hash !== "") {
|
||||
if (location.hash == "#faq") {
|
||||
ShowFAQ();
|
||||
} else if (location.hash == "#stats") {
|
||||
ShowStats();
|
||||
} else if (location.hash == "#donate") {
|
||||
ShowDonate();
|
||||
} else {
|
||||
Elements.PageView.style.display = "block";
|
||||
let vm = new ViewManager();
|
||||
vm.LoadView();
|
||||
}
|
||||
window.site_info = await Api.GetSiteInfo();
|
||||
} else {
|
||||
window.site_info = await Api.GetSiteInfo();
|
||||
Elements.PageUpload.style.display = "block";
|
||||
$('#dropzone').innerHTML = `Click me!<br><small>(${Utils.FormatBytes(window.site_info.data.max_upload_size)} max)</small>`;
|
||||
new DropzoneManager(Elements.Dropzone);
|
||||
}
|
||||
|
||||
if (window.site_info.ok) {
|
||||
let elms = document.querySelectorAll("#footer-stats div span");
|
||||
elms[0].textContent = window.site_info.data.basic_stats.Files;
|
||||
elms[1].textContent = Utils.FormatBytes(window.site_info.data.basic_stats.Size, 2);
|
||||
elms[2].textContent = Utils.FormatBytes(window.site_info.data.basic_stats.Transfer_24h, 2);
|
||||
}
|
||||
|
||||
let faq_headers = document.querySelectorAll('#page-faq .faq-header');
|
||||
for (let x = 0; x < faq_headers.length; x++) {
|
||||
faq_headers[x].addEventListener('click', function () {
|
||||
this.nextElementSibling.classList.toggle("show");
|
||||
}.bind(faq_headers[x]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds in polyfills for this browser
|
||||
*/
|
||||
function MakePolyfills() {
|
||||
if (typeof TextEncoder === "undefined" || typeof TextDecoder === "undefined") {
|
||||
InsertScript("//unpkg.com/text-encoding@0.6.4/lib/encoding-indexes.js");
|
||||
InsertScript("//unpkg.com/text-encoding@0.6.4/lib/encoding.js");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a script tag at the top of the header
|
||||
* @param {string} src - The script src url
|
||||
* @param {function} fnWait - Function to use in promise to test if the script is loaded
|
||||
*/
|
||||
async function InsertScript(src, fnWait) {
|
||||
var before = document.head.getElementsByTagName('script')[0];
|
||||
var newlink = document.createElement('script');
|
||||
newlink.src = src;
|
||||
document.head.insertBefore(newlink, before);
|
||||
|
||||
if (typeof fnWait === "function") {
|
||||
await new Promise((resolve, reject) => {
|
||||
let timer = setInterval(() => {
|
||||
if (fnWait()) {
|
||||
clearInterval(timer);
|
||||
resolve();
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks browser version
|
||||
*/
|
||||
function CheckBrowserSupport() {
|
||||
if (!Browser.IsFirefox) {
|
||||
if (Browser.IsChrome) {
|
||||
AddNoticeItem("Uploads bigger then 100MiB usually crash Chrome when uploading. Please upload with Firefox. Or check <a target=\"_blank\" href=\"https://github.com/v0l/void.cat/releases\">GitHub</a> for tools.");
|
||||
}
|
||||
if (Browser.IsEdge) {
|
||||
let edge_version = /Edge\/([0-9]{1,3}\.[0-9]{1,5})/.exec(navigator.userAgent)[1];
|
||||
Log.I(`Edge version is: ${edge_version}`);
|
||||
if (parseFloat(edge_version) < 18.18218) {
|
||||
AddNoticeItem("Upload progress isn't reported in the version of Edge you are using, see <a target=\"_blank\" href=\"https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12224510/\">here for more info</a>.");
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelector('#page-notice').style.display = "block";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a notice to the UI notice box
|
||||
* @param {string} txt - Message to add to notice list
|
||||
*/
|
||||
function AddNoticeItem(txt) {
|
||||
let ne = document.createElement('li');
|
||||
ne.innerHTML = txt;
|
||||
document.querySelector('#page-notice ul').appendChild(ne);
|
||||
}
|
||||
|
||||
export { Init, Templates };
|
59
src/js/modules/Const.js
Normal file
59
src/js/modules/Const.js
Normal file
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Change Log
|
||||
* 1.0 - https://github.com/v0l/void.cat/commit/b0a49e5bc28d62ebd954fe344c6f604b952e905c
|
||||
* 1.1 - https://github.com/v0l/void.cat/commit/e3f7f2a59e86f86a27a06d8725f4f6401af7f190
|
||||
* 1.2 - TBC
|
||||
*/
|
||||
|
||||
/**
|
||||
* @constant {string} - Stores the current app version
|
||||
*/
|
||||
export const AppVersion = require('../../../package.json').version;
|
||||
/**
|
||||
* @constant {string} - The hashing algo to use to verify the file
|
||||
*/
|
||||
export const HashingAlgo = 'SHA-256';
|
||||
/**
|
||||
* @constant {string} - The encryption algoritm to use for file uploads
|
||||
*/
|
||||
export const EncryptionAlgo = 'AES-CBC';
|
||||
/**
|
||||
* @constant {object} - The 'algo' argument for importing/exporting/generating keys
|
||||
*/
|
||||
export const EncryptionKeyDetails = { name: EncryptionAlgo, length: 128 };
|
||||
/**
|
||||
* @constant {object} - The 'algo' argument for importing/exporting/generating hmac keys
|
||||
*/
|
||||
export const HMACKeyDetails = { name: 'HMAC', hash: HashingAlgo };
|
||||
/**
|
||||
* @constant {number} - Size of 1 kiB
|
||||
*/
|
||||
export const kiB = Math.pow(1024, 1);
|
||||
/**
|
||||
* @constant {number} - Size of 1 MiB
|
||||
*/
|
||||
export const MiB = Math.pow(1024, 2);
|
||||
/**
|
||||
* @constant {number} - Size of 1 GiB
|
||||
*/
|
||||
export const GiB = Math.pow(1024, 3);
|
||||
/**
|
||||
* @constant {number} - Size of 1 TiB
|
||||
*/
|
||||
export const TiB = Math.pow(1024, 4);
|
||||
/**
|
||||
* @constant {number} - Size of 1 PiB
|
||||
*/
|
||||
export const PiB = Math.pow(1024, 5);
|
||||
/**
|
||||
* @constant {number} - Size of 1 EiB
|
||||
*/
|
||||
export const EiB = Math.pow(1024, 6);
|
||||
/**
|
||||
* @constant {number} - Size of 1 ZiB
|
||||
*/
|
||||
export const ZiB = Math.pow(1024, 7);
|
||||
/**
|
||||
* @constant {number} - Size of 1 YiB
|
||||
*/
|
||||
export const YiB = Math.pow(1024, 8);
|
@ -1,11 +1,13 @@
|
||||
import { FileUpload } from './FileUpload.js';
|
||||
|
||||
/**
|
||||
* @constructor Creates an instance of the DropzoneManager
|
||||
* @param {HTMLElement} dz - Dropzone element
|
||||
*/
|
||||
const DropzoneManager = function (dz) {
|
||||
function DropzoneManager(dz) {
|
||||
this.dz = dz;
|
||||
|
||||
this.SetUI = function() {
|
||||
this.SetUI = function () {
|
||||
document.querySelector('#page-upload div:nth-child(1)').removeAttribute("style");
|
||||
document.querySelector('#uploads').removeAttribute("style");
|
||||
};
|
||||
@ -27,4 +29,6 @@ const DropzoneManager = function (dz) {
|
||||
};
|
||||
|
||||
this.dz.addEventListener('click', this.OpenFileSelect.bind(this), false);
|
||||
};
|
||||
};
|
||||
|
||||
export { DropzoneManager };
|
@ -1,3 +1,7 @@
|
||||
import * as Const from './Const.js';
|
||||
import { VBF } from './VBF.js';
|
||||
import { XHR, Utils, Log } from './Util.js';
|
||||
|
||||
/**
|
||||
* File download and decryption class
|
||||
* @class
|
||||
@ -5,7 +9,7 @@
|
||||
* @param {string} key - The key to use for decryption
|
||||
* @param {string} iv - The IV to use for decryption
|
||||
*/
|
||||
const FileDownloader = function (fileinfo, key, iv) {
|
||||
function FileDownloader(fileinfo, key, iv) {
|
||||
this.fileinfo = fileinfo;
|
||||
this.key = key;
|
||||
this.iv = iv;
|
||||
@ -113,11 +117,11 @@ const FileDownloader = function (fileinfo, key, iv) {
|
||||
let iv_raw = Utils.HexToArray(this.iv);
|
||||
Log.I(`${this.fileinfo.FileId} decrypting with key ${this.key} and iv ${this.iv}`);
|
||||
|
||||
let key = await crypto.subtle.importKey("raw", key_raw, EncryptionKeyDetails, false, ['decrypt']);
|
||||
let keyhmac = await crypto.subtle.importKey("raw", key_raw, HMACKeyDetails, false, ['verify']);
|
||||
let key = await crypto.subtle.importKey("raw", key_raw, Const.EncryptionKeyDetails, false, ['decrypt']);
|
||||
let keyhmac = await crypto.subtle.importKey("raw", key_raw, Const.HMACKeyDetails, false, ['verify']);
|
||||
|
||||
let enc_data = VBF.GetEncryptedPart(header.version, blob);
|
||||
let decrypted_file = await crypto.subtle.decrypt({ name: EncryptionAlgo, iv: iv_raw }, key, enc_data);
|
||||
let decrypted_file = await crypto.subtle.decrypt({ name: Const.EncryptionAlgo, iv: iv_raw }, key, enc_data);
|
||||
|
||||
//read the header
|
||||
let json_header_length = new Uint16Array(decrypted_file.slice(0, 2))[0];
|
||||
@ -126,7 +130,7 @@ const FileDownloader = function (fileinfo, key, iv) {
|
||||
|
||||
//hash the file to verify
|
||||
let file_data = decrypted_file.slice(2 + json_header_length);
|
||||
let hmac_verify = await crypto.subtle.verify(HMACKeyDetails, keyhmac, header.hmac, file_data);
|
||||
let hmac_verify = await crypto.subtle.verify(Const.HMACKeyDetails, keyhmac, header.hmac, file_data);
|
||||
if (hmac_verify) {
|
||||
Log.I(`${this.fileinfo.FileId} HMAC verified!`);
|
||||
|
||||
@ -136,4 +140,6 @@ const FileDownloader = function (fileinfo, key, iv) {
|
||||
throw "HMAC verify failed";
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export { FileDownloader };
|
@ -1,10 +1,16 @@
|
||||
import * as Const from './Const.js';
|
||||
import { Templates } from './App.js';
|
||||
import { XHR, Utils, Log, $ } from './Util.js';
|
||||
import { VBF } from './VBF.js';
|
||||
import { bytes_to_base64 } from 'asmcrypto.js';
|
||||
|
||||
/**
|
||||
* File upload handler class
|
||||
* @class
|
||||
* @param {File} file - The file handle to upload
|
||||
* @param {string} host - The hostname to upload to
|
||||
*/
|
||||
const FileUpload = function (file, host) {
|
||||
function FileUpload(file, host) {
|
||||
this.hasCrypto = typeof window.crypto.subtle === "object";
|
||||
this.file = file;
|
||||
this.host = host;
|
||||
@ -46,6 +52,23 @@ const FileUpload = function (file, host) {
|
||||
return `${await this.HexKey()}:${this.HexIV()}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retruns the formatted hash fragment for this upload
|
||||
* @returns {Promise<string>} The id:key:iv concatenated and converted to base64
|
||||
*/
|
||||
this.FormatUrl = async (id) => {
|
||||
let id_hex = new Uint8Array(Utils.HexToArray(id));
|
||||
let key = new Uint8Array(await crypto.subtle.exportKey('raw', this.key));
|
||||
let iv = new Uint8Array(this.iv);
|
||||
|
||||
let ret = new Uint8Array(id_hex.byteLength + key.byteLength + iv.byteLength);
|
||||
ret.set(id_hex, 0);
|
||||
ret.set(key, id_hex.byteLength);
|
||||
ret.set(iv, id_hex.byteLength + key.byteLength);
|
||||
|
||||
return bytes_to_base64(ret);
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads the file and SHA256 hashes it
|
||||
* @return {Promise<ArrayBuffer>}
|
||||
@ -64,7 +87,7 @@ const FileUpload = function (file, host) {
|
||||
|
||||
fr.onload = function (ev) {
|
||||
this.HandleProgress('state-hash-start');
|
||||
crypto.subtle.sign(HMACKeyDetails, this.hmackey, ev.target.result).then(function (hash) {
|
||||
crypto.subtle.sign(Const.HMACKeyDetails, this.hmackey, ev.target.result).then(function (hash) {
|
||||
this.HandleProgress('state-hash-end');
|
||||
resolve({
|
||||
hash: hash,
|
||||
@ -189,7 +212,7 @@ const FileUpload = function (file, host) {
|
||||
* Creates a template for the upload to show progress
|
||||
*/
|
||||
this.CreateNode = function () {
|
||||
let nelm = document.importNode(App.Templates.Upload.content, true);
|
||||
let nelm = document.importNode(Templates.Upload.content, true);
|
||||
|
||||
nelm.filename = nelm.querySelector('.file-info .file-info-name');
|
||||
nelm.filesize = nelm.querySelector('.file-info .file-info-size');
|
||||
@ -213,8 +236,8 @@ const FileUpload = function (file, host) {
|
||||
* @returns {Promise<CryptoKey>} The new key
|
||||
*/
|
||||
this.GenerateKey = async function () {
|
||||
this.key = await crypto.subtle.generateKey(EncryptionKeyDetails, true, ['encrypt', 'decrypt']);
|
||||
this.hmackey = await crypto.subtle.importKey("raw", await crypto.subtle.exportKey('raw', this.key), HMACKeyDetails, false, ["sign"]);
|
||||
this.key = await crypto.subtle.generateKey(Const.EncryptionKeyDetails, true, ['encrypt', 'decrypt']);
|
||||
this.hmackey = await crypto.subtle.importKey("raw", await crypto.subtle.exportKey('raw', this.key), Const.HMACKeyDetails, false, ["sign"]);
|
||||
|
||||
crypto.getRandomValues(this.iv);
|
||||
|
||||
@ -230,7 +253,7 @@ const FileUpload = function (file, host) {
|
||||
this.EncryptFile = async function (fileData) {
|
||||
this.HandleProgress('state-encrypt-start');
|
||||
let encryptedData = await crypto.subtle.encrypt({
|
||||
name: EncryptionAlgo,
|
||||
name: Const.EncryptionAlgo,
|
||||
iv: this.iv
|
||||
}, this.key, fileData);
|
||||
this.HandleProgress('state-encrypt-end');
|
||||
@ -288,7 +311,7 @@ const FileUpload = function (file, host) {
|
||||
Log.I(`${this.file.name} hash is: ${h256}`);
|
||||
|
||||
//create blob for encryption
|
||||
let header_data = new TextEncoder().encode(header);
|
||||
let header_data = new TextEncoder('utf-8').encode(header);
|
||||
Log.I(`Using header: ${header} (length=${header_data.byteLength})`);
|
||||
|
||||
let encryption_payload = new Uint8Array(2 + header_data.byteLength + hash_data.data.byteLength);
|
||||
@ -315,7 +338,7 @@ const FileUpload = function (file, host) {
|
||||
|
||||
let nl = document.createElement("a");
|
||||
nl.target = "_blank";
|
||||
nl.href = `${window.location.protocol}//${window.location.host}/#${uploadResult.id}:${await this.TextKey()}`;
|
||||
nl.href = `${window.location.protocol}//${window.location.host}/#${await this.FormatUrl(uploadResult.id)}`;
|
||||
nl.textContent = this.file.name;
|
||||
this.domNode.links.appendChild(nl);
|
||||
} else {
|
||||
@ -324,3 +347,5 @@ const FileUpload = function (file, host) {
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export { FileUpload };
|
@ -1,14 +1,16 @@
|
||||
import * as Const from './Const.js';
|
||||
|
||||
/**
|
||||
* @constant {function} - Helper function for document.querySelector
|
||||
* @param {string} selector - The selector to use in the query
|
||||
* @returns {HTMLElement} The first selected element
|
||||
*/
|
||||
const $ = (selector) => document.querySelector(selector);
|
||||
export const $ = (selector) => document.querySelector(selector);
|
||||
|
||||
const Log = {
|
||||
I: (msg) => console.log(`[App_v ${AppVersion}][I]: ${msg}`),
|
||||
W: (msg) => console.warn(`[App_v ${AppVersion}][W]: ${msg}`),
|
||||
E: (msg) => console.error(`[App_v ${AppVersion}][E]: ${msg}`)
|
||||
export const Log = {
|
||||
I: (msg) => console.log(`[App_v ${Const.AppVersion}][I]: ${msg}`),
|
||||
W: (msg) => console.warn(`[App_v ${Const.AppVersion}][W]: ${msg}`),
|
||||
E: (msg) => console.error(`[App_v ${Const.AppVersion}][E]: ${msg}`)
|
||||
};
|
||||
|
||||
/**
|
||||
@ -18,7 +20,7 @@ const Log = {
|
||||
* @param {[object]} data - Request payload (method must be post)
|
||||
* @returns {Promise<XMLHttpRequest>} The completed request
|
||||
*/
|
||||
const JsonXHR = async function (method, url, data) {
|
||||
export async function JsonXHR(method, url, data) {
|
||||
return await XHR(method, url, JSON.stringify(data), {
|
||||
'Content-Type': 'application/json'
|
||||
});
|
||||
@ -35,7 +37,7 @@ const JsonXHR = async function (method, url, data) {
|
||||
* @param {[function]} editrequest - Function that can edit the request before its sent
|
||||
* @returns {Promise<XMLHttpRequest>} The completed request
|
||||
*/
|
||||
const XHR = function (method, url, data, headers, uploadprogress, downloadprogress, editrequest) {
|
||||
export function XHR(method, url, data, headers, uploadprogress, downloadprogress, editrequest) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
let x = new XMLHttpRequest();
|
||||
x.onreadystatechange = function (ev) {
|
||||
@ -76,7 +78,7 @@ const XHR = function (method, url, data, headers, uploadprogress, downloadprogre
|
||||
/**
|
||||
* Calls api handler
|
||||
*/
|
||||
const Api = {
|
||||
export const Api = {
|
||||
DoRequest: async function (req) {
|
||||
return JSON.parse((await JsonXHR('POST', '/api', req)).response);
|
||||
},
|
||||
@ -118,7 +120,7 @@ const Api = {
|
||||
/**
|
||||
* Generic util functions
|
||||
*/
|
||||
const Utils = {
|
||||
export const Utils = {
|
||||
/**
|
||||
* Formats an ArrayBuffer to hex
|
||||
* @param {ArrayBuffer} buffer - Input data to convert to hex
|
||||
@ -149,22 +151,22 @@ const Utils = {
|
||||
*/
|
||||
FormatBytes: (b, f) => {
|
||||
f = typeof f === 'number' ? 2 : f;
|
||||
if (b >= YiB)
|
||||
return (b / YiB).toFixed(f) + ' YiB';
|
||||
if (b >= ZiB)
|
||||
return (b / ZiB).toFixed(f) + ' ZiB';
|
||||
if (b >= EiB)
|
||||
return (b / EiB).toFixed(f) + ' EiB';
|
||||
if (b >= PiB)
|
||||
return (b / PiB).toFixed(f) + ' PiB';
|
||||
if (b >= TiB)
|
||||
return (b / TiB).toFixed(f) + ' TiB';
|
||||
if (b >= GiB)
|
||||
return (b / GiB).toFixed(f) + ' GiB';
|
||||
if (b >= MiB)
|
||||
return (b / MiB).toFixed(f) + ' MiB';
|
||||
if (b >= kiB)
|
||||
return (b / kiB).toFixed(f) + ' KiB';
|
||||
if (b >= Const.YiB)
|
||||
return (b / Const.YiB).toFixed(f) + ' YiB';
|
||||
if (b >= Const.ZiB)
|
||||
return (b / Const.ZiB).toFixed(f) + ' ZiB';
|
||||
if (b >= Const.EiB)
|
||||
return (b / Const.EiB).toFixed(f) + ' EiB';
|
||||
if (b >= Const.PiB)
|
||||
return (b / Const.PiB).toFixed(f) + ' PiB';
|
||||
if (b >= Const.TiB)
|
||||
return (b / Const.TiB).toFixed(f) + ' TiB';
|
||||
if (b >= Const.GiB)
|
||||
return (b / Const.GiB).toFixed(f) + ' GiB';
|
||||
if (b >= Const.MiB)
|
||||
return (b / Const.MiB).toFixed(f) + ' MiB';
|
||||
if (b >= Const.kiB)
|
||||
return (b / Const.kiB).toFixed(f) + ' KiB';
|
||||
return b.toFixed(f) + ' B'
|
||||
}
|
||||
};
|
@ -84,4 +84,6 @@ const VBF = {
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export { VBF };
|
@ -1,16 +1,27 @@
|
||||
import { Api, Utils, Log, $ } from './Util.js';
|
||||
import { FileDownloader } from './FileDownloader.js';
|
||||
import { base64_to_bytes } from 'asmcrypto.js';
|
||||
|
||||
/**
|
||||
* @constructor Creates an instance of the ViewManager
|
||||
*/
|
||||
const ViewManager = function () {
|
||||
export function ViewManager() {
|
||||
this.id = null;
|
||||
this.key = null;
|
||||
this.iv = null;
|
||||
|
||||
this.ParseUrlHash = function () {
|
||||
let hs = window.location.hash.substr(1).split(':');
|
||||
this.id = hs[0];
|
||||
this.key = hs[1];
|
||||
this.iv = hs[2];
|
||||
if (window.location.hash.indexOf(':') !== -1) {
|
||||
let hs = window.location.hash.substr(1).split(':');
|
||||
this.id = hs[0];
|
||||
this.key = hs[1];
|
||||
this.iv = hs[2];
|
||||
} else if (window.location.hash.length === 73) { //base64 encoded #id:key:iv
|
||||
let hs = base64_to_bytes(window.location.hash.substr(1));
|
||||
this.id = Utils.ArrayToHex(hs.slice(0, 20));
|
||||
this.key = Utils.ArrayToHex(hs.slice(20, 36));
|
||||
this.iv = Utils.ArrayToHex(hs.slice(36));
|
||||
}
|
||||
};
|
||||
|
||||
this.LoadView = async function () {
|
||||
@ -104,6 +115,4 @@ const ViewManager = function () {
|
||||
document.body.appendChild(st);
|
||||
}
|
||||
};
|
||||
|
||||
this.LoadView();
|
||||
};
|
@ -46,10 +46,16 @@
|
||||
return "$this->DocumentRoot/$this->UploadFolder";
|
||||
}
|
||||
|
||||
/**
|
||||
* $id should be the ripemd160(hmac-sha256(file, key)) as hex
|
||||
*/
|
||||
public function GetRelativeFilePath($id) : string {
|
||||
return "$this->UploadFolder/$id";
|
||||
return $this->UploadFolder . "/" . $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* $id should be the ripemd160(hmac-sha256(file, key)) as hex
|
||||
*/
|
||||
public function GetAbsoluteFilePath($id) : string {
|
||||
return $this->GetUploadDirAbsolute() . "/" . $id;
|
||||
}
|
||||
@ -94,7 +100,7 @@
|
||||
}
|
||||
|
||||
public function StoreV1File($bf, $file) : ?string {
|
||||
$id = gmp_strval(gmp_init("0x" . hash(Config::$Instance->public_hash_algo, $bf->Hash)), 62);
|
||||
$id = hash(Config::$Instance->public_hash_algo, $bf->Hash);
|
||||
|
||||
$input = fopen($file, "rb");
|
||||
$res = $this->StoreFile($input, $id);
|
||||
@ -115,7 +121,7 @@
|
||||
$hash = unpack("H64hash256", fread($input_temp, 32));
|
||||
fclose($input_temp);
|
||||
|
||||
$id = gmp_strval(gmp_init("0x" . hash(Config::$Instance->public_hash_algo, $hash["hash256"])), 62);
|
||||
$id = hash(Config::$Instance->public_hash_algo, $hash["hash256"]);
|
||||
$file_path = $this->GetAbsoluteFilePath($id);
|
||||
if(!file_exists($file_path)){
|
||||
rename($temp_name, $file_path);
|
||||
@ -124,6 +130,9 @@
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* $id should be formatted base62 filename
|
||||
*/
|
||||
public function GetFileSize($id) : int {
|
||||
return filesize($this->GetAbsoluteFilePath($id));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user