Compare commits
2 Commits
d147f53ef1
...
4218bb8344
| Author | SHA1 | Date | |
|---|---|---|---|
| 4218bb8344 | |||
| 6b2fd299e0 |
@@ -2,6 +2,7 @@
|
||||
let
|
||||
serverCfg = config.syscfg.server;
|
||||
mediaCfg = config.syscfg.media;
|
||||
palette = serverCfg.colorScheme.palette or { };
|
||||
port = 8080;
|
||||
assetSize = 64;
|
||||
cacheControl = containerCfg.extra.cacheControl or "public, max-age=86400";
|
||||
@@ -9,10 +10,32 @@ let
|
||||
logoSvgFileName = builtins.baseNameOf (toString mediaCfg.logo.svg);
|
||||
logoSvgMount = "/assets/${logoSvgFileName}";
|
||||
borderRadius = toString (containerCfg.extra.borderRadius or 32);
|
||||
resolveColor = value:
|
||||
if value == null then null
|
||||
else if !builtins.isString value then
|
||||
throw "favicon color values must be strings"
|
||||
else if lib.hasPrefix "#" value then
|
||||
value
|
||||
else
|
||||
lib.attrByPath [ value ] (throw "Unknown favicon color reference `${value}`") palette;
|
||||
normalizeProfile = profile:
|
||||
let
|
||||
bg =
|
||||
if profile ? bg then resolveColor profile.bg
|
||||
else if profile ? background then resolveColor profile.background
|
||||
else null;
|
||||
fg =
|
||||
if profile ? fg then resolveColor profile.fg
|
||||
else if profile ? foreground then resolveColor profile.foreground
|
||||
else null;
|
||||
in
|
||||
(lib.filterAttrs (name: _: !(builtins.elem name [ "bg" "background" "fg" "foreground" ])) profile)
|
||||
// lib.optionalAttrs (bg != null) { bg = bg; }
|
||||
// lib.optionalAttrs (fg != null) { fg = fg; };
|
||||
hostMappings = lib.mapAttrs' (mapping: profile:
|
||||
lib.nameValuePair
|
||||
(if lib.hasInfix "." mapping then mapping else "${mapping}.${serverCfg.domain}")
|
||||
profile
|
||||
(normalizeProfile profile)
|
||||
) (containerCfg.extra.mappings or {});
|
||||
traefikAssetPathRegexp =
|
||||
"^/(.*/)?"
|
||||
@@ -26,7 +49,9 @@ let
|
||||
inherit cacheControl;
|
||||
borderRadius = borderRadius;
|
||||
mappings = hostMappings;
|
||||
default = containerCfg.extra.default or null;
|
||||
default =
|
||||
if containerCfg.extra ? default then normalizeProfile containerCfg.extra.default
|
||||
else null;
|
||||
});
|
||||
pythonEnv = pkgs.python3.withPackages (ps: with ps; [
|
||||
cairosvg
|
||||
@@ -104,8 +129,8 @@ let
|
||||
return f"{text}px"
|
||||
|
||||
def _render_svg(profile):
|
||||
bg = _color(profile.get("background"), "#111827")
|
||||
fg = _color(profile.get("foreground"), "#f8fafc")
|
||||
bg = _color(profile.get("bg") or profile.get("background"), "#111827")
|
||||
fg = _color(profile.get("fg") or profile.get("foreground"), "#f8fafc")
|
||||
border_radius = _border_radius()
|
||||
logo_data_uri = _tinted_logo_data_uri(fg)
|
||||
|
||||
@@ -115,8 +140,26 @@ let
|
||||
<image href="{logo_data_uri}" x="0" y="0" width="{canvas}" height="{canvas}" preserveAspectRatio="xMidYMid meet" />
|
||||
</svg>"""
|
||||
|
||||
def _cache_key(host, profile):
|
||||
bg = _color(profile.get("bg") or profile.get("background"), "#111827")
|
||||
fg = _color(profile.get("fg") or profile.get("foreground"), "#f8fafc")
|
||||
cache_inputs = {
|
||||
"asset_size": ASSET_SIZE,
|
||||
"bg": bg,
|
||||
"border_radius": _border_radius(),
|
||||
"fg": fg,
|
||||
"host": host,
|
||||
"logo_hash": LOGO_HASH,
|
||||
}
|
||||
payload = json.dumps(cache_inputs, sort_keys=True, separators=(",", ":"))
|
||||
return hashlib.sha256(payload.encode("utf-8")).hexdigest()[:16]
|
||||
|
||||
def _cache_name(host, profile):
|
||||
safe_host = re.sub(r"[^a-z0-9.-]+", "_", host or "default")
|
||||
return f"{safe_host}-{_cache_key(host, profile)}.ico"
|
||||
|
||||
def _generate_asset(host, profile):
|
||||
cache_name = f"{host}.ico"
|
||||
cache_name = _cache_name(host, profile)
|
||||
target = CACHE_DIR / cache_name
|
||||
if target.exists():
|
||||
return target
|
||||
@@ -140,11 +183,20 @@ let
|
||||
return
|
||||
|
||||
asset_path = _generate_asset(host, profile)
|
||||
etag = f'"{asset_path.stem.rsplit("-", 1)[-1]}"'
|
||||
if self.headers.get("If-None-Match") == etag:
|
||||
self.send_response(304)
|
||||
self.send_header("ETag", etag)
|
||||
self.send_header("Cache-Control", APP_CONFIG.get("cacheControl", DEFAULT_CACHE_CONTROL))
|
||||
self.end_headers()
|
||||
return
|
||||
|
||||
payload = asset_path.read_bytes()
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Type", "image/x-icon")
|
||||
self.send_header("Content-Length", str(len(payload)))
|
||||
self.send_header("Cache-Control", APP_CONFIG.get("cacheControl", DEFAULT_CACHE_CONTROL))
|
||||
self.send_header("ETag", etag)
|
||||
self.end_headers()
|
||||
if include_body:
|
||||
self.wfile.write(payload)
|
||||
|
||||
+30
-27
@@ -38,11 +38,11 @@
|
||||
homepage.subdomain = "home";
|
||||
homepage.extra.stocks = [ "GLD" "VOO" "QUIK" "AMD" "DPRO" "KTOS" "NNE" "NVO" ];
|
||||
# ===== CLOUD =====
|
||||
# nextcloud.subdomain = "cloud";
|
||||
# collabora.subdomain = "office";
|
||||
# etherpad.subdomain = "pad";
|
||||
nextcloud.subdomain = "cloud";
|
||||
collabora.subdomain = "office";
|
||||
etherpad.subdomain = "pad";
|
||||
# ethercalc.subdomain = "calc";
|
||||
# immich.subdomain = "pic";
|
||||
immich.subdomain = "pic";
|
||||
# ===== FLIX =====
|
||||
# invidious.subdomain = "yt";
|
||||
# jellyfin.subdomain = "flix";
|
||||
@@ -51,39 +51,42 @@
|
||||
# transmission = { subdomain = "arr"; subpath = "transmission"; };
|
||||
# handbrake = { subdomain = "arr"; subpath = "hb"; };
|
||||
# ===== DEV =====
|
||||
# gitea.subdomain = "git";
|
||||
gitea.subdomain = "git";
|
||||
# ===== HOME =====
|
||||
# openhab.subdomain = "hab";
|
||||
# trmnl = { subdomain = "hass"; subpath = "trmnl"; };
|
||||
influx.subdomain = "metrum";
|
||||
|
||||
# freshrss.subdomain = "rss";
|
||||
# suwayomi.subdomain = "manga";
|
||||
# calibre.subdomain = "books";
|
||||
# selfmark = { subdomain = "arr"; subpath = "selfmark"; };
|
||||
freshrss.subdomain = "rss";
|
||||
suwayomi.subdomain = "manga";
|
||||
calibre.subdomain = "books";
|
||||
selfmark = { subdomain = "arr"; subpath = "selfmark"; };
|
||||
|
||||
favicon.extra = {
|
||||
mappings = {
|
||||
"home" = {
|
||||
background = "#0f172a";
|
||||
foreground = "#22c55e";
|
||||
};
|
||||
"cloud" = {
|
||||
background = "#0b1220";
|
||||
foreground = "#38bdf8";
|
||||
};
|
||||
"sso" = {
|
||||
background = "#1f1630";
|
||||
foreground = "#f59e0b";
|
||||
};
|
||||
"metrum" = {
|
||||
background = "#1a1d29";
|
||||
foreground = "#a78bfa";
|
||||
};
|
||||
traefik={bg="base01";fg="low0B";};
|
||||
umami={bg="base01";fg="low0B";};
|
||||
sso={bg="base01";fg="high08";};
|
||||
searx={bg="base01";fg="alt_high0D";};
|
||||
home={bg="base01";fg="base07";};
|
||||
cloud={bg="base01";fg="high0C";};
|
||||
office={bg="base01";fg="alt_high0C";};
|
||||
pad={bg="base01";fg="alt_high0C";};
|
||||
#calc={bg="base01";fg="alt_high0C";};
|
||||
pic={bg="base01";fg="high09";};
|
||||
yt={bg="base01";fg="high0F";};
|
||||
flix={bg="base01";fg="high0D";};
|
||||
arr={bg="base01";fg="low0D";};
|
||||
git={bg="base01";fg="high0A";};
|
||||
hab={bg="base01";fg="high08";};
|
||||
metrum={bg="base01";fg="low0B";};
|
||||
rss={bg="base01";fg="high0A";};
|
||||
manga={bg="base01";fg="high0E";};
|
||||
books={bg="base01";fg="high0E";};
|
||||
};
|
||||
default = {
|
||||
background = "#111827";
|
||||
foreground = "#ffffff";
|
||||
bg = "base01";
|
||||
fg = "base07";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user