Archived
Polish KarlDNS production UI and docs
This commit is contained in:
1 parent
ca92dc211a
commit
5beee0e5c6
61 files changed
+1566
-523
No files matched your search
@@ -5,13 +5,13 @@ from pathlib import Path
|
||||
from fastapi import APIRouter, Response
|
||||
from fastapi.responses import FileResponse, RedirectResponse
|
||||
|
||||
from ..web.styles import APP_CSS
|
||||
from ..web.styles import ADMIN_CSS, BASE_CSS
|
||||
|
||||
ASSET_DIR = Path(__file__).parents[1] / "assets"
|
||||
router = APIRouter(include_in_schema=False)
|
||||
ASSET_CACHE_HEADERS = {"Cache-Control": "public, max-age=3600"}
|
||||
ASSET_CACHE_HEADERS = {"Cache-Control": "public, max-age=31536000, immutable"}
|
||||
|
||||
APP_JS = """
|
||||
THEME_JS = """
|
||||
(() => {
|
||||
const media = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
const storageKey = "karldns-theme";
|
||||
@@ -30,8 +30,9 @@ APP_JS = """
|
||||
};
|
||||
const syncToggle = (theme) => {
|
||||
document.querySelectorAll("[data-theme-toggle]").forEach((button) => {
|
||||
button.textContent = theme === "dark" ? "Dark" : "Light";
|
||||
button.setAttribute("aria-label", `Switch to ${theme === "dark" ? "light" : "dark"} theme`);
|
||||
const nextTheme = theme === "dark" ? "light" : "dark";
|
||||
button.dataset.themeNext = nextTheme;
|
||||
button.setAttribute("aria-label", `Switch to ${nextTheme.toLowerCase()} theme`);
|
||||
});
|
||||
};
|
||||
const applyTheme = (requestedTheme = storedTheme() || systemTheme()) => {
|
||||
@@ -54,7 +55,10 @@ APP_JS = """
|
||||
applyTheme(nextTheme);
|
||||
});
|
||||
})();
|
||||
""".strip()
|
||||
|
||||
|
||||
CLIPBOARD_JS = """
|
||||
document.addEventListener("click", async (event) => {
|
||||
const toggle = event.target.closest("[data-secret-toggle]");
|
||||
if (toggle) {
|
||||
@@ -87,7 +91,10 @@ document.addEventListener("click", async (event) => {
|
||||
}, 1200);
|
||||
} catch (_) {}
|
||||
});
|
||||
""".strip()
|
||||
|
||||
|
||||
ADMIN_TABLES_JS = """
|
||||
document.querySelectorAll("[data-admin-table]").forEach((tableShell) => {
|
||||
const table = tableShell.querySelector("table");
|
||||
const tbody = tableShell.querySelector("tbody");
|
||||
@@ -176,10 +183,22 @@ document.querySelectorAll("[data-admin-table]").forEach((tableShell) => {
|
||||
""".strip()
|
||||
|
||||
|
||||
def _minify_css(css: str) -> str:
|
||||
return " ".join(css.split()).replace(" {", "{").replace("{ ", "{").replace("; ", ";").replace(": ", ":").replace(", ", ",").replace(" }", "}").replace(";}", "}")
|
||||
|
||||
|
||||
MINIFIED_BASE_CSS = _minify_css(BASE_CSS)
|
||||
MINIFIED_ADMIN_CSS = _minify_css(ADMIN_CSS)
|
||||
|
||||
|
||||
@router.get("/favicon.ico")
|
||||
def favicon() -> FileResponse:
|
||||
return FileResponse(ASSET_DIR / "favicon.ico", media_type="image/x-icon", headers=ASSET_CACHE_HEADERS)
|
||||
|
||||
@router.get("/bootstrap-subset.css")
|
||||
def bootstrap_subset_css() -> FileResponse:
|
||||
return FileResponse(ASSET_DIR / "bootstrap-subset.css", media_type="text/css", headers=ASSET_CACHE_HEADERS)
|
||||
|
||||
@router.get("/favicon-16x16.png")
|
||||
def favicon_16_png() -> FileResponse:
|
||||
return FileResponse(ASSET_DIR / "favicon-16x16.png", media_type="image/png", headers=ASSET_CACHE_HEADERS)
|
||||
@@ -196,6 +215,14 @@ def apple_touch_icon_png() -> FileResponse:
|
||||
def logo_png() -> FileResponse:
|
||||
return FileResponse(ASSET_DIR / "logo.png", media_type="image/png", headers=ASSET_CACHE_HEADERS)
|
||||
|
||||
@router.get("/logo.webp")
|
||||
def logo_webp() -> FileResponse:
|
||||
return FileResponse(ASSET_DIR / "logo.webp", media_type="image/webp", headers=ASSET_CACHE_HEADERS)
|
||||
|
||||
@router.get("/logo-nav.webp")
|
||||
def logo_nav_webp() -> FileResponse:
|
||||
return FileResponse(ASSET_DIR / "logo-nav.webp", media_type="image/webp", headers=ASSET_CACHE_HEADERS)
|
||||
|
||||
@router.get("/logo-192.png")
|
||||
def logo_192_png() -> FileResponse:
|
||||
return FileResponse(ASSET_DIR / "logo-192.png", media_type="image/png", headers=ASSET_CACHE_HEADERS)
|
||||
@@ -204,6 +231,14 @@ def logo_192_png() -> FileResponse:
|
||||
def logo_512_png() -> FileResponse:
|
||||
return FileResponse(ASSET_DIR / "logo-512.png", media_type="image/png", headers=ASSET_CACHE_HEADERS)
|
||||
|
||||
@router.get("/social-preview.png")
|
||||
def social_preview_png() -> FileResponse:
|
||||
return FileResponse(ASSET_DIR / "social-preview.png", media_type="image/png", headers=ASSET_CACHE_HEADERS)
|
||||
|
||||
@router.get("/social-preview.webp")
|
||||
def social_preview_webp() -> FileResponse:
|
||||
return FileResponse(ASSET_DIR / "social-preview.webp", media_type="image/webp", headers=ASSET_CACHE_HEADERS)
|
||||
|
||||
@router.get("/site.webmanifest")
|
||||
def site_webmanifest() -> Response:
|
||||
return Response(
|
||||
@@ -211,7 +246,7 @@ def site_webmanifest() -> Response:
|
||||
{
|
||||
"name": "KarlDNS",
|
||||
"short_name": "KarlDNS",
|
||||
"description": "Accountless Dynamic DNS for routers and home labs.",
|
||||
"description": "Private update links for routers.",
|
||||
"start_url": "/",
|
||||
"scope": "/",
|
||||
"display": "standalone",
|
||||
@@ -227,14 +262,29 @@ def site_webmanifest() -> Response:
|
||||
headers=ASSET_CACHE_HEADERS,
|
||||
)
|
||||
|
||||
@router.get("/app.js")
|
||||
def app_js() -> Response:
|
||||
return Response(APP_JS, media_type="application/javascript", headers=ASSET_CACHE_HEADERS)
|
||||
@router.get("/theme.js")
|
||||
def theme_js() -> Response:
|
||||
return Response(THEME_JS, media_type="application/javascript", headers=ASSET_CACHE_HEADERS)
|
||||
|
||||
|
||||
@router.get("/clipboard.js")
|
||||
def clipboard_js() -> Response:
|
||||
return Response(CLIPBOARD_JS, media_type="application/javascript", headers=ASSET_CACHE_HEADERS)
|
||||
|
||||
|
||||
@router.get("/admin-tables.js")
|
||||
def admin_tables_js() -> Response:
|
||||
return Response(ADMIN_TABLES_JS, media_type="application/javascript", headers=ASSET_CACHE_HEADERS)
|
||||
|
||||
|
||||
@router.get("/app.css")
|
||||
def app_css() -> Response:
|
||||
return Response(APP_CSS, media_type="text/css", headers=ASSET_CACHE_HEADERS)
|
||||
return Response(MINIFIED_BASE_CSS, media_type="text/css", headers=ASSET_CACHE_HEADERS)
|
||||
|
||||
|
||||
@router.get("/admin.css")
|
||||
def admin_css() -> Response:
|
||||
return Response(MINIFIED_ADMIN_CSS, media_type="text/css", headers=ASSET_CACHE_HEADERS)
|
||||
|
||||
|
||||
@router.get("/install.sh")
|
||||
|
||||
Reference in new issue
Block a user