fixes lol

This commit is contained in:
doomtube 2026-01-07 03:38:34 -05:00
parent c358db55aa
commit c2bfa06faa
5 changed files with 149 additions and 4 deletions

View file

@ -2336,6 +2336,41 @@ void AdminController::updateSiteSettings(const HttpRequestPtr &req,
};
}
// Update announcement_enabled if provided
if (json->isMember("announcement_enabled")) {
bool enabled = (*json)["announcement_enabled"].asBool();
std::string value = enabled ? "true" : "false";
*dbClient << "INSERT INTO site_settings (setting_key, setting_value) VALUES ('announcement_enabled', $1) "
"ON CONFLICT (setting_key) DO UPDATE SET setting_value = $1, updated_at = CURRENT_TIMESTAMP"
<< value
>> [](const Result&) {
LOG_INFO << "Announcement enabled setting updated successfully";
}
>> [](const DrogonDbException& e) {
LOG_ERROR << "Failed to update announcement_enabled: " << e.base().what();
};
}
// Update announcement_text if provided
if (json->isMember("announcement_text")) {
std::string text = (*json)["announcement_text"].asString();
// Limit to 500 characters
if (text.length() > 500) {
text = text.substr(0, 500);
}
// Sanitize to prevent XSS
text = htmlEscape(text);
*dbClient << "INSERT INTO site_settings (setting_key, setting_value) VALUES ('announcement_text', $1) "
"ON CONFLICT (setting_key) DO UPDATE SET setting_value = $1, updated_at = CURRENT_TIMESTAMP"
<< text
>> [](const Result&) {
LOG_INFO << "Announcement text updated successfully";
}
>> [](const DrogonDbException& e) {
LOG_ERROR << "Failed to update announcement_text: " << e.base().what();
};
}
// Update censored_words if provided (comma-separated list)
if (json->isMember("censored_words")) {
// Rate limit: 10 updates per minute per admin
@ -2464,7 +2499,8 @@ void AdminController::getPublicSiteSettings(const HttpRequestPtr &,
>> [callback](const Result& r) {
// Whitelist of publicly-safe settings
static const std::unordered_set<std::string> publicKeys = {
"site_title", "logo_path", "logo_display_mode"
"site_title", "logo_path", "logo_display_mode",
"announcement_enabled", "announcement_text"
};
Json::Value resp;

View file

@ -555,7 +555,9 @@ CREATE TABLE IF NOT EXISTS site_settings (
INSERT INTO site_settings (setting_key, setting_value) VALUES
('site_title', 'Stream'),
('logo_path', ''),
('logo_display_mode', 'text') -- 'text', 'image', 'both'
('logo_display_mode', 'text'), -- 'text', 'image', 'both'
('announcement_enabled', 'false'),
('announcement_text', '')
ON CONFLICT (setting_key) DO NOTHING;
-- Trigger for site_settings updated_at

View file

@ -3,5 +3,7 @@ import { writable } from 'svelte/store';
export const siteSettings = writable({
site_title: 'Stream',
logo_path: '',
logo_display_mode: 'text'
logo_display_mode: 'text',
announcement_enabled: false,
announcement_text: ''
});

View file

@ -30,7 +30,9 @@
siteSettings.set({
site_title: settings.site_title || 'Stream',
logo_path: settings.logo_path || '',
logo_display_mode: settings.logo_display_mode || 'text'
logo_display_mode: settings.logo_display_mode || 'text',
announcement_enabled: settings.announcement_enabled === 'true',
announcement_text: settings.announcement_text || ''
});
}
} catch (e) {
@ -413,6 +415,16 @@
.dropdown-item.logout:hover :global(svg) {
color: var(--error);
}
.announcement-banner {
background: linear-gradient(135deg, #8b5cf6, #a855f7);
color: white;
padding: 0.75rem 1rem;
text-align: center;
font-size: 0.95rem;
font-weight: 500;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
</style>
{#if !isPopoutPage}
@ -559,6 +571,12 @@
</div>
</nav>
{#if $siteSettings.announcement_enabled && $siteSettings.announcement_text}
<div class="announcement-banner">
{$siteSettings.announcement_text}
</div>
{/if}
{#if browser}
<GlobalAudioPlayer />
<ChatTerminal />

View file

@ -57,6 +57,11 @@
lastRenewalError: null,
autoRenewalEnabled: true
};
// Announcement Settings
let announcementEnabled = false;
let announcementText = '';
let announcementSaving = false;
let sslLoading = false;
let sslSaving = false;
let sslRequesting = false;
@ -1142,6 +1147,8 @@
logo_display_mode: settings.logo_display_mode || 'text'
};
censoredWords = settings.censored_words || '';
announcementEnabled = settings.announcement_enabled === 'true';
announcementText = settings.announcement_text || '';
} else {
console.error('Failed to load site settings');
}
@ -1221,6 +1228,35 @@
setTimeout(() => { message = ''; error = ''; }, 3000);
}
async function saveAnnouncementSettings() {
announcementSaving = true;
try {
const response = await fetch('/api/admin/settings/site', {
method: 'PUT',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
announcement_enabled: announcementEnabled,
announcement_text: announcementText
})
});
if (response.ok) {
message = 'Announcement settings updated successfully';
} else {
error = 'Failed to update announcement settings';
}
} catch (e) {
error = 'Error updating announcement settings';
console.error(e);
}
announcementSaving = false;
setTimeout(() => { message = ''; error = ''; }, 3000);
}
async function loadDefaultAvatars() {
try {
const response = await fetch('/api/admin/default-avatars', {
@ -3603,6 +3639,57 @@
{/if}
</div>
<!-- Announcement Settings -->
<div class="card">
<h2>Site Announcement</h2>
<p style="color: var(--gray); margin-bottom: 2rem;">
Display a site-wide announcement banner at the top of all pages
</p>
<div style="display: grid; gap: 1.5rem; max-width: 600px;">
<div>
<label style="display: flex; align-items: center; gap: 0.75rem; cursor: pointer;">
<input
type="checkbox"
bind:checked={announcementEnabled}
style="width: auto; margin: 0;"
/>
<span>Enable announcement banner</span>
</label>
<small style="color: var(--gray); display: block; margin-top: 0.25rem; margin-left: 1.75rem;">
When enabled, the announcement will be displayed at the top of all pages
</small>
</div>
<div>
<label for="announcementText">
Announcement Message
</label>
<textarea
id="announcementText"
bind:value={announcementText}
placeholder="Enter your announcement message here..."
rows="3"
maxlength="500"
style="margin-top: 0.5rem; width: 100%; resize: vertical;"
></textarea>
<small style="color: var(--gray); display: block; margin-top: 0.25rem;">
{announcementText.length}/500 characters. This message will be displayed to all visitors.
</small>
</div>
<div>
<button
class="btn btn-primary"
on:click={saveAnnouncementSettings}
disabled={announcementSaving}
>
{announcementSaving ? 'Saving...' : 'Save Announcement'}
</button>
</div>
</div>
</div>
{:else if activeTab === 'botkeys'}
<div class="card">
<h2>Bot API Keys</h2>