allow custom css
This commit is contained in:
@@ -5,6 +5,13 @@ import {
|
||||
subscribeStatus,
|
||||
subscribeUnread,
|
||||
} from "./notify-client.js";
|
||||
|
||||
(function injectCustomCSS() {
|
||||
const link = document.createElement("link");
|
||||
link.rel = "stylesheet";
|
||||
link.href = "/assets/custom.css";
|
||||
document.head.appendChild(link);
|
||||
})();
|
||||
import { mountNav } from "./nav/template.js";
|
||||
import { createAdaptiveNavController } from "./nav/layout.js";
|
||||
import { applyUserRoleUI } from "./nav/role-ui.js";
|
||||
|
||||
@@ -56,6 +56,11 @@ const tlsCertbotCertFileInput = document.getElementById("tls-certbot-cert-file")
|
||||
const tlsCertbotKeyFileInput = document.getElementById("tls-certbot-key-file");
|
||||
const tlsCertbotCaFileInput = document.getElementById("tls-certbot-ca-file");
|
||||
|
||||
const appearanceForm = document.getElementById("appearance-form");
|
||||
const appearancePermissionNote = document.getElementById("appearance-permission-note");
|
||||
const appearanceSaveBtn = document.getElementById("appearance-save-btn");
|
||||
const appearanceCustomCssInput = document.getElementById("appearance-custom-css");
|
||||
|
||||
let currentUser = null;
|
||||
let securityConfig = null;
|
||||
let tlsConfig = null;
|
||||
@@ -605,12 +610,68 @@ async function handleUnbanClick(event) {
|
||||
await loadBannedIps();
|
||||
}
|
||||
|
||||
async function loadAppearanceSettings() {
|
||||
if (!currentUser) return;
|
||||
|
||||
if (!isAdmin(currentUser.role)) {
|
||||
if (appearancePermissionNote) appearancePermissionNote.classList.remove("hidden");
|
||||
if (appearanceCustomCssInput) appearanceCustomCssInput.disabled = true;
|
||||
if (appearanceSaveBtn) appearanceSaveBtn.disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (appearancePermissionNote) appearancePermissionNote.classList.add("hidden");
|
||||
|
||||
try {
|
||||
const res = await fetch("/api/settings/appearance", { credentials: "include" });
|
||||
if (!res.ok) {
|
||||
showMessage("Failed to load custom CSS settings.", "error");
|
||||
return;
|
||||
}
|
||||
const data = await res.json().catch(() => ({}));
|
||||
if (appearanceCustomCssInput) appearanceCustomCssInput.value = data.customCSS || "";
|
||||
if (appearanceSaveBtn) appearanceSaveBtn.disabled = false;
|
||||
} catch {
|
||||
showMessage("Failed to load custom CSS settings.", "error");
|
||||
}
|
||||
}
|
||||
|
||||
async function saveAppearanceSettings(event) {
|
||||
event.preventDefault();
|
||||
if (!isAdmin(currentUser?.role)) {
|
||||
showMessage("Admin role required.", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
const customCSS = appearanceCustomCssInput ? appearanceCustomCssInput.value : "";
|
||||
if (customCSS.length > 51200) {
|
||||
showMessage("CSS exceeds the 50 KB size limit.", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await fetch("/api/settings/appearance", {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
credentials: "include",
|
||||
body: JSON.stringify({ customCSS }),
|
||||
});
|
||||
|
||||
const data = await res.json().catch(() => ({}));
|
||||
if (!res.ok) {
|
||||
showMessage(data.error || "Failed to save custom CSS.", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
showMessage("Custom CSS saved. Reload the page to apply the new styles.");
|
||||
}
|
||||
|
||||
async function init() {
|
||||
try {
|
||||
await loadCurrentUser();
|
||||
loadPrefs();
|
||||
await loadSecurityPolicy();
|
||||
await loadTlsSettings();
|
||||
await loadAppearanceSettings();
|
||||
await loadBannedIps();
|
||||
|
||||
passwordForm.addEventListener("submit", updatePassword);
|
||||
@@ -618,6 +679,7 @@ async function init() {
|
||||
securityForm.addEventListener("submit", saveSecurityPolicy);
|
||||
tlsForm.addEventListener("submit", saveTlsSettings);
|
||||
tlsCertbotAutoBtn.addEventListener("click", runCertbotAutoSetup);
|
||||
if (appearanceForm) appearanceForm.addEventListener("submit", saveAppearanceSettings);
|
||||
refreshBansBtn.addEventListener("click", loadBannedIps);
|
||||
bansTableBody.addEventListener("click", handleUnbanClick);
|
||||
} catch (error) {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
referrerpolicy="no-referrer"
|
||||
/>
|
||||
<link rel="stylesheet" href="/assets/main.css" />
|
||||
<link rel="stylesheet" href="/assets/custom.css" />
|
||||
<script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.2/anime.min.js"
|
||||
crossorigin="anonymous"
|
||||
|
||||
@@ -337,6 +337,37 @@
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<form id="appearance-form" class="space-y-3 pt-1 border-t border-slate-800 pt-4">
|
||||
<div>
|
||||
<h3 class="font-medium">Custom CSS</h3>
|
||||
<p class="text-xs text-slate-400">Admin-only. Paste custom CSS to override the default theme across all pages.</p>
|
||||
</div>
|
||||
|
||||
<div id="appearance-permission-note" class="hidden rounded-lg border border-amber-800 bg-amber-900/20 text-amber-200 px-3 py-2 text-sm">
|
||||
Only admins can update custom CSS.
|
||||
</div>
|
||||
|
||||
<label class="block">
|
||||
<span class="text-sm text-slate-300">Custom CSS</span>
|
||||
<textarea
|
||||
id="appearance-custom-css"
|
||||
rows="12"
|
||||
spellcheck="false"
|
||||
placeholder="/* Paste your custom CSS here */ :root { --color-primary: #f59e0b; }"
|
||||
class="mt-1 w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500 font-mono text-xs resize-y"
|
||||
></textarea>
|
||||
<p class="text-xs text-slate-500 mt-1">Max 50 KB. Reload any open page after saving to see the new styles.</p>
|
||||
</label>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
id="appearance-save-btn"
|
||||
class="px-4 py-2 rounded-lg bg-violet-700 hover:bg-violet-600 transition-colors text-white font-medium"
|
||||
>
|
||||
<i class="fa-solid fa-paintbrush mr-2"></i>Save Custom CSS
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p id="settings-message" class="hidden text-sm rounded-lg px-3 py-2"></p>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user