Add automatic SSL certificate generation
All checks were successful
Build and Push / build-all (push) Successful in 8m13s

This commit is contained in:
doomtube 2026-01-06 15:22:41 -05:00
parent e13fffdaac
commit 42855330c0
11 changed files with 105 additions and 38 deletions

View file

@ -152,6 +152,8 @@ class ChatWebSocket {
...info,
username: data.newName
}));
// Persist to localStorage for reconnection
localStorage.setItem('guestName', data.newName);
console.log('Rename successful:', data.newName);
break;
@ -173,7 +175,10 @@ class ChatWebSocket {
case 'error':
console.error('Chat error:', data.error);
// Optionally show error to user
// Show error to user
if (data.error && typeof window !== 'undefined') {
alert(data.error);
}
break;
case 'mod_action_success':

View file

@ -144,7 +144,7 @@
msg = msg.replace(/^#\s+:(\w+):$/gm, (match, stickerName) => {
const stickerKey = stickerName.toLowerCase();
if (stickersMap[stickerKey]) {
return `# <img src="${stickersMap[stickerKey]}" alt="${stickerName}" title="${stickerName}" data-sticker="${stickerName}" class="sticker-img" />`;
return `# <img src="${stickersMap[stickerKey]}" alt="${stickerName}" title="${stickerName}" data-sticker="${stickerName}" class="sticker-img" onerror="this.onerror=null;this.src='/dlive2.gif';" />`;
}
return match;
});
@ -153,7 +153,7 @@
msg = msg.replace(/^##\s+:(\w+):$/gm, (match, stickerName) => {
const stickerKey = stickerName.toLowerCase();
if (stickersMap[stickerKey]) {
return `## <img src="${stickersMap[stickerKey]}" alt="${stickerName}" title="${stickerName}" data-sticker="${stickerName}" class="sticker-img" />`;
return `## <img src="${stickersMap[stickerKey]}" alt="${stickerName}" title="${stickerName}" data-sticker="${stickerName}" class="sticker-img" onerror="this.onerror=null;this.src='/dlive2.gif';" />`;
}
return match;
});
@ -164,7 +164,7 @@
msg = msg.replace(/:(\w+):/g, (match, stickerName) => {
const stickerKey = stickerName.toLowerCase();
if (stickersMap[stickerKey]) {
return `<img src="${stickersMap[stickerKey]}" alt="${stickerName}" title="${stickerName}" data-sticker="${stickerName}" class="sticker-img" />`;
return `<img src="${stickersMap[stickerKey]}" alt="${stickerName}" title="${stickerName}" data-sticker="${stickerName}" class="sticker-img" onerror="this.onerror=null;this.src='/dlive2.gif';" />`;
}
return match;
});
@ -185,7 +185,7 @@
// Step 5: Sanitize with DOMPurify - allow img tags and safe CSS
html = DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'img', 'br', 'strong', 'em', 'code', 'pre', 'del', 'span'],
ALLOWED_ATTR: ['src', 'alt', 'title', 'class', 'style', 'data-sticker'],
ALLOWED_ATTR: ['src', 'alt', 'title', 'class', 'style', 'data-sticker', 'onerror'],
FORBID_TAGS: ['a', 'button', 'script'],
ALLOW_DATA_ATTR: false
});

View file

@ -271,16 +271,9 @@
}
// Send rename message to server
// Note: State updates (localStorage + store) happen in rename_success handler
// to avoid updating UI before server confirms the rename
if (chatWebSocket.sendRename(newGuestName.trim())) {
// Store in localStorage for persistence
localStorage.setItem('guestName', newGuestName.trim());
// Update local store
chatUserInfo.update(info => ({
...info,
username: newGuestName.trim()
}));
showRenameModal = false;
newGuestName = '';
} else {

View file

@ -138,7 +138,7 @@
preview = preview.replace(/:(\w+):/g, (match, name) => {
const key = name.toLowerCase();
if (stickerMap && stickerMap[key]) {
return `<img src="${stickerMap[key]}" alt="${name}" class="sticker-img-small" />`;
return `<img src="${stickerMap[key]}" alt="${name}" class="sticker-img-small" onerror="this.onerror=null;this.src='/dlive2.gif';" />`;
}
return match;
});
@ -147,7 +147,7 @@
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['p', 'img', 'br', 'strong', 'em', 'code', 'del', 'span'],
ALLOWED_ATTR: ['src', 'alt', 'class'],
ALLOWED_ATTR: ['src', 'alt', 'class', 'onerror'],
FORBID_TAGS: ['a', 'button', 'script']
});
}

View file

@ -113,7 +113,7 @@
msg = msg.replace(/:(\w+):/g, (match, name) => {
const key = name.toLowerCase();
if (stickerMap && stickerMap[key]) {
return `<img src="${stickerMap[key]}" alt="${name}" title="${name}" class="sticker-img" />`;
return `<img src="${stickerMap[key]}" alt="${name}" title="${name}" class="sticker-img" onerror="this.onerror=null;this.src='/dlive2.gif';" />`;
}
return match;
});
@ -125,7 +125,7 @@
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'img', 'br', 'strong', 'em', 'code', 'pre', 'del', 'span'],
ALLOWED_ATTR: ['src', 'alt', 'title', 'class', 'style'],
ALLOWED_ATTR: ['src', 'alt', 'title', 'class', 'style', 'onerror'],
FORBID_TAGS: ['a', 'button', 'script']
});
}

View file

@ -394,12 +394,40 @@
flex: 1;
}
.fingerprint-row {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.25rem;
flex-wrap: wrap;
}
.fingerprint-display {
font-family: monospace;
font-size: 0.9rem;
margin-bottom: 0.25rem;
}
.key-origin-tag {
display: inline-block;
padding: 0.15rem 0.4rem;
border-radius: 4px;
font-size: 0.7rem;
font-weight: 600;
text-transform: uppercase;
}
.key-origin-tag.generated {
background: rgba(40, 167, 69, 0.15);
color: #28a745;
border: 1px solid rgba(40, 167, 69, 0.3);
}
.key-origin-tag.imported {
background: rgba(0, 123, 255, 0.15);
color: #007bff;
border: 1px solid rgba(0, 123, 255, 0.3);
}
.key-date {
color: var(--gray);
font-size: 0.85rem;
@ -796,15 +824,20 @@
{#if pgpKeys.length > 0}
{#each pgpKeys as key}
<div class="pgp-key-item">
<div
class="pgp-key-header"
<div
class="pgp-key-header"
on:click={() => toggleKey(key.fingerprint)}
on:keypress={(e) => e.key === 'Enter' && toggleKey(key.fingerprint)}
role="button"
tabindex="0"
>
<div class="pgp-key-info">
<div class="fingerprint-display">{key.fingerprint}</div>
<div class="fingerprint-row">
<div class="fingerprint-display">{key.fingerprint}</div>
<span class="key-origin-tag {key.keyOrigin || 'imported'}">
{key.keyOrigin === 'generated' ? 'Generated' : 'Imported'}
</span>
</div>
<div class="key-date">Added {new Date(key.createdAt).toLocaleDateString()}</div>
</div>
<span class="expand-icon" class:expanded={expandedKeys[key.fingerprint]}>

View file

@ -932,7 +932,8 @@
credentials: 'include',
body: JSON.stringify({
publicKey: generatedPublicKey,
fingerprint: fingerprint
fingerprint: fingerprint,
origin: 'generated'
})
});
@ -987,7 +988,8 @@
credentials: 'include',
body: JSON.stringify({
publicKey: newPublicKey,
fingerprint
fingerprint,
origin: 'imported'
})
});

BIN
frontend/static/dlive2.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB