add upload form & script

This commit is contained in:
Hajamieli 2023-08-12 06:23:18 +03:00
parent ab0d215f6a
commit 78da18a80b
7 changed files with 299 additions and 0 deletions

BIN
site/i/t_192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
site/i/t_48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
site/i/t_96.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

184
site/index.html Normal file
View File

@ -0,0 +1,184 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>media</title>
<link rel="shortcut icon" type="image/ico" href="" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="renderer" content="webkit" />
<meta name="keywords" content="media" />
<meta name="description" content="PWA, Web PWA, Web App" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Window-target" content="_top" />
<meta http-equiv="content-Type" content="text/html;charset=utf-8" />
<meta http-equiv="Content-Language" content="en" />
<meta http-equiv="imagetoolbar" content="false" />
<link rel="icon" sizes="192x192" href="./i/t_192.png" />
<link rel="apple-touch-icon" href="./i/t_96.png" />
<meta name="msapplication-square310x310logo" content="./i/t_96.png" />
<meta
name="apple-mobile-web-app-status-bar-style"
content="black-translucent"
/>
<link rel="apple-touch-startup-image" href="./i/t_192.png" />
<link rel="manifest" href="manifest.json" />
<style type="text/css">
:root {
color-scheme: light dark;
}
</style>
<script>
if ("serviceWorker" in navigator) {
navigator.serviceWorker
.register("./sw.js")
.then(function (registration) {
console.log(
"ServiceWorker registration successful with scope: ",
registration.scope
);
})
.catch(function (err) {
console.log("ServiceWorker registration failed: ", err);
});
}
window.addEventListener("beforeinstallprompt", function (e) {
e.userChoice.then(function (choiceResult) {
console.log(choiceResult.outcome);
if (choiceResult.outcome == "dismissed") {
console.log("User cancelled home screen install");
} else {
console.log("User added to home screen");
}
});
});
</script>
</head>
<body>
<p style="color:red">!! Please report errors, bugs and weird shit to Hajamieli !!</p>
<form id="userform" method="POST" enctype="multipart/form-data">
<h3>Upload a file</h3>
<div id="upload-preview"></div>
<div id="field"><label class="label"><input type="file" multiple="true" accept="image/*,video/*,.pdf,.epub" name="media"><span>Select an file</span></label></div>
<input id="submit" type="submit" name="upload" value="Upload">
</form>
<p>styles coming soon? propably...</p>
<div id="results"></div>
<script type="text/javascript">
const form = document.getElementById("userform")
const submit = document.getElementById("submit")
const results = document.getElementById("results")
const chunksize = 1000000;
const splitter = (file, hash) => {
let numberofChunks = Math.ceil(file.byteLength/chunksize);
let left = 0
let chunks = []
for (let i = 0; i < numberofChunks; i++) {
const chunkForm = new FormData();
let contentRange = "";
let chunk;
if(left+chunksize <= file.byteLength){
contentRange = `${left}-${left+chunksize}`
chunk = file.slice(left,left+chunksize)
left += chunksize
} else {
contentRange = `${left}-${file.byteLength}`
chunk = file.slice(left,file.byteLength)
}
chunkForm.append('file', new Blob([chunk], {type:"video/webm"}))
chunks.push(
fetch("/uploadchunk", {
method: "POST",
headers: {
"Content-Number": i,
"Content-Range": `bytes ${contentRange}/${file.byteLength}`,
"File-Hash": hash
},
body: chunkForm
}).then(res => Promise.resolve(res.status))
)
}
return chunks
}
async function digest(data){
const hash = await crypto.subtle.digest("SHA-256", data);
return hash
}
const processFile = file => {
const fr = new FileReader()
fr.readAsArrayBuffer(file)
fr.addEventListener("loadend", e => {
digest(e.target.result).then(digestBuffer => {
const hashAsString = Array.from(new Uint8Array(digestBuffer)).map((b) => b.toString(16).padStart(2, "0")).join(""); // hex the buffer for readability
let details = JSON.stringify({"usercode": "anonymous", "hash": hashAsString, "size": file.size, "type": file.type})
fetch(`/announce`, {
method: "POST",
headers:{
"Content-Type": "json",
},
body: details
})
.then(res => {
if(res.status == 200){
Promise.all(splitter(e.target.result,hashAsString))
.then((values) => {
if(values.every(x => x === 200)){
fetch(`/finish`, {
method: "POST",
headers: {
"Content-Type": "json",
},
body: details
}).then(res => res.json())
.then(res => {
console.log(res)
results.innerHTML += `<a href="/${res.file}">https://i.hjmt.xyz/${res.file}</a><br>`
})
}else{
alert("upload failed")
}
})
}else if(res.status == 409){
alert("file exists!")
}else if(res.status == 403){
alert("get a usertoken for bigger files")
}
})
})
})
}
submit.addEventListener("click", e => {
e.preventDefault();
for (const file of form.media.files){
processFile(file)
}
})
</script>
</body>
</html>

28
site/manifest.json Normal file
View File

@ -0,0 +1,28 @@
{
"short_name": "hajamedia",
"name": "hajamielis file hosting app",
"description": "upload files",
"version": "0.1",
"background_color": "#010e1c",
"theme_color": "#010e1c",
"display": "standalone",
"orientation": "portrait",
"icons": [
{
"src": "./i/t_48.png",
"type": "image/png",
"sizes": "48x48"
},
{
"src": "./i/t_96.png",
"type": "image/png",
"sizes": "96x96"
},
{
"src": "./i/t_192.png",
"type": "image/png",
"sizes": "192x192"
}
],
"start_url": "/"
}

84
site/script.js Normal file
View File

@ -0,0 +1,84 @@
const form = document.getElementById("userform")
const submit = document.getElementById("submit")
const results = document.getElementById("results")
const chunksize = 1000000;
const splitter = (file, hash) => {
let numberofChunks = Math.ceil(file.byteLength/chunksize);
let left = 0
let chunks = []
for (let i = 0; i < numberofChunks; i++) {
const chunkForm = new FormData();
let contentRange = "";
let chunk;
if(left+chunksize <= file.byteLength){
contentRange = `${left}-${left+chunksize}`
chunk = file.slice(left,left+chunksize)
left += chunksize
} else {
contentRange = `${left}-${file.byteLength}`
chunk = file.slice(left,file.byteLength)
}
chunkForm.append('file', new Blob([chunk], {type:"video/webm"}))
chunks.push(
fetch("/test", {
method: "POST",
headers: {
"Content-Number": i,
"Content-Range": `bytes ${contentRange}/${file.byteLength}`,
"File-Hash": hash
},
body: chunkForm
}))
}
return chunks
}
async function digest(data){
const hash = await crypto.subtle.digest("SHA-256", data);
return hash
}
const processFile = file => {
const fr = new FileReader()
fr.readAsArrayBuffer(file)
fr.addEventListener("loadend", e => {
digest(e.target.result).then(digestBuffer => {
const hashAsString = Array.from(new Uint8Array(digestBuffer)).map((b) => b.toString(16).padStart(2, "0")).join(""); // hex the buffer for readability
Promise.all(splitter(e.target.result,hashAsString))
.then((values) => {
fetch(`/finish`, {
method: "POST",
headers: {
"Content-Type": "json",
},
body: JSON.stringify({"hash": hashAsString, "type": file.type})
})
})
})
})
}
submit.addEventListener("click", e => {
e.preventDefault();
let fd = new FormData();
for (const file of form.media.files){
processFile(file)
fd.append('file', file)
console.log(file)
}
})

3
site/sw.js Normal file
View File

@ -0,0 +1,3 @@
self.addEventListener("fetch", function(event) {
console.log(`start server worker`)
});