forked from snxraven/p2ns
Updates peer.paste
This commit is contained in:
@@ -664,6 +664,11 @@ function renderPastePage(paste) {
|
|||||||
input{flex:1;background:#020617;color:#e2e8f0;border:1px solid #334155;border-radius:8px;padding:.5rem}
|
input{flex:1;background:#020617;color:#e2e8f0;border:1px solid #334155;border-radius:8px;padding:.5rem}
|
||||||
button{background:#2563eb;color:#fff;border:0;border-radius:8px;padding:.5rem .85rem;cursor:pointer}
|
button{background:#2563eb;color:#fff;border:0;border-radius:8px;padding:.5rem .85rem;cursor:pointer}
|
||||||
.preview{white-space:pre-wrap;word-break:break-word}
|
.preview{white-space:pre-wrap;word-break:break-word}
|
||||||
|
.actions{display:flex;gap:.5rem;margin:.75rem 0 0}
|
||||||
|
.btn{background:#1d4ed8;color:#fff;border:0;border-radius:8px;padding:.45rem .8rem;cursor:pointer;transition:transform 120ms ease, background-color 120ms ease}
|
||||||
|
.btn:hover{transform:translateY(-1px);background:#2563eb}
|
||||||
|
.btn.ok{background:#16a34a;animation:pulse .55s ease}
|
||||||
|
@keyframes pulse{0%{transform:scale(1)}35%{transform:scale(1.04)}100%{transform:scale(1)}}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -674,6 +679,10 @@ function renderPastePage(paste) {
|
|||||||
<input id="manual-key" placeholder="Paste key (base64url) if missing from URL" />
|
<input id="manual-key" placeholder="Paste key (base64url) if missing from URL" />
|
||||||
<button id="decrypt-btn" type="button">Decrypt</button>
|
<button id="decrypt-btn" type="button">Decrypt</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<button id="copy-btn" class="btn" type="button">Copy</button>
|
||||||
|
<button id="download-btn" class="btn" type="button">Download</button>
|
||||||
|
</div>
|
||||||
<div class="card"><div id="out" class="preview">Decrypting...</div></div>
|
<div class="card"><div id="out" class="preview">Decrypting...</div></div>
|
||||||
${attachments.length ? `<p class="meta">Attachments: ${attachments.map((a) => a.name).join(', ')}</p>` : ''}
|
${attachments.length ? `<p class="meta">Attachments: ${attachments.map((a) => a.name).join(', ')}</p>` : ''}
|
||||||
<p><a href="/">Create another paste</a></p>
|
<p><a href="/">Create another paste</a></p>
|
||||||
@@ -683,6 +692,7 @@ function renderPastePage(paste) {
|
|||||||
<script>
|
<script>
|
||||||
const payload = ${JSON.stringify(encryptedPayload)};
|
const payload = ${JSON.stringify(encryptedPayload)};
|
||||||
const pasteFormat = ${JSON.stringify(normalizeFormat(safePaste.format || 'text'))};
|
const pasteFormat = ${JSON.stringify(normalizeFormat(safePaste.format || 'text'))};
|
||||||
|
let lastPlaintext = '';
|
||||||
function b64uToBytes(s) {
|
function b64uToBytes(s) {
|
||||||
const p = s.replace(/-/g, '+').replace(/_/g, '/');
|
const p = s.replace(/-/g, '+').replace(/_/g, '/');
|
||||||
const pad = p.length % 4 ? '='.repeat(4 - (p.length % 4)) : '';
|
const pad = p.length % 4 ? '='.repeat(4 - (p.length % 4)) : '';
|
||||||
@@ -710,6 +720,7 @@ function renderPastePage(paste) {
|
|||||||
const key = await crypto.subtle.importKey('raw', keyBytes, { name: 'AES-GCM' }, false, ['decrypt']);
|
const key = await crypto.subtle.importKey('raw', keyBytes, { name: 'AES-GCM' }, false, ['decrypt']);
|
||||||
const plain = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, ciphertext);
|
const plain = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, ciphertext);
|
||||||
const text = new TextDecoder().decode(plain);
|
const text = new TextDecoder().decode(plain);
|
||||||
|
lastPlaintext = text;
|
||||||
if (pasteFormat === 'markdown' && window.marked) {
|
if (pasteFormat === 'markdown' && window.marked) {
|
||||||
const sanitize = (html) => String(html)
|
const sanitize = (html) => String(html)
|
||||||
.replace(/<script[\\s\\S]*?>[\\s\\S]*?<\\/script>/gi, '')
|
.replace(/<script[\\s\\S]*?>[\\s\\S]*?<\\/script>/gi, '')
|
||||||
@@ -739,14 +750,44 @@ function renderPastePage(paste) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
lastPlaintext = '';
|
||||||
out.textContent = 'Failed to decrypt paste: ' + (err && err.message ? err.message : 'unknown error');
|
out.textContent = 'Failed to decrypt paste: ' + (err && err.message ? err.message : 'unknown error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async function copyCurrent() {
|
||||||
|
const btn = document.getElementById('copy-btn');
|
||||||
|
if (!lastPlaintext) return;
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(lastPlaintext);
|
||||||
|
btn.textContent = 'Copied';
|
||||||
|
btn.classList.add('ok');
|
||||||
|
setTimeout(() => { btn.textContent = 'Copy'; btn.classList.remove('ok'); }, 900);
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
function downloadCurrent() {
|
||||||
|
const btn = document.getElementById('download-btn');
|
||||||
|
if (!lastPlaintext) return;
|
||||||
|
const ext = pasteFormat === 'markdown' ? 'md' : 'txt';
|
||||||
|
const blob = new Blob([lastPlaintext], { type: 'text/plain;charset=utf-8' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = 'paste-${paste.id}.' + ext;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
a.remove();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
btn.textContent = 'Downloaded';
|
||||||
|
btn.classList.add('ok');
|
||||||
|
setTimeout(() => { btn.textContent = 'Download'; btn.classList.remove('ok'); }, 900);
|
||||||
|
}
|
||||||
function initDecryptView() {
|
function initDecryptView() {
|
||||||
const btn = document.getElementById('decrypt-btn');
|
const btn = document.getElementById('decrypt-btn');
|
||||||
const input = document.getElementById('manual-key');
|
const input = document.getElementById('manual-key');
|
||||||
const keyRow = document.getElementById('key-row');
|
const keyRow = document.getElementById('key-row');
|
||||||
btn.addEventListener('click', () => decryptAndRender(input.value));
|
btn.addEventListener('click', () => decryptAndRender(input.value));
|
||||||
|
document.getElementById('copy-btn').addEventListener('click', copyCurrent);
|
||||||
|
document.getElementById('download-btn').addEventListener('click', downloadCurrent);
|
||||||
input.addEventListener('keydown', (e) => {
|
input.addEventListener('keydown', (e) => {
|
||||||
if (e.key === 'Enter') decryptAndRender(input.value);
|
if (e.key === 'Enter') decryptAndRender(input.value);
|
||||||
});
|
});
|
||||||
@@ -788,12 +829,21 @@ function renderPastePage(paste) {
|
|||||||
pre{white-space:pre-wrap;word-break:break-word;background:#020617;border:1px solid #1e293b;border-radius:8px;padding:1rem}
|
pre{white-space:pre-wrap;word-break:break-word;background:#020617;border:1px solid #1e293b;border-radius:8px;padding:1rem}
|
||||||
a{color:#60a5fa}
|
a{color:#60a5fa}
|
||||||
.meta{font-size:.9rem;color:#94a3b8;margin:.5rem 0 1rem}
|
.meta{font-size:.9rem;color:#94a3b8;margin:.5rem 0 1rem}
|
||||||
|
.actions{display:flex;gap:.5rem;margin:0 0 .75rem}
|
||||||
|
.btn{background:#1d4ed8;color:#fff;border:0;border-radius:8px;padding:.45rem .8rem;cursor:pointer;transition:transform 120ms ease, background-color 120ms ease}
|
||||||
|
.btn:hover{transform:translateY(-1px);background:#2563eb}
|
||||||
|
.btn.ok{background:#16a34a;animation:pulse .55s ease}
|
||||||
|
@keyframes pulse{0%{transform:scale(1)}35%{transform:scale(1.04)}100%{transform:scale(1)}}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<h1>paste ${paste.id}</h1>
|
<h1>paste ${paste.id}</h1>
|
||||||
<div class="meta">Language: ${language} | Format: ${safePaste.format} | Expires in: ${hours}h ${minutes}m</div>
|
<div class="meta">Language: ${language} | Format: ${safePaste.format} | Expires in: ${hours}h ${minutes}m</div>
|
||||||
|
<div class="actions">
|
||||||
|
<button id="copy-btn" class="btn" type="button">Copy</button>
|
||||||
|
<button id="download-btn" class="btn" type="button">Download</button>
|
||||||
|
</div>
|
||||||
<div class="card">${safePaste.format === 'markdown'
|
<div class="card">${safePaste.format === 'markdown'
|
||||||
? `<div id="md-render">${paste.content.replace(/[<>&]/g, (m) => ({ '<': '<', '>': '>', '&': '&' }[m]))}</div>`
|
? `<div id="md-render">${paste.content.replace(/[<>&]/g, (m) => ({ '<': '<', '>': '>', '&': '&' }[m]))}</div>`
|
||||||
: `<pre><code id="code-render">${paste.content.replace(/[<>&]/g, (m) => ({ '<': '<', '>': '>', '&': '&' }[m]))}</code></pre>`}</div>
|
: `<pre><code id="code-render">${paste.content.replace(/[<>&]/g, (m) => ({ '<': '<', '>': '>', '&': '&' }[m]))}</code></pre>`}</div>
|
||||||
@@ -804,6 +854,31 @@ function renderPastePage(paste) {
|
|||||||
? `<script src="/api/vendor/marked.min.js"></script>
|
? `<script src="/api/vendor/marked.min.js"></script>
|
||||||
<script src="/api/vendor/highlight.min.js"></script>
|
<script src="/api/vendor/highlight.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
const sourceText = ${JSON.stringify(paste.content)};
|
||||||
|
const copyBtn = document.getElementById('copy-btn');
|
||||||
|
const downloadBtn = document.getElementById('download-btn');
|
||||||
|
copyBtn.addEventListener('click', async () => {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(sourceText);
|
||||||
|
copyBtn.textContent = 'Copied';
|
||||||
|
copyBtn.classList.add('ok');
|
||||||
|
setTimeout(() => { copyBtn.textContent = 'Copy'; copyBtn.classList.remove('ok'); }, 900);
|
||||||
|
} catch {}
|
||||||
|
});
|
||||||
|
downloadBtn.addEventListener('click', () => {
|
||||||
|
const blob = new Blob([sourceText], { type: 'text/markdown;charset=utf-8' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = 'paste-${paste.id}.md';
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
a.remove();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
downloadBtn.textContent = 'Downloaded';
|
||||||
|
downloadBtn.classList.add('ok');
|
||||||
|
setTimeout(() => { downloadBtn.textContent = 'Download'; downloadBtn.classList.remove('ok'); }, 900);
|
||||||
|
});
|
||||||
const el = document.getElementById('md-render');
|
const el = document.getElementById('md-render');
|
||||||
const src = el.textContent || '';
|
const src = el.textContent || '';
|
||||||
const sanitize = (html) => String(html)
|
const sanitize = (html) => String(html)
|
||||||
@@ -820,6 +895,31 @@ function renderPastePage(paste) {
|
|||||||
</script>`
|
</script>`
|
||||||
: `<script src="/api/vendor/highlight.min.js"></script>
|
: `<script src="/api/vendor/highlight.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
const sourceText = ${JSON.stringify(paste.content)};
|
||||||
|
const copyBtn = document.getElementById('copy-btn');
|
||||||
|
const downloadBtn = document.getElementById('download-btn');
|
||||||
|
copyBtn.addEventListener('click', async () => {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(sourceText);
|
||||||
|
copyBtn.textContent = 'Copied';
|
||||||
|
copyBtn.classList.add('ok');
|
||||||
|
setTimeout(() => { copyBtn.textContent = 'Copy'; copyBtn.classList.remove('ok'); }, 900);
|
||||||
|
} catch {}
|
||||||
|
});
|
||||||
|
downloadBtn.addEventListener('click', () => {
|
||||||
|
const blob = new Blob([sourceText], { type: 'text/plain;charset=utf-8' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = 'paste-${paste.id}.txt';
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
a.remove();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
downloadBtn.textContent = 'Downloaded';
|
||||||
|
downloadBtn.classList.add('ok');
|
||||||
|
setTimeout(() => { downloadBtn.textContent = 'Download'; downloadBtn.classList.remove('ok'); }, 900);
|
||||||
|
});
|
||||||
const block = document.getElementById('code-render');
|
const block = document.getElementById('code-render');
|
||||||
if (block && window.hljs) {
|
if (block && window.hljs) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -30,6 +30,17 @@ function setCreateState(message, isError = false) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function flashLinkFeedback(el, okText, fallbackText = null) {
|
||||||
|
if (!el) return;
|
||||||
|
const original = el.textContent;
|
||||||
|
el.textContent = okText;
|
||||||
|
el.style.opacity = '0.78';
|
||||||
|
setTimeout(() => {
|
||||||
|
el.textContent = fallbackText || original;
|
||||||
|
el.style.opacity = '';
|
||||||
|
}, 900);
|
||||||
|
}
|
||||||
|
|
||||||
function bytesToB64u(bytes) {
|
function bytesToB64u(bytes) {
|
||||||
let bin = '';
|
let bin = '';
|
||||||
for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
|
for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
|
||||||
@@ -386,9 +397,9 @@ document.getElementById('list').addEventListener('click', async (e) => {
|
|||||||
try {
|
try {
|
||||||
const full = `${window.location.origin}${link.getAttribute('data-copy-url')}`;
|
const full = `${window.location.origin}${link.getAttribute('data-copy-url')}`;
|
||||||
await navigator.clipboard.writeText(full);
|
await navigator.clipboard.writeText(full);
|
||||||
setCreateState('Recent paste link copied.');
|
flashLinkFeedback(link, 'Copied');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setCreateState('Could not copy recent paste link.', true);
|
flashLinkFeedback(link, 'Copy failed', 'Copy link');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user