refactor: modernize JavaScript with ES6+ syntax (arrow functions, destructuring, template literals)

This commit is contained in:
2026-03-01 10:00:57 +01:00
parent 2b24d18df2
commit 0de588b2d6

View File

@@ -112,16 +112,18 @@ pub fn not_found() -> String {
fn decrypt_js(encrypted_content: String) -> String { fn decrypt_js(encrypted_content: String) -> String {
" "
(async function() { (async () => {
const encryptedContent = '" <> escape_js_string(encrypted_content) <> "'; const encryptedContent = '" <> escape_js_string(encrypted_content) <> "';
const hash = window.location.hash.slice(1); const hash = location.hash.slice(1);
const loadingEl = document.getElementById('loading'); const els = {
const errorEl = document.getElementById('error'); loading: document.getElementById('loading'),
const contentEl = document.getElementById('content-display'); error: document.getElementById('error'),
content: document.getElementById('content-display')
};
if (!hash) { if (!hash) {
loadingEl.classList.add('hidden'); els.loading.classList.add('hidden');
errorEl.classList.remove('hidden'); els.error.classList.remove('hidden');
return; return;
} }
@@ -130,29 +132,18 @@ fn decrypt_js(encrypted_content: String) -> String {
const encryptedBytes = Uint8Array.from(atob(encryptedContent), c => c.charCodeAt(0)); const encryptedBytes = Uint8Array.from(atob(encryptedContent), c => c.charCodeAt(0));
const keyBytes = Uint8Array.from(atob(keyBase64), c => c.charCodeAt(0)); const keyBytes = Uint8Array.from(atob(keyBase64), c => c.charCodeAt(0));
const iv = encryptedBytes.slice(0, 12); const iv = encryptedBytes.slice(0, 12);
const ciphertext = encryptedBytes.slice(12);
const key = await crypto.subtle.importKey( const key = await crypto.subtle.importKey('raw', keyBytes, { name: 'AES-GCM' }, false, ['decrypt']);
'raw', const decrypted = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, encryptedBytes.slice(12));
keyBytes,
{ name: 'AES-GCM' }, els.content.textContent = new TextDecoder().decode(decrypted);
false, els.loading.classList.add('hidden');
['decrypt'] els.error.classList.add('hidden');
); els.content.classList.remove('hidden');
const decrypted = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv: iv },
key,
ciphertext
);
const decoder = new TextDecoder();
const plaintext = decoder.decode(decrypted);
contentEl.textContent = plaintext;
loadingEl.classList.add('hidden');
errorEl.classList.add('hidden');
contentEl.classList.remove('hidden');
} catch (e) { } catch (e) {
console.error('Decryption failed:', e); console.error('Decryption failed:', e);
loadingEl.classList.add('hidden'); els.loading.classList.add('hidden');
errorEl.classList.remove('hidden'); els.error.classList.remove('hidden');
} }
})(); })();
" "
@@ -172,73 +163,57 @@ fn crypto_js() -> String {
async function encryptContent(content) { async function encryptContent(content) {
const encoder = new TextEncoder(); const encoder = new TextEncoder();
const data = encoder.encode(content); const data = encoder.encode(content);
const key = await crypto.subtle.generateKey( const key = await crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt']);
{ name: 'AES-GCM', length: 256 },
true,
['encrypt', 'decrypt']
);
const iv = crypto.getRandomValues(new Uint8Array(12)); const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt( const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, data);
{ name: 'AES-GCM', iv: iv },
key,
data
);
const keyData = await crypto.subtle.exportKey('raw', key); const keyData = await crypto.subtle.exportKey('raw', key);
const keyBytes = new Uint8Array(keyData); return {
const combined = new Uint8Array(iv.length + encrypted.byteLength); encrypted: btoa(String.fromCharCode(...new Uint8Array([...iv, ...new Uint8Array(encrypted)]))),
combined.set(iv); key: btoa(String.fromCharCode(...new Uint8Array(keyData)))
combined.set(new Uint8Array(encrypted), iv.length); };
const encryptedBase64 = btoa(String.fromCharCode(...combined));
const keyBase64 = btoa(String.fromCharCode(...keyBytes));
return { encrypted: encryptedBase64, key: keyBase64 };
} }
async function base64ToUrlSafeBase64(base64) { function base64ToUrlSafe(base64) {
let result = base64.split('+').join('-'); return base64.replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '');
result = result.split('/').join('_');
while (result.endsWith('=')) {
result = result.slice(0, -1);
}
return result;
} }
document.getElementById('paste-form').addEventListener('submit', async function(e) { function showShareUrl(url) {
const card = document.querySelector('.card');
const div = document.createElement('div');
div.className = 'share-url';
div.innerHTML = '<label>Share this URL</label>';
const input = document.createElement('input');
input.type = 'text';
input.readOnly = true;
input.value = url;
input.onclick = () => input.select();
div.appendChild(input);
card.insertBefore(div, card.firstChild);
input.select();
}
document.getElementById('paste-form').addEventListener('submit', async (e) => {
e.preventDefault(); e.preventDefault();
const content = document.getElementById('content').value; const content = document.getElementById('content').value;
const result = await encryptContent(content); const { encrypted, key } = await encryptContent(content);
document.getElementById('encrypted-content').value = result.encrypted; document.getElementById('encrypted-content').value = encrypted;
const response = await fetch('/', { const csrfToken = document.getElementById('csrf-token').value;
const res = await fetch('/', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'encrypted_content=' + encodeURIComponent(result.encrypted) + '&csrf_token=' + encodeURIComponent(document.getElementById('csrf-token').value) body: `encrypted_content=${encodeURIComponent(encrypted)}&csrf_token=${encodeURIComponent(csrfToken)}`
}); });
const html = await response.text();
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const keyUrlSafe = await base64ToUrlSafeBase64(result.key);
const url = new URL(window.location.href);
const pasteId = doc.querySelector('[data-paste-id]').getAttribute('data-paste-id');
const pasteUrl = url.origin + '/paste/' + pasteId + '#' + keyUrlSafe;
const html = await res.text();
const doc = new DOMParser().parseFromString(html, 'text/html');
const pasteId = doc.querySelector('[data-paste-id]')?.getAttribute('data-paste-id');
if (!pasteId) return;
const pasteUrl = `${location.origin}/paste/${pasteId}#${base64ToUrlSafe(key)}`;
document.body.replaceChildren(...doc.body.childNodes); document.body.replaceChildren(...doc.body.childNodes);
showShareUrl(pasteUrl);
const card = document.querySelector('.card'); history.replaceState({}, '', pasteUrl);
const urlDiv = document.createElement('div');
urlDiv.className = 'share-url';
const lbl = document.createElement('label');
lbl.textContent = 'Share this URL';
urlDiv.appendChild(lbl);
const inp = document.createElement('input');
inp.type = 'text';
inp.readOnly = true;
inp.value = pasteUrl;
inp.onclick = function() { this.select(); };
urlDiv.appendChild(inp);
card.insertBefore(urlDiv, card.firstChild);
window.history.replaceState({}, '', pasteUrl);
inp.select();
}); });
" "
} }