Fix share links: use external URL (docs.jongsma.me), fix copy button, add copy on existing shares

This commit is contained in:
James 2026-02-09 12:09:16 -05:00
parent 9f0bac5783
commit a77a31f4c9
1 changed files with 39 additions and 10 deletions

View File

@ -244,7 +244,7 @@
<p class="text-sm text-green-700 dark:text-green-300 mb-2">Share link created!</p> <p class="text-sm text-green-700 dark:text-green-300 mb-2">Share link created!</p>
<div class="flex gap-2"> <div class="flex gap-2">
<input id="share-url" type="text" readonly class="flex-1 px-3 py-2 text-sm bg-white dark:bg-gray-700 rounded-lg border border-gray-200 dark:border-gray-600"> <input id="share-url" type="text" readonly class="flex-1 px-3 py-2 text-sm bg-white dark:bg-gray-700 rounded-lg border border-gray-200 dark:border-gray-600">
<button onclick="copyShareUrl()" class="px-3 py-2 bg-gray-100 dark:bg-gray-600 hover:bg-gray-200 dark:hover:bg-gray-500 text-sm rounded-lg transition-colors">Copy</button> <button onclick="copyShareUrl(this)" class="px-3 py-2 bg-gray-100 dark:bg-gray-600 hover:bg-gray-200 dark:hover:bg-gray-500 text-sm rounded-lg transition-colors">Copy</button>
</div> </div>
</div> </div>
@ -413,6 +413,30 @@
function closeShareModal() { function closeShareModal() {
document.getElementById('share-modal').classList.add('hidden'); document.getElementById('share-modal').classList.add('hidden');
} }
const EXTERNAL_BASE = 'https://docs.jongsma.me';
function copyToClipboard(text, btn) {
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(() => showCopied(btn));
} else {
const ta = document.createElement('textarea');
ta.value = text;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
showCopied(btn);
}
}
function showCopied(btn) {
if (!btn) return;
const orig = btn.textContent;
btn.textContent = 'Copied!';
setTimeout(() => btn.textContent = orig, 1500);
}
async function createShare() { async function createShare() {
const days = parseInt(document.getElementById('share-days').value); const days = parseInt(document.getElementById('share-days').value);
const res = await fetch('/api/share/{{.Document.ID}}', { const res = await fetch('/api/share/{{.Document.ID}}', {
@ -422,15 +446,15 @@
}); });
if (res.ok) { if (res.ok) {
const data = await res.json(); const data = await res.json();
const fullUrl = window.location.origin + data.url; const fullUrl = EXTERNAL_BASE + data.url;
document.getElementById('share-url').value = fullUrl; document.getElementById('share-url').value = fullUrl;
document.getElementById('share-result').classList.remove('hidden'); document.getElementById('share-result').classList.remove('hidden');
loadShares(); loadShares();
} }
} }
function copyShareUrl() { function copyShareUrl(btn) {
const input = document.getElementById('share-url'); const input = document.getElementById('share-url');
navigator.clipboard.writeText(input.value); copyToClipboard(input.value, btn);
} }
async function loadShares() { async function loadShares() {
const res = await fetch('/api/shares/{{.Document.ID}}'); const res = await fetch('/api/shares/{{.Document.ID}}');
@ -440,15 +464,20 @@
list.innerHTML = '<p class="text-sm text-gray-400 italic">No active shares</p>'; list.innerHTML = '<p class="text-sm text-gray-400 italic">No active shares</p>';
return; return;
} }
list.innerHTML = shares.map(s => ` list.innerHTML = shares.map(s => {
const url = EXTERNAL_BASE + '/s/' + s.Token;
return `
<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg"> <div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
<div class="text-sm"> <div class="text-sm truncate mr-2">
<span class="font-mono text-gray-600 dark:text-gray-300">${window.location.origin}/s/${s.Token}</span> <span class="font-mono text-gray-600 dark:text-gray-300">${url}</span>
<span class="text-gray-400 ml-2">${s.ExpiresAt ? 'expires ' + s.ExpiresAt : 'permanent'}</span> <span class="text-gray-400 ml-2">${s.ExpiresAt ? 'expires ' + s.ExpiresAt : 'permanent'}</span>
</div> </div>
<div class="flex gap-2 shrink-0">
<button onclick="copyToClipboard('${url}', this)" class="text-brand-600 dark:text-brand-400 hover:text-brand-700 text-sm">Copy</button>
<button onclick="revokeShare('${s.Token}')" class="text-red-500 hover:text-red-700 text-sm">Revoke</button> <button onclick="revokeShare('${s.Token}')" class="text-red-500 hover:text-red-700 text-sm">Revoke</button>
</div> </div>
`).join(''); </div>`;
}).join('');
} }
async function revokeShare(token) { async function revokeShare(token) {
await fetch('/api/share/' + token, {method: 'DELETE'}); await fetch('/api/share/' + token, {method: 'DELETE'});