mirror of
https://github.com/rikkaneko/paste.git
synced 2025-06-06 16:45:41 +00:00
Implentment large paste upload (frontend)
Signed-off-by: Joe Ma <rikkaneko23@gmail.com>
This commit is contained in:
parent
2f67469ef5
commit
72c965787f
2 changed files with 105 additions and 41 deletions
|
@ -26,7 +26,7 @@
|
||||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.9.1/font/bootstrap-icons.min.css"
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.9.1/font/bootstrap-icons.min.css"
|
||||||
rel="stylesheet">
|
rel="stylesheet">
|
||||||
<link href="/static/paste.css" rel="stylesheet">
|
<link href="static/paste.css" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar sticky-top navbar-expand-lg navbar-dark bg-dark" id="navbar">
|
<nav class="navbar sticky-top navbar-expand-lg navbar-dark bg-dark" id="navbar">
|
||||||
|
@ -179,7 +179,7 @@
|
||||||
This service is primarily designed for own usage and interest only.<br>
|
This service is primarily designed for own usage and interest only.<br>
|
||||||
All data may be deleted or expired without any notification and guarantee. Please <b>DO NOT</b> abuse this
|
All data may be deleted or expired without any notification and guarantee. Please <b>DO NOT</b> abuse this
|
||||||
service.
|
service.
|
||||||
The limit for file upload is <b>10 MB</b> and the paste will be kept for <b>28 days</b> only by default.<br>
|
The limit for file upload is <b>250 MB</b> and the paste will be kept for <b>28 days</b> only by default.<br>
|
||||||
The source code is available in my GitHub repository <a
|
The source code is available in my GitHub repository <a
|
||||||
href="https://github.com/rikkaneko/paste">[here]</a>.<br>
|
href="https://github.com/rikkaneko/paste">[here]</a>.<br>
|
||||||
This webpage is designed for upload files only.
|
This webpage is designed for upload files only.
|
||||||
|
@ -271,7 +271,8 @@
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.6/umd/popper.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.6/umd/popper.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.min.js"></script>
|
||||||
<script src="/static/paste.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js"></script>
|
||||||
|
<script src="static/paste.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
/// <reference path="../../node_modules/@types/bootstrap/index.d.ts" />
|
/// <reference path="../../node_modules/@types/bootstrap/index.d.ts" />
|
||||||
|
|
||||||
|
const ENDPOINT = '';
|
||||||
|
|
||||||
let input_div = {
|
let input_div = {
|
||||||
file: null,
|
file: null,
|
||||||
text: null,
|
text: null,
|
||||||
|
@ -113,6 +115,16 @@ function build_paste_modal(paste_info, show_qrcode = true, saved = true, build_o
|
||||||
if (!build_only) modal.show();
|
if (!build_only) modal.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param file {File}
|
||||||
|
* @returns {str}
|
||||||
|
*/
|
||||||
|
async function get_file_hash(file) {
|
||||||
|
const word_arr = CryptoJS.lib.WordArray.create(await file.arrayBuffer());
|
||||||
|
const file_hash = CryptoJS.SHA256(word_arr).toString(CryptoJS.enc.Hex);
|
||||||
|
return file_hash;
|
||||||
|
}
|
||||||
|
|
||||||
$(function () {
|
$(function () {
|
||||||
input_div.file = $('#file_upload_layout');
|
input_div.file = $('#file_upload_layout');
|
||||||
input_div.text = $('#text_input_layout');
|
input_div.text = $('#text_input_layout');
|
||||||
|
@ -170,11 +182,11 @@ $(function () {
|
||||||
title.val(this.files[0]?.name || '');
|
title.val(this.files[0]?.name || '');
|
||||||
file_stat.text(`${this.files[0]?.type || 'application/octet-stream'}, ${size}`);
|
file_stat.text(`${this.files[0]?.type || 'application/octet-stream'}, ${size}`);
|
||||||
|
|
||||||
// Check length
|
// Check length <= 250MB
|
||||||
if (bytes > 10485760) {
|
if (bytes > 262144000) {
|
||||||
inputs.file.addClass('is-invalid');
|
inputs.file.addClass('is-invalid');
|
||||||
file_stat.addClass('text-danger');
|
file_stat.addClass('text-danger');
|
||||||
file_stat.text('The uploaded file is larger than the 10 MB limit.');
|
file_stat.text('The uploaded file is larger than the 250MB limit.');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -216,6 +228,7 @@ $(function () {
|
||||||
const form = $('#upload_form')[0];
|
const form = $('#upload_form')[0];
|
||||||
let formdata = new FormData(form);
|
let formdata = new FormData(form);
|
||||||
const type = formdata.get('paste-type');
|
const type = formdata.get('paste-type');
|
||||||
|
/** @type {File} */
|
||||||
const content = formdata.get('u');
|
const content = formdata.get('u');
|
||||||
|
|
||||||
inputs[type].trigger('input');
|
inputs[type].trigger('input');
|
||||||
|
@ -230,45 +243,95 @@ $(function () {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type) {
|
upload_button.prop('disabled', true);
|
||||||
case 'file':
|
upload_button.text('Waiting...');
|
||||||
formdata.set('paste-type', 'paste');
|
|
||||||
break;
|
|
||||||
case 'text':
|
|
||||||
formdata.set('paste-type', 'paste');
|
|
||||||
break;
|
|
||||||
case 'url':
|
|
||||||
formdata.set('paste-type', 'link');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Hanlde large paste (> 10MB)
|
||||||
|
if (content.size > 10485760) {
|
||||||
|
const file_hash = await get_file_hash(content);
|
||||||
|
const params = {
|
||||||
|
title: content.name,
|
||||||
|
'file-size': content.size,
|
||||||
|
'file-sha256-hash': file_hash,
|
||||||
|
'mime-type': content.type,
|
||||||
|
'read-limit': formdata.get('read-limit') || undefined,
|
||||||
|
pass: formdata.get('pass') || undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Remove empty entries
|
||||||
|
const filtered = new FormData();
|
||||||
|
Object.entries(params).forEach(([key, val]) => {
|
||||||
|
if (val) filtered.set(key, val);
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Retrieve presigned URL for upload large paste
|
||||||
|
const res = await fetch(`${ENDPOINT}/v2/large_upload/create`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: filtered,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`Unable to create paste: ${(await res.text()) || `${res.status} ${res.statusText}`}`);
|
||||||
|
}
|
||||||
|
// Upload the paste to the endpoint
|
||||||
|
upload_button.text('Uploading...');
|
||||||
|
const create_result = await res.json();
|
||||||
|
const res1 = await fetch(create_result.signed_url, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'X-Amz-Content-Sha256': file_hash,
|
||||||
|
},
|
||||||
|
body: content,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res1.ok) {
|
||||||
|
throw new Error(`Unable to upload paste: ${(await res1.text()) || `${res.status} ${res.statusText}`}`);
|
||||||
|
}
|
||||||
|
// Finialize the paste
|
||||||
|
const res2 = await fetch(`${ENDPOINT}/large_upload/complete/${create_result.uuid}`, {
|
||||||
|
method: 'POST',
|
||||||
|
});
|
||||||
|
if (res2.ok) {
|
||||||
|
const complete_result = await res2.json();
|
||||||
|
build_paste_modal(complete_result.paste_info, show_qrcode);
|
||||||
|
show_pop_alert(`Paste #${complete_result.paste_info.uuid} created!`, 'alert-success');
|
||||||
|
pass_input.val('');
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unable to finialize paste: ${(await res2.text()) || `${res.status} ${res.statusText}`}`);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('error', err);
|
||||||
|
show_pop_alert(err.message, 'alert-danger');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handle normal paste (<= 25MB)
|
||||||
// Remove empty entries
|
// Remove empty entries
|
||||||
let filtered = new FormData();
|
let filtered = new FormData();
|
||||||
formdata.forEach((val, key) => {
|
formdata.forEach((val, key) => {
|
||||||
if (val) filtered.set(key, val);
|
if (val) filtered.set(key, val);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Request JSON response
|
// Request JSON response
|
||||||
filtered.set('json', '1');
|
filtered.set('json', '1');
|
||||||
upload_button.prop('disabled', true);
|
|
||||||
upload_button.text('Uploading...');
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/', {
|
const res = await fetch(`${ENDPOINT}/`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: filtered,
|
body: filtered,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const paste_info = await res.json();
|
const paste_info = await res.json();
|
||||||
show_pop_alert('Paste created!', 'alert-success');
|
|
||||||
pass_input.val('');
|
|
||||||
build_paste_modal(paste_info, show_qrcode);
|
build_paste_modal(paste_info, show_qrcode);
|
||||||
|
show_pop_alert(`Paste #${paste_info.uuid} created!`, 'alert-success');
|
||||||
|
pass_input.val('');
|
||||||
} else {
|
} else {
|
||||||
show_pop_alert('Unable to create paste', 'alert-warning');
|
throw new Error('Unable to upload paste');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('error', err);
|
console.log('error', err);
|
||||||
show_pop_alert(err.message, 'alert-danger');
|
show_pop_alert(err.message, 'alert-danger');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
upload_button.prop('disabled', false);
|
upload_button.prop('disabled', false);
|
||||||
upload_button.text('Upload');
|
upload_button.text('Upload');
|
||||||
|
@ -304,7 +367,7 @@ $(function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/${uuid}/settings?${new URLSearchParams({ json: '1' })}`);
|
const res = await fetch(`${ENDPOINT}/${uuid}/settings?${new URLSearchParams({ json: '1' })}`);
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const paste_info = await res.json();
|
const paste_info = await res.json();
|
||||||
build_paste_modal(paste_info, show_qrcode, false);
|
build_paste_modal(paste_info, show_qrcode, false);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue