mirror of
https://github.com/rikkaneko/paste.git
synced 2025-06-06 16:45:41 +00:00
Add modal to show paste info (web)
Done paste upload logic (web) Add json response (paste) Signed-off-by: Joe Ma <rikkaneko23@gmail.com>
This commit is contained in:
parent
aa154b213e
commit
576a543703
6 changed files with 289 additions and 51 deletions
2
.idea/inspectionProfiles/Project_Default.xml
generated
2
.idea/inspectionProfiles/Project_Default.xml
generated
|
@ -4,10 +4,12 @@
|
||||||
<inspection_tool class="DuplicatedCode" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
<inspection_tool class="DuplicatedCode" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||||
<inspection_tool class="ES6MissingAwait" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
<inspection_tool class="ES6MissingAwait" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||||
<inspection_tool class="HtmlFormInputWithoutLabel" enabled="false" level="WARNING" enabled_by_default="false" />
|
<inspection_tool class="HtmlFormInputWithoutLabel" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="JSUnresolvedVariable" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||||
<inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
|
<inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
|
||||||
<option name="processCode" value="false" />
|
<option name="processCode" value="false" />
|
||||||
<option name="processLiterals" value="false" />
|
<option name="processLiterals" value="false" />
|
||||||
<option name="processComments" value="true" />
|
<option name="processComments" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
|
<inspection_tool class="TsLint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
</profile>
|
</profile>
|
||||||
</component>
|
</component>
|
84
src/index.ts
84
src/index.ts
|
@ -23,7 +23,8 @@ import dedent from 'dedent-js';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
const SERVICE_URL = 'pb.nekoul.com';
|
const SERVICE_URL = 'pb.nekoul.com';
|
||||||
const PASTE_INDEX_HTML_URL = 'https://raw.githubusercontent.com/rikkaneko/paste/main/paste.html';
|
const PASTE_INDEX_HTML_URL_v1 = 'https://raw.githubusercontent.com/rikkaneko/paste/main/web/v1/paste.html';
|
||||||
|
const PASTE_INDEX_HTML_URL = 'https://raw.githubusercontent.com/rikkaneko/paste/main/web/v2/paste.html';
|
||||||
const UUID_LENGTH = 4;
|
const UUID_LENGTH = 4;
|
||||||
|
|
||||||
export interface Env {
|
export interface Env {
|
||||||
|
@ -67,6 +68,22 @@ export default {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (path === '/v1' && method == 'GET') {
|
||||||
|
return await fetch(PASTE_INDEX_HTML_URL_v1, {
|
||||||
|
cf: {
|
||||||
|
cacheEverything: true,
|
||||||
|
},
|
||||||
|
}).then(value => {
|
||||||
|
return new Response(value.body, {
|
||||||
|
// Add the correct content-type to response header
|
||||||
|
headers: {
|
||||||
|
'content-type': 'text/html; charset=UTF-8;',
|
||||||
|
'cache-control': 'public, max-age=172800',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (path === '/') {
|
if (path === '/') {
|
||||||
switch (method) {
|
switch (method) {
|
||||||
// Fetch the HTML for uploading text/file
|
// Fetch the HTML for uploading text/file
|
||||||
|
@ -98,6 +115,7 @@ export default {
|
||||||
let read_limit: number | undefined;
|
let read_limit: number | undefined;
|
||||||
let need_qrcode: boolean = false;
|
let need_qrcode: boolean = false;
|
||||||
let paste_type: string | undefined;
|
let paste_type: string | undefined;
|
||||||
|
let reply_json: boolean = false;
|
||||||
// Content-Type: multipart/form-data (deprecated)
|
// Content-Type: multipart/form-data (deprecated)
|
||||||
if (content_type.includes('multipart/form-data')) {
|
if (content_type.includes('multipart/form-data')) {
|
||||||
const formdata = await request.formData();
|
const formdata = await request.formData();
|
||||||
|
@ -142,17 +160,24 @@ export default {
|
||||||
|
|
||||||
// Check if qrcode generation needed
|
// Check if qrcode generation needed
|
||||||
const qr = formdata.get('qrcode');
|
const qr = formdata.get('qrcode');
|
||||||
if (typeof qr === 'string' && qr.toLowerCase() === 'true' || qr === 'on') {
|
if (typeof qr === 'string' && qr === '1') {
|
||||||
need_qrcode = true;
|
need_qrcode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Paste API v2
|
// Check reply format
|
||||||
|
const json = formdata.get('json');
|
||||||
|
if (typeof json === 'string' && json === '1') {
|
||||||
|
reply_json = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paste API v2
|
||||||
} else {
|
} else {
|
||||||
title = headers.get('x-paste-title') || undefined;
|
title = headers.get('x-paste-title') || undefined;
|
||||||
mime_type = headers.get('x-paste-content-type') || undefined;
|
mime_type = headers.get('x-paste-content-type') || undefined;
|
||||||
password = headers.get('x-paste-pass') || undefined;
|
password = headers.get('x-paste-pass') || undefined;
|
||||||
paste_type = headers.get('x-paste-type') || undefined;
|
paste_type = headers.get('x-paste-type') || undefined;
|
||||||
need_qrcode = headers.get('x-paste-qr') === '1';
|
need_qrcode = headers.get('x-paste-qr') === '1';
|
||||||
|
reply_json = headers.get('x-json') === '1';
|
||||||
const count = headers.get('x-paste-read-limit') || '';
|
const count = headers.get('x-paste-read-limit') || '';
|
||||||
const n = parseInt(count);
|
const n = parseInt(count);
|
||||||
if (isNaN(n) || n <= 0) {
|
if (isNaN(n) || n <= 0) {
|
||||||
|
@ -235,7 +260,7 @@ export default {
|
||||||
|
|
||||||
// Key will be expired after 28 day if unmodified
|
// Key will be expired after 28 day if unmodified
|
||||||
ctx.waitUntil(env.PASTE_INDEX.put(uuid, JSON.stringify(descriptor), {expirationTtl: 2419200}));
|
ctx.waitUntil(env.PASTE_INDEX.put(uuid, JSON.stringify(descriptor), {expirationTtl: 2419200}));
|
||||||
return await get_paste_info(uuid, descriptor, env, is_browser, need_qrcode);
|
return await get_paste_info(uuid, descriptor, env, is_browser, need_qrcode, reply_json);
|
||||||
} else {
|
} else {
|
||||||
return new Response('Unable to upload the paste.\n', {
|
return new Response('Unable to upload the paste.\n', {
|
||||||
status: 500,
|
status: 500,
|
||||||
|
@ -462,22 +487,49 @@ export default {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
async function get_paste_info(uuid: string, descriptor: PasteIndexEntry, env: Env, use_html: boolean = true, need_qr: boolean = false): Promise<Response> {
|
async function get_paste_info(uuid: string, descriptor: PasteIndexEntry, env: Env,
|
||||||
|
use_html: boolean = true, need_qr: boolean = false, reply_json = false): Promise<Response> {
|
||||||
const created = new Date(descriptor.last_modified);
|
const created = new Date(descriptor.last_modified);
|
||||||
const expired = new Date(descriptor.last_modified + 2419200000);
|
const expired = new Date(descriptor.last_modified + 2419200000);
|
||||||
const link = `https://${SERVICE_URL}/${uuid}`;
|
const link = `https://${SERVICE_URL}/${uuid}`;
|
||||||
|
const paste_info = {
|
||||||
|
uuid,
|
||||||
|
link,
|
||||||
|
link_qr: 'https://qrcode.nekoul.com/?' + new URLSearchParams({q: link, type: 'svg'}),
|
||||||
|
type: descriptor.type ?? 'paste',
|
||||||
|
title: descriptor.title?.trim(),
|
||||||
|
mime_type: descriptor.mime_type,
|
||||||
|
human_readable_size: `${to_human_readable_size(descriptor.size)}`,
|
||||||
|
size: descriptor.size,
|
||||||
|
password: !!descriptor.password,
|
||||||
|
read_count_remain: descriptor.read_count_remain,
|
||||||
|
created: created.toISOString(),
|
||||||
|
expired: expired.toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reply with JSON
|
||||||
|
if (reply_json) {
|
||||||
|
return new Response(JSON.stringify(paste_info), {
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/json; charset=utf-8',
|
||||||
|
'cache-control': 'no-store',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plain text reply
|
||||||
let content = dedent`
|
let content = dedent`
|
||||||
id: ${uuid}
|
uuid: ${uuid}
|
||||||
link: ${link}
|
link: ${link}
|
||||||
type: ${descriptor.type ?? 'paste'}
|
type: ${paste_info.type ?? 'paste'}
|
||||||
title: ${descriptor.title?.trim() || '-'}
|
title: ${paste_info.title || '-'}
|
||||||
mime-type: ${descriptor.mime_type ?? '-'}
|
mime-type: ${paste_info.mime_type ?? '-'}
|
||||||
size: ${descriptor.size} bytes (${to_human_readable_size(descriptor.size)})
|
size: ${paste_info.size} bytes (${paste_info.human_readable_size})
|
||||||
password: ${(!!descriptor.password)}
|
password: ${paste_info.password}
|
||||||
remaining read count: ${descriptor.read_count_remain !== undefined ?
|
remaining read count: ${paste_info.read_count_remain !== undefined ?
|
||||||
descriptor.read_count_remain ? descriptor.read_count_remain : `0 (expired)` : '-'}
|
paste_info.read_count_remain ? paste_info.read_count_remain : `0 (expired)` : '-'}
|
||||||
created at ${created.toISOString()}
|
created at ${paste_info.created}
|
||||||
expired at ${expired.toISOString()}
|
expired at ${paste_info.expired}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Browser response
|
// Browser response
|
||||||
|
@ -492,7 +544,7 @@ async function get_paste_info(uuid: string, descriptor: PasteIndexEntry, env: En
|
||||||
<body>
|
<body>
|
||||||
<pre style="word-wrap: break-word; white-space: pre-wrap;
|
<pre style="word-wrap: break-word; white-space: pre-wrap;
|
||||||
font-family: 'Fira Mono', monospace; font-size: 16px;">${content}</pre>
|
font-family: 'Fira Mono', monospace; font-size: 16px;">${content}</pre>
|
||||||
${(need_qr) ? `<img src="${'https://qrcode.nekoul.com/?' + new URLSearchParams({q: link, type: 'svg'})}"
|
${(need_qr) ? `<img src="${paste_info.link_qr}"
|
||||||
alt="${link}" style="max-width: 280px">` : ''}
|
alt="${link}" style="max-width: 280px">` : ''}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -42,12 +42,15 @@
|
||||||
]
|
]
|
||||||
/* Specify type package names to be included without being referenced in a source file. */,
|
/* Specify type package names to be included without being referenced in a source file. */,
|
||||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||||
"resolveJsonModule": true /* Enable importing .json files */,
|
"resolveJsonModule": true
|
||||||
|
/* Enable importing .json files */,
|
||||||
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
|
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
|
||||||
|
|
||||||
/* JavaScript Support */
|
/* JavaScript Support */
|
||||||
"allowJs": true /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */,
|
"allowJs": true
|
||||||
"checkJs": true /* Enable error reporting in type-checked JavaScript files. */,
|
/* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */,
|
||||||
|
"checkJs": false
|
||||||
|
/* Enable error reporting in type-checked JavaScript files. */,
|
||||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
|
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
|
||||||
|
|
||||||
/* Emit */
|
/* Emit */
|
||||||
|
|
|
@ -83,7 +83,7 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<input id="show_qr_checkbox" type="checkbox" name="qrcode" checked>
|
<input id="show_qr_checkbox" type="checkbox" name="qrcode" checked value="1">
|
||||||
<label for="show_qr_checkbox">Show QR code on sumbitted</label>
|
<label for="show_qr_checkbox">Show QR code on sumbitted</label>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -169,7 +169,9 @@
|
||||||
document.getElementById('upload_file_form').addEventListener('submit', handle_submit_form, false);
|
document.getElementById('upload_file_form').addEventListener('submit', handle_submit_form, false);
|
||||||
</script>
|
</script>
|
||||||
<br>
|
<br>
|
||||||
<a href="https://nekoul.com">[Homepage]</a><a href="https://github.com/rikkaneko/paste#api-specification">[API]</a>
|
<a href="https://nekoul.com">[Homepage]</a>
|
||||||
|
<a href="https://github.com/rikkaneko/paste#api-specification">[API]</a>
|
||||||
|
<a href="https://pb.nekou.com">[Switch to new version]</a>
|
||||||
<p>© 2022 rikkaneko</p>
|
<p>© 2022 rikkaneko</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const endpoint = 'https://pb.nekoul.com';
|
||||||
|
|
||||||
let input_div = {
|
let input_div = {
|
||||||
file: null,
|
file: null,
|
||||||
text: null,
|
text: null,
|
||||||
|
@ -28,7 +30,15 @@ let inputs = {
|
||||||
url: null,
|
url: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
let selected_type = 'file';
|
let paste_modal = {
|
||||||
|
modal: null,
|
||||||
|
uuid: null,
|
||||||
|
qrcode: null,
|
||||||
|
title: null,
|
||||||
|
expired: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
let saved_modal = null;
|
||||||
|
|
||||||
function validate_url(path) {
|
function validate_url(path) {
|
||||||
let url;
|
let url;
|
||||||
|
@ -43,12 +53,13 @@ function validate_url(path) {
|
||||||
function show_pop_alert(message, alert_type = 'alert-primary') {
|
function show_pop_alert(message, alert_type = 'alert-primary') {
|
||||||
remove_pop_alert();
|
remove_pop_alert();
|
||||||
$('body').prepend(jQuery.parseHTML(
|
$('body').prepend(jQuery.parseHTML(
|
||||||
`<div class="alert ${alert_type} alert-dismissible position-absolute top-0 start-50 translate-middle-x outer"
|
`<div class="alert ${alert_type} alert-dismissible fade show position-absolute top-0 start-50 translate-middle-x"
|
||||||
style="margin-top: 80px; max-width: 500px; width: 80%" id="pop_alert" role="alert"> \
|
style="margin-top: 80px; max-width: 500px; width: 80%" id="pop_alert" role="alert"> \
|
||||||
<div>${message}</div> \
|
<div>${message}</div> \
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> \
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> \
|
||||||
</div>`,
|
</div>`,
|
||||||
));
|
));
|
||||||
|
window.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function remove_pop_alert() {
|
function remove_pop_alert() {
|
||||||
|
@ -57,6 +68,37 @@ function remove_pop_alert() {
|
||||||
alert.remove();
|
alert.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function build_paste_modal(paste_info, show_qrcode = true) {
|
||||||
|
// Show saved modal
|
||||||
|
if (!!!paste_info && !!!saved_modal) {
|
||||||
|
console.err('Invalid call to build_paste_modal().');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!!!paste_info) {
|
||||||
|
saved_modal.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
saved_modal = null;
|
||||||
|
paste_modal.uuid.text(paste_info.link);
|
||||||
|
paste_modal.uuid.prop('href', paste_info.link);
|
||||||
|
paste_modal.qrcode.prop('src', paste_info.link_qr);
|
||||||
|
paste_modal.qrcode.prop('alt', paste_info.link);
|
||||||
|
|
||||||
|
// Hide/Show QRCode
|
||||||
|
if (!show_qrcode) paste_modal.qrcode.addClass('d-none');
|
||||||
|
else paste_modal.qrcode.removeClass('d-none');
|
||||||
|
|
||||||
|
Object.entries(paste_info).forEach(([key, val]) => {
|
||||||
|
if (key.includes('link')) return;
|
||||||
|
$(`#paste_info_${key}`).text(val ?? '-');
|
||||||
|
});
|
||||||
|
let modal = new bootstrap.Modal(paste_modal.modal);
|
||||||
|
saved_modal = modal;
|
||||||
|
modal.show();
|
||||||
|
}
|
||||||
|
|
||||||
$(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');
|
||||||
|
@ -64,6 +106,9 @@ $(function () {
|
||||||
inputs.file = $('#file_upload');
|
inputs.file = $('#file_upload');
|
||||||
inputs.text = $('#text_input');
|
inputs.text = $('#text_input');
|
||||||
inputs.url = $('#url_input');
|
inputs.url = $('#url_input');
|
||||||
|
paste_modal.modal = $('#paste_modal');
|
||||||
|
paste_modal.uuid = $('#paste_uuid');
|
||||||
|
paste_modal.qrcode = $('#paste_qrcode');
|
||||||
|
|
||||||
let file_stat = $('#file_stats');
|
let file_stat = $('#file_stats');
|
||||||
let title = $('#paste_title');
|
let title = $('#paste_title');
|
||||||
|
@ -73,6 +118,15 @@ $(function () {
|
||||||
let upload_button = $('#upload_button');
|
let upload_button = $('#upload_button');
|
||||||
let url_validate_result = $('#url_validate_result');
|
let url_validate_result = $('#url_validate_result');
|
||||||
let tos_btn = $('#tos_btn');
|
let tos_btn = $('#tos_btn');
|
||||||
|
let show_saved_btn = $('#show_saved_button');
|
||||||
|
let go_btn = $('#go_button');
|
||||||
|
let go_id = $('#go_paste_id');
|
||||||
|
|
||||||
|
// Enable bootstrap tooltips
|
||||||
|
const tooltip_trigger_list = [].slice.call($('[data-bs-toggle="tooltip"]'));
|
||||||
|
const tooltip_list = tooltip_trigger_list.map(function (e) {
|
||||||
|
return new bootstrap.Tooltip(e);
|
||||||
|
});
|
||||||
|
|
||||||
inputs.file.on('change', function () {
|
inputs.file.on('change', function () {
|
||||||
inputs.file.removeClass('is-invalid');
|
inputs.file.removeClass('is-invalid');
|
||||||
|
@ -124,6 +178,7 @@ $(function () {
|
||||||
inputs.url.on('input', function () {
|
inputs.url.on('input', function () {
|
||||||
inputs.url.removeClass('is-invalid');
|
inputs.url.removeClass('is-invalid');
|
||||||
url_validate_result.removeClass('text-danger');
|
url_validate_result.removeClass('text-danger');
|
||||||
|
url_validate_result.text('');
|
||||||
if (!validate_url(this.value)) {
|
if (!validate_url(this.value)) {
|
||||||
inputs.url.addClass('is-invalid');
|
inputs.url.addClass('is-invalid');
|
||||||
url_validate_result.addClass('text-danger');
|
url_validate_result.addClass('text-danger');
|
||||||
|
@ -131,16 +186,15 @@ $(function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
upload_button.on('click', function () {
|
upload_button.on('click', async function () {
|
||||||
inputs[selected_type].trigger('input');
|
|
||||||
const form = $('#upload_form')[0];
|
const form = $('#upload_form')[0];
|
||||||
const formdata = new FormData(form);
|
let formdata = new FormData(form);
|
||||||
let content = {};
|
const type = formdata.get('paste-type');
|
||||||
formdata.forEach((val, key) => {
|
const content = formdata.get('u');
|
||||||
content[key] = val;
|
const show_qrcode = formdata.get('qrcode') === '1';
|
||||||
});
|
|
||||||
|
|
||||||
if (inputs[selected_type].hasClass('is-invalid') || !(!!content.u?.size || !!content.u?.length)) {
|
inputs[type].trigger('input');
|
||||||
|
if (inputs[type].hasClass('is-invalid') || !(!!content?.size || !!content?.length)) {
|
||||||
show_pop_alert('Please check your upload file or content', 'alert-danger');
|
show_pop_alert('Please check your upload file or content', 'alert-danger');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -150,13 +204,71 @@ $(function () {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Upload to pb service
|
switch (type) {
|
||||||
show_pop_alert('Paste created!', 'alert-success');
|
case 'file':
|
||||||
|
formdata.set('paste-type', 'paste');
|
||||||
|
break;
|
||||||
|
case 'text':
|
||||||
|
formdata.set('paste-type', 'paste');
|
||||||
|
break;
|
||||||
|
case 'url':
|
||||||
|
formdata.set('paste-type', 'link');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove empty entries
|
||||||
|
let filtered = new FormData();
|
||||||
|
formdata.forEach((val, key) => {
|
||||||
|
if (!!val) filtered.set(key, val);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Request JSON response
|
||||||
|
filtered.set('json', '1');
|
||||||
|
upload_button.prop('disabled', true);
|
||||||
|
upload_button.text('Uploading...');
|
||||||
|
try {
|
||||||
|
const res = await fetch(endpoint, {
|
||||||
|
method: 'POST',
|
||||||
|
body: filtered,
|
||||||
|
});
|
||||||
|
|
||||||
|
const paste_info = await res.json();
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
show_pop_alert('Paste created!', 'alert-success');
|
||||||
|
pass_input.val('');
|
||||||
|
build_paste_modal(paste_info, show_qrcode);
|
||||||
|
show_saved_btn.prop('disabled', false);
|
||||||
|
} else {
|
||||||
|
show_pop_alert('Unable to create paste', 'alert-warning');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('error', err);
|
||||||
|
show_pop_alert(err.message, 'alert-danger');
|
||||||
|
}
|
||||||
|
|
||||||
|
upload_button.prop('disabled', false);
|
||||||
|
upload_button.text('Upload');
|
||||||
|
});
|
||||||
|
|
||||||
|
show_saved_btn.on('click', function () {
|
||||||
|
if (!!!saved_modal) {
|
||||||
|
show_pop_alert('No saved paste found.', 'alert-warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
saved_modal.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
go_btn.on('click', function () {
|
||||||
|
const uuid = go_id.val();
|
||||||
|
if (uuid.length !== 4) {
|
||||||
|
show_pop_alert('Invalid Paste ID.', 'alert-warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.open(`https://pb.nekoul.com/${uuid}`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function select_input_type(name) {
|
function select_input_type(name) {
|
||||||
selected_type = name;
|
|
||||||
Object.keys(input_div).forEach(key => {
|
Object.keys(input_div).forEach(key => {
|
||||||
input_div[key].collapse('hide');
|
input_div[key].collapse('hide');
|
||||||
inputs[key].prop('disabled', true);
|
inputs[key].prop('disabled', true);
|
||||||
|
|
|
@ -39,15 +39,19 @@
|
||||||
<div class="collapse navbar-collapse" id="navbar_supported_content">
|
<div class="collapse navbar-collapse" id="navbar_supported_content">
|
||||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" aria-current="page" href="https://nekoul.com">Home</a>
|
<a class="nav-link active" aria-current="page" href="https://nekoul.com">Home</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="https://github.com/rikkaneko/paste#api-specification">API</a>
|
<a class="nav-link active" href="https://github.com/rikkaneko/paste#api-specification">API</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="https://pb.nekoul.com/v1">Switch to old version</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<form class="d-flex">
|
<form class="d-flex">
|
||||||
<input class="form-control me-2" type="search" placeholder="" aria-label="go">
|
<input class="form-control me-2" type="search" placeholder="Paste ID" aria-label="go" id="go_paste_id"
|
||||||
<button class="btn btn-outline-success" type="button">Go</button>
|
data-bs-toggle="tooltip" data-bs-placement="bottom" title="Paste ID should be in 4 characters.">
|
||||||
|
<button class="btn btn-outline-success" type="button" id="go_button">Go</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -58,13 +62,13 @@
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<div><label class="form-label">Paste Type</label></div>
|
<div><label class="form-label">Paste Type</label></div>
|
||||||
<div class="btn-group w-100" role="group" aria-label="Paste type group">
|
<div class="btn-group w-100" role="group" aria-label="Paste type group">
|
||||||
<input type="radio" class="btn-check" name="paste_type" id="paste_type_file" autocomplete="off" checked
|
<input type="radio" class="btn-check" name="paste-type" id="paste_type_file" autocomplete="off" checked
|
||||||
onclick="select_input_type('file')" value="file">
|
onclick="select_input_type('file')" value="file">
|
||||||
<label class="btn btn-outline-primary" for="paste_type_file">File</label>
|
<label class="btn btn-outline-primary" for="paste_type_file">File</label>
|
||||||
<input type="radio" class="btn-check" name="paste_type" id="paste_type_text" autocomplete="off"
|
<input type="radio" class="btn-check" name="paste-type" id="paste_type_text" autocomplete="off"
|
||||||
onclick="select_input_type('text')" value="text">
|
onclick="select_input_type('text')" value="text">
|
||||||
<label class="btn btn-outline-primary" for="paste_type_text">Text</label>
|
<label class="btn btn-outline-primary" for="paste_type_text">Text</label>
|
||||||
<input type="radio" class="btn-check" name="paste_type" id="paste_type_url" autocomplete="off"
|
<input type="radio" class="btn-check" name="paste-type" id="paste_type_url" autocomplete="off"
|
||||||
onclick="select_input_type('url')" value="url">
|
onclick="select_input_type('url')" value="url">
|
||||||
<label class="btn btn-outline-primary" for="paste_type_url">URL</label>
|
<label class="btn btn-outline-primary" for="paste_type_url">URL</label>
|
||||||
</div>
|
</div>
|
||||||
|
@ -131,7 +135,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-check mb-3">
|
<div class="form-check mb-3">
|
||||||
<input class="form-check-input" type="checkbox" value="" id="show_qrcode_checkbox" name="qrcode" checked>
|
<input class="form-check-input" type="checkbox" id="show_qrcode_checkbox" name="qrcode" checked value="1">
|
||||||
<label class="form-check-label" for="show_qrcode_checkbox">
|
<label class="form-check-label" for="show_qrcode_checkbox">
|
||||||
Show QR code after upload
|
Show QR code after upload
|
||||||
</label>
|
</label>
|
||||||
|
@ -141,33 +145,37 @@
|
||||||
<div class="form-check mb-3">
|
<div class="form-check mb-3">
|
||||||
<input class="form-check-input" type="checkbox" value="" id="tos_btn" required>
|
<input class="form-check-input" type="checkbox" value="" id="tos_btn" required>
|
||||||
<label class="form-check-label" for="tos_btn">
|
<label class="form-check-label" for="tos_btn">
|
||||||
I understand <a class="link-primary" data-bs-toggle="modal" data-bs-target="#tos_model" role="button">the terms and conditions</a>
|
I understand <a class="link-primary" data-bs-toggle="modal" data-bs-target="#tos_modal" role="button">the terms
|
||||||
|
and conditions</a>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3 text-end">
|
<div class="mb-3 text-end">
|
||||||
|
<button type="button" class="btn btn-secondary me-2" id="show_saved_button" disabled>Show saved</button>
|
||||||
<button type="button" class="btn btn-primary" id="upload_button">Upload</button>
|
<button type="button" class="btn btn-primary" id="upload_button">Upload</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal fade" id="tos_model" tabindex="-1" aria-labelledby="tos_model_label" aria-hidden="true">
|
<div class="modal fade" id="tos_modal" tabindex="-1" aria-labelledby="tos_modal_label" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-scrollable">
|
<div class="modal-dialog modal-dialog-scrollable">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="tos_model_label">Terms and conditions</h5>
|
<h5 class="modal-title" id="tos_modal_label">Terms and conditions</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<h3>Paste Service</h3>
|
<h3>Paste Service</h3>
|
||||||
<p>
|
<p>
|
||||||
<a href="https://pb.nekoul.com">pb.nekoul.com</a> is a pastebin-like service hosted on Cloudflare Worker.<br>
|
<a href="https://pb.nekoul.com">pb.nekoul.com</a> is a pastebin-like service hosted on Cloudflare Worker.
|
||||||
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.<br>
|
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>10 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 href="https://github.com/rikkaneko/paste">[here]</a>.<br>
|
The source code is available in my GitHub repository <a
|
||||||
|
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.
|
||||||
For other operations like changing paste settings and deleting paste, please make use of the
|
For other operations like changing paste settings and deleting paste, please make use of the
|
||||||
<a href="https://github.com/rikkaneko/paste#api-specification">API call</a> with <a
|
<a href="https://github.com/rikkaneko/paste#api-specification">API call</a> with <a
|
||||||
|
@ -175,7 +183,66 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Close</button>
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="paste_modal" tabindex="-1" aria-labelledby="paste_modal_label" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-scrollable">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="paste_modal_label">Paste</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="mb-2 text-center fs-4">
|
||||||
|
<a href="" class="link-primary" id="paste_uuid"></a>
|
||||||
|
</div>
|
||||||
|
<img src="" class="mb-3 rounded mx-auto d-block w-75" alt="" id="paste_qrcode" style="max-width: 280px">
|
||||||
|
<div class="mb-3 w-75 mx-auto">
|
||||||
|
<table class="table table-striped table-bordered align-middle caption-top">
|
||||||
|
<caption>Paste information</caption>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="text-center col-3">Paste ID</td>
|
||||||
|
<td class="text-center col-6" id="paste_info_uuid">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-center col-3">Title</td>
|
||||||
|
<td class="text-center col-6" id="paste_info_title">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-center col-3">Type</td>
|
||||||
|
<td class="text-center col-6" id="paste_info_type">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-center col-3">Size</td>
|
||||||
|
<td class="text-center col-6" id="paste_info_human_readable_size">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-center col-3">Password</td>
|
||||||
|
<td class="text-center col-6" id="paste_info_password">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-center col-3">Read limit</td>
|
||||||
|
<td class="text-center col-6" id="paste_info_read_count_remain">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-center col-3">Created</td>
|
||||||
|
<td class="text-center col-6" id="paste_info_created">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-center col-3">Expired</td>
|
||||||
|
<td class="text-center col-6" id="paste_info_expired">-</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue