Compare commits

...

21 Commits

Author SHA1 Message Date
soraefir d3f549751f media and cfg 2026-06-04 21:33:55 +02:00
sora-ext edb48ace8a Update systems/sandbox/cfg.nix 2026-06-04 17:59:24 +02:00
sora-ext 215b546128 Add modules/server/containers/apps/favicon.nix 2026-06-04 17:58:00 +02:00
sora-ext 35e41fa630 Update modules/server/containers/apps/homepage.nix 2026-06-04 17:29:52 +02:00
sora-ext 210cf2dc9a Add modules/shared/syscfg/media.nix 2026-06-04 17:29:20 +02:00
sora-ext 809dd65eb0 Update modules/shared/syscfg/default.nix 2026-06-04 17:29:06 +02:00
sora-ext a8f730b4af Update modules/server/containers/default.nix 2026-06-04 17:28:40 +02:00
sora-ext 023620de83 Add modules/server/containers/data/authentik/branding.yaml 2026-06-04 17:28:23 +02:00
sora-ext eb2258a850 Update modules/server/containers/apps/nextcloud.nix 2026-06-04 17:28:04 +02:00
sora-ext 720d8bf154 Update modules/server/containers/apps/authentik.nix 2026-06-04 17:27:53 +02:00
sora-ext 6da8823f85 Add systems/avalon/hardware.nix 2026-06-04 10:55:49 +02:00
sora-ext 4d8fc16bbc Delete systems/avalon/server/docker/secrets.txt 2026-06-04 10:55:24 +02:00
sora-ext f2f9e95198 Update systems/asgard/default.nix 2026-06-04 10:55:09 +02:00
sora-ext 07c748071e Update systems/asgard/cfg.nix 2026-06-04 10:54:59 +02:00
sora-ext b489d719da Update shells/default.nix 2026-06-04 10:54:44 +02:00
sora-ext 9e042ebeaf Update modules/nixos/tools/develop/ollama/default.nix 2026-06-04 10:54:10 +02:00
sora-ext d4887fbd64 Update modules/nixos/tools/debug/default.nix 2026-06-04 10:54:00 +02:00
sora-ext 3642b2f41b Update modules/nixos/tools/debug/default.nix 2026-06-04 10:53:24 +02:00
sora-ext d9f7c63b8a Update modules/nixos/system/hw/udev/default.nix 2026-06-04 10:53:04 +02:00
sora-ext 0b5fd3ac41 Update modules/nixos/system/hw/power/default.nix 2026-06-04 10:52:44 +02:00
sora-ext e9f5942202 Update modules/home/gui/theme/default.nix 2026-06-04 10:46:24 +02:00
36 changed files with 513 additions and 45 deletions
-1
View File
@@ -1,6 +1,5 @@
{ lib, config, pkgs, ... }:
let
colorVariant = " black";
gtkThemeFromScheme = import ./gtk-theme-gen.nix { inherit pkgs config; };
wallpaperGen = import ./wallpaper-gen.nix { inherit pkgs config; };
in {
+4 -4
View File
@@ -15,10 +15,10 @@
# suspend to RAM (deep) rather than `s2idle`
boot.kernelParams = [ "mem_sleep_default=deep" ];
# suspend-then-hibernate
systemd.sleep.extraConfig = ''
HibernateDelaySec=30m
SuspendState=mem
'';
systemd.sleep.settings.Sleep = {
HibernateDelaySec = "30m";
SuspendState = "mem";
};
services.logind.settings.Login.HandleLidSwitch = "suspend-then-hibernate";
# Hibernate on power button pressed
+2 -2
View File
@@ -1,8 +1,8 @@
{ ... }: {
{ pkgs, ... }: {
systemd.services.systemd-udevd.restartIfChanged = false;
services.udev = {
packages = [ ];
packages = with pkgs; [ android-udev-rules ];
extraRules = ''
SUBSYSTEM=="usb", ATTRS{idVendor}=="2104", ATTRS{idProduct}=="0127", GROUP="plugdev", TAG+="uaccess"
SUBSYSTEM=="usb", ATTRS{idVendor}=="2104", ATTRS{idProduct}=="0118", GROUP="plugdev", TAG+="uaccess"
+2 -5
View File
@@ -1,12 +1,9 @@
{ pkgs, config, lib, ... }: {
config = lib.mkIf (config.syscfg.make.develop) {
programs.adb.enable = true;
# services.udev.packages = [
# pkgs.android-udev-rules
# ];
programs.wireshark.enable = true;
environment.systemPackages = with pkgs; [ wget dconf wireshark ];
environment.systemPackages = with pkgs; [ wget dconf wireshark mtr android-tools ];
};
}
@@ -1,14 +1,13 @@
{ lib, config, pkgs, ... }:
let
ollamaPkg = pkgs.ollama-rocm;
ollamaPkg = pkgs.ollama-vulkan;
in{
config = lib.mkIf (config.syscfg.make.develop) {
services.ollama = {
enable = true;
package = ollamaPkg;
acceleration = "rocm";
loadModels = [ "deepseek-v2:lite" "qwen2.5-coder:7b" "qwen2.5-coder:1.5b" ];
loadModels = [ ];
syncModels = true;
};
environment.systemPackages = with pkgs; [ ollamaPkg ];
@@ -2,11 +2,23 @@
let
version = "2026.2.2";
serverCfg = config.syscfg.server;
mediaCfg = config.syscfg.media;
authentikBackground = if mediaCfg.banner.png != null then mediaCfg.banner.png else mediaCfg.bg;
logoSvgFileName = builtins.baseNameOf (toString mediaCfg.logo.svg);
logoIcoFileName = builtins.baseNameOf (toString mediaCfg.logo.ico);
backgroundFileName = builtins.baseNameOf (toString authentikBackground);
logoSvgMount = "/media/custom/${logoSvgFileName}";
logoIcoMount = "/media/custom/${logoIcoFileName}";
backgroundMount = "/media/custom/${backgroundFileName}";
authentikData = builder.mkData {
name = "authentik"; dir = "authentik"; vars = {
AUTHENTIK_DOMAIN = "${containerCfg.subdomain}.${serverCfg.domain}";
COOKIE_DOMAIN = "${serverCfg.domain}";
AUTHENTIK_LDAP_DC_DOMAIN = "dc=ldap," + (lib.concatMapStringsSep "," (x: "dc=${x}") (lib.splitString "." serverCfg.domain));
AUTHENTIK_BRANDING_TITLE = if containerCfg.extra ? name then containerCfg.extra.name else "authentik";
AUTHENTIK_BRANDING_LOGO = "custom/${logoSvgFileName}";
AUTHENTIK_BRANDING_FAVICON = "custom/${logoIcoFileName}";
AUTHENTIK_BRANDING_BACKGROUND = "custom/${backgroundFileName}";
}
// (if serverCfg.containers?jellyfin then { JELLYFIN_DOMAIN = "${serverCfg.containers.jellyfin.subdomain}.${serverCfg.domain}";} else {})
// (if serverCfg.containers?gitea then { GITEA_DOMAIN = "${serverCfg.containers.gitea.subdomain}.${serverCfg.domain}";} else {})
@@ -60,6 +72,9 @@ in {
"${serverCfg.path.config}/authentik/media:/media"
"${serverCfg.path.config}/authentik/templates:/templates"
"${authentikData}:/blueprints/custom:ro"
"${mediaCfg.logo.svg}:${logoSvgMount}:ro"
"${mediaCfg.logo.ico}:${logoIcoMount}:ro"
"${authentikBackground}:${backgroundMount}:ro"
];
};
};
@@ -82,6 +97,9 @@ in {
"${serverCfg.path.config}/authentik/media:/media"
"${serverCfg.path.config}/authentik/templates:/templates"
"${authentikData}:/blueprints/custom:ro"
"${mediaCfg.logo.svg}:${logoSvgMount}:ro"
"${mediaCfg.logo.ico}:${logoIcoMount}:ro"
"${authentikBackground}:${backgroundMount}:ro"
];
};
};
@@ -103,6 +121,7 @@ in {
AK="${pkgs.podman}/bin/podman --events-backend=none exec --env-file ${config.sops.secrets."CUSTOM".path} -e DOMAIN=${serverCfg.domain} -u root authentik-worker ak"
$AK apply_blueprint /blueprints/custom/authentik.yaml
$AK apply_blueprint /blueprints/custom/branding.yaml
$AK apply_blueprint /blueprints/custom/traefik.yaml
$AK apply_blueprint /blueprints/custom/ldap.yaml
+301
View File
@@ -0,0 +1,301 @@
{ config, containerCfg, pkgs, lib, builder, name, ... }:
let
serverCfg = config.syscfg.server;
mediaCfg = config.syscfg.media;
port = 8080;
cacheControl = containerCfg.extra.cacheControl or "public, max-age=86400";
priority = toString (containerCfg.extra.priority or 1000);
logoSvgFileName = builtins.baseNameOf (toString mediaCfg.logo.svg);
logoSvgMount = "/assets/${logoSvgFileName}";
configFile = pkgs.writeText "favicon-config.json" (builtins.toJSON {
inherit cacheControl;
mappings = containerCfg.extra.mappings or {};
default = containerCfg.extra.default or null;
logoScale = containerCfg.extra.logoScale or 0.72;
});
pythonEnv = pkgs.python3.withPackages (ps: with ps; [
cairosvg
pillow
]);
serverScript = pkgs.writeText "favicon-server.py" ''
import base64
import hashlib
from io import BytesIO
import json
import os
import re
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from urllib.parse import urlsplit
import cairosvg
from PIL import Image
CONFIG_PATH = os.environ.get("FAVICON_CONFIG", "/config/config.json")
LOGO_PATH = os.environ.get("FAVICON_LOGO", "/assets/logo.svg")
CACHE_DIR = Path(os.environ.get("FAVICON_CACHE_DIR", "/cache"))
LISTEN_HOST = os.environ.get("FAVICON_LISTEN_HOST", "0.0.0.0")
LISTEN_PORT = int(os.environ.get("FAVICON_PORT", "8080"))
DEFAULT_CACHE_CONTROL = "public, max-age=86400"
PATH_PATTERN = re.compile(
r"(?i)(?:^|/)(?:"
r"favicon(?:-[0-9]+x[0-9]+)?\.(?:ico|png|svg)"
r"|apple-touch-icon(?:-precomposed)?\.png"
r"|android-chrome-(?:192x192|512x512)\.png"
r"|mstile-150x150\.png"
r")$"
)
with open(CONFIG_PATH, "r", encoding="utf-8") as fh:
APP_CONFIG = json.load(fh)
with open(LOGO_PATH, "rb") as fh:
LOGO_BYTES = fh.read()
LOGO_DATA_URI = "data:image/svg+xml;base64," + base64.b64encode(LOGO_BYTES).decode("ascii")
def _normalize_host(host):
return (host or "").split(":", 1)[0].strip().lower()
def _pick_profile(host):
mappings = APP_CONFIG.get("mappings", {})
exact = mappings.get(host)
if exact is not None:
return exact
wildcard_matches = []
for pattern, profile in mappings.items():
if pattern.startswith("*.") and host.endswith(pattern[1:].lower()):
wildcard_matches.append((pattern.count("."), profile))
if wildcard_matches:
wildcard_matches.sort(key=lambda item: item[0], reverse=True)
return wildcard_matches[0][1]
return APP_CONFIG.get("default")
def _color(value, fallback):
return value if isinstance(value, str) and value else fallback
def _badge_text(profile):
badge = profile.get("icon") or profile.get("label") or profile.get("text") or ""
badge = str(badge).strip()
if not badge:
return ""
return badge[:3].upper()
def _logo_scale():
scale = APP_CONFIG.get("logoScale", 0.72)
try:
scale = float(scale)
except (TypeError, ValueError):
return 0.72
return max(0.2, min(0.9, scale))
def _render_svg(profile):
bg = _color(profile.get("background"), "#111827")
fg = _color(profile.get("foreground"), "#f8fafc")
accent = _color(profile.get("accent"), "#38bdf8")
badge = _badge_text(profile)
canvas = 256
panel = 188
panel_offset = (canvas - panel) // 2
logo_size = int(canvas * _logo_scale())
logo_offset = (canvas - logo_size) // 2
badge_svg = ""
if badge:
badge_svg = f"""
<g>
<circle cx="196" cy="60" r="34" fill="{accent}" />
<text x="196" y="72"
font-family="DejaVu Sans, sans-serif"
font-size="34"
font-weight="700"
text-anchor="middle"
fill="{fg}">{badge}</text>
</g>
"""
return f"""<svg xmlns="http://www.w3.org/2000/svg" width="{canvas}" height="{canvas}" viewBox="0 0 {canvas} {canvas}">
<defs>
<radialGradient id="glow" cx="84%" cy="16%" r="75%">
<stop offset="0%" stop-color="{accent}" stop-opacity="0.34" />
<stop offset="100%" stop-color="{accent}" stop-opacity="0" />
</radialGradient>
</defs>
<rect width="{canvas}" height="{canvas}" rx="56" fill="{bg}" />
<rect width="{canvas}" height="{canvas}" rx="56" fill="url(#glow)" />
<rect x="{panel_offset}" y="{panel_offset}" width="{panel}" height="{panel}" rx="44" fill="#ffffff" fill-opacity="0.08" stroke="{accent}" stroke-opacity="0.6" stroke-width="6" />
<rect x="42" y="214" width="172" height="10" rx="5" fill="{accent}" fill-opacity="0.9" />
<image href="{LOGO_DATA_URI}" x="{logo_offset}" y="{logo_offset}" width="{logo_size}" height="{logo_size}" preserveAspectRatio="xMidYMid meet" />
{badge_svg}
</svg>"""
def _target_spec(path):
name = Path(path).name.lower()
if name.endswith(".svg"):
return ("svg", None)
if name.endswith(".ico"):
return ("ico", 64)
known_sizes = {
"apple-touch-icon.png": 180,
"apple-touch-icon-precomposed.png": 180,
"android-chrome-192x192.png": 192,
"android-chrome-512x512.png": 512,
"mstile-150x150.png": 150,
"favicon.png": 256,
}
if name in known_sizes:
return ("png", known_sizes[name])
match = re.search(r"([0-9]{2,4})x([0-9]{2,4})", name)
if match:
return ("png", int(match.group(1)))
return ("png", 256)
def _cache_key(host, spec, profile):
payload = json.dumps(
{"host": host, "spec": spec, "profile": profile, "logo": hashlib.sha256(LOGO_BYTES).hexdigest()},
sort_keys=True,
).encode("utf-8")
return hashlib.sha256(payload).hexdigest()
def _mime(ext):
return {
"svg": "image/svg+xml",
"png": "image/png",
"ico": "image/x-icon",
}[ext]
def _generate_asset(host, profile, path):
spec = _target_spec(path)
cache_name = f"{_cache_key(host, spec, profile)}.{spec[0]}"
target = CACHE_DIR / cache_name
if target.exists():
return target, _mime(spec[0])
CACHE_DIR.mkdir(parents=True, exist_ok=True)
svg = _render_svg(profile).encode("utf-8")
if spec[0] == "svg":
target.write_bytes(svg)
return target, _mime("svg")
png_bytes = cairosvg.svg2png(bytestring=svg, output_width=spec[1], output_height=spec[1])
if spec[0] == "png":
target.write_bytes(png_bytes)
return target, _mime("png")
image = Image.open(BytesIO(png_bytes))
image.save(target, format="ICO", sizes=[(64, 64), (48, 48), (32, 32), (16, 16)])
image.close()
return target, _mime("ico")
class Handler(BaseHTTPRequestHandler):
server_version = "favicon-router/1.0"
def _serve(self, include_body):
path = urlsplit(self.path).path
if not PATH_PATTERN.search(path):
self.send_error(404)
return
host = _normalize_host(self.headers.get("Host", ""))
profile = _pick_profile(host)
if not profile:
self.send_error(404, "No favicon mapping for host")
return
asset_path, content_type = _generate_asset(host, profile, path)
payload = asset_path.read_bytes()
self.send_response(200)
self.send_header("Content-Type", content_type)
self.send_header("Content-Length", str(len(payload)))
self.send_header("Cache-Control", APP_CONFIG.get("cacheControl", DEFAULT_CACHE_CONTROL))
self.end_headers()
if include_body:
self.wfile.write(payload)
def do_GET(self):
self._serve(include_body=True)
def do_HEAD(self):
self._serve(include_body=False)
def log_message(self, fmt, *args):
print("%s - - [%s] %s" % (self.address_string(), self.log_date_time_string(), fmt % args))
if __name__ == "__main__":
httpd = ThreadingHTTPServer((LISTEN_HOST, LISTEN_PORT), Handler)
httpd.serve_forever()
'';
image = pkgs.dockerTools.streamLayeredImage {
name = "favicon";
tag = "1";
contents = [
pythonEnv
pkgs.fontconfig
pkgs.dejavu_fonts
pkgs.cacert
pkgs.tzdata
];
config = {
Entrypoint = [ "${pythonEnv}/bin/python3" serverScript ];
ExposedPorts = { "${toString port}/tcp" = { }; };
WorkingDir = "/";
};
};
in {
runtime = {
paths = [
{
path = "${serverCfg.path.config}/favicon";
mode = "0755";
dirs = [ "cache" ];
}
];
containers = {
server = builder.mkContainer {
imageStream = image;
port = port;
extraEnv = {
FAVICON_CONFIG = "/config/config.json";
FAVICON_LOGO = logoSvgMount;
FAVICON_CACHE_DIR = "/cache";
FAVICON_PORT = toString port;
FONTCONFIG_FILE = "${pkgs.fontconfig.out}/etc/fonts/fonts.conf";
FONTCONFIG_PATH = "${pkgs.fontconfig.out}/etc/fonts";
};
extraLabels = {
"traefik.enable" = "true";
"traefik.http.routers.${name}.entrypoints" = "web-secure";
"traefik.http.routers.${name}.rule" = "HostRegexp(`{host:.+}`) && PathRegexp(`^/(?:.*\\/)?(?:favicon(?:-[0-9]+x[0-9]+)?\\.(?:ico|png|svg)|apple-touch-icon(?:-precomposed)?\\.png|android-chrome-(?:192x192|512x512)\\.png|mstile-150x150\\.png)$`)";
"traefik.http.routers.${name}.priority" = priority;
"traefik.http.routers.${name}.tls" = "true";
"traefik.http.services.${name}.loadbalancer.server.port" = toString port;
};
overrides = {
volumes = [
"${configFile}:/config/config.json:ro"
"${serverCfg.path.config}/favicon/cache:/cache"
"${mediaCfg.logo.svg}:${logoSvgMount}:ro"
];
};
};
};
};
}
+6 -1
View File
@@ -2,13 +2,17 @@
let
version = "latest";
serverCfg = config.syscfg.server;
mediaCfg = config.syscfg.media;
backgroundImage = if mediaCfg.banner.png != null then mediaCfg.banner.png else mediaCfg.bg;
backgroundFileName = builtins.baseNameOf (toString backgroundImage);
backgroundMount = "/app/public/media/${backgroundFileName}";
settings = pkgs.writers.writeYAML "settings.yaml" {
title = "My Self-Hosted Dashboard";
description = "";
startUrl = "https://${containerCfg.subdomain}.${serverCfg.domain}";
background = {
# image = "...";
image = "/media/${backgroundFileName}";
blur = "sm"; #sm, md, xl,...
# saturate = "";
# brightness = "";
@@ -278,6 +282,7 @@ in {
"${services}:/app/config/services.yaml:ro"
"${widgets}:/app/config/widgets.yaml:ro"
"${bookmarks}:/app/config/bookmarks.yaml:ro"
"${backgroundImage}:${backgroundMount}:ro"
];
};
};
+27 -13
View File
@@ -1,7 +1,17 @@
{ config, containerCfg, pkgs, lib, builder, name,... }:
let
version = "31";
serverCfg = config.syscfg.server;
version = "31";
serverCfg = config.syscfg.server;
mediaCfg = config.syscfg.media;
backgroundImage = if mediaCfg.banner.png != null then mediaCfg.banner.png else mediaCfg.bg;
backgroundFileName = builtins.baseNameOf (toString backgroundImage);
logoPngFileName = builtins.baseNameOf (toString mediaCfg.logo.png);
logoSvgFileName = builtins.baseNameOf (toString mediaCfg.logo.svg);
logoIcoFileName = builtins.baseNameOf (toString mediaCfg.logo.ico);
logoPngMount = "/var/www/html/theming/${logoPngFileName}";
logoSvgMount = "/var/www/html/theming/${logoSvgFileName}";
logoIcoMount = "/var/www/html/theming/${logoIcoFileName}";
backgroundMount = "/var/www/html/theming/${backgroundFileName}";
in {
requires = {
secrets = [ name ];
@@ -50,6 +60,10 @@ in {
volumes = [
"${serverCfg.path.config}/nextcloud:/var/www/html"
"${serverCfg.path.cloud}:/var/www/html/data"
"${mediaCfg.logo.png}:${logoPngMount}:ro"
"${mediaCfg.logo.svg}:${logoSvgMount}:ro"
"${mediaCfg.logo.ico}:${logoIcoMount}:ro"
"${backgroundImage}:${backgroundMount}:ro"
];
};
};
@@ -168,21 +182,21 @@ in {
'') containerCfg.extra.federatedServers or [])}
$OCC config:app:set systemtags allow_user_creating --value="no"
echo "Applying Theme..."
$OCC config:app:set theming url --value="https://${containerCfg.subdomain}.${serverCfg.domain}"
${lib.optionalString (containerCfg.extra ? name) ''$OCC config:app:set theming name --value="${containerCfg.extra.name}"''}
${lib.optionalString (containerCfg.extra ? slogan) ''$OCC config:app:set theming slogan --value="${containerCfg.extra.slogan}"''}
$OCC config:app:set theming background_color --value="${serverCfg.colorScheme.palette.base02}"
$OCC config:app:set theming primary_color --value="${serverCfg.colorScheme.palette.base0C}"
#$OCC theming:config logo {serverCfg.colorScheme.logo}
#$OCC theming:config logoheader {serverCfg.colorScheme.logo}
#$OCC theming:config background {serverCfg.colorScheme.bg}
else
echo "Nextcloud is already installed. Skipping setup."
fi
echo "Applying Theme..."
$OCC config:app:set theming url --value="https://${containerCfg.subdomain}.${serverCfg.domain}"
${lib.optionalString (containerCfg.extra ? name) ''$OCC config:app:set theming name --value="${containerCfg.extra.name}"''}
${lib.optionalString (containerCfg.extra ? slogan) ''$OCC config:app:set theming slogan --value="${containerCfg.extra.slogan}"''}
$OCC config:app:set theming background_color --value="${serverCfg.colorScheme.palette.base02}"
$OCC config:app:set theming primary_color --value="${serverCfg.colorScheme.palette.base0C}"
$OCC theming:config logo "${logoPngMount}"
$OCC theming:config logoheader "${logoSvgMount}"
$OCC theming:config favicon "${logoIcoMount}"
$OCC theming:config background "${backgroundMount}"
$OCC config:app:set serverinfo token --value="$HOMEPAGE_VAR_NEXTCLOUD_API"
echo "Maintenance..."
@@ -0,0 +1,13 @@
version: 1
metadata:
name: "Branding Setup"
entries:
- model: authentik_brands.brand
identifiers:
domain: "@AUTHENTIK_DOMAIN@"
attrs:
domain: "@AUTHENTIK_DOMAIN@"
branding_title: "@AUTHENTIK_BRANDING_TITLE@"
branding_logo: "@AUTHENTIK_BRANDING_LOGO@"
branding_favicon: "@AUTHENTIK_BRANDING_FAVICON@"
branding_default_flow_background: "@AUTHENTIK_BRANDING_BACKGROUND@"
+3 -1
View File
@@ -81,11 +81,13 @@ in {
}) allVMConfigs
// mkNamedUnits (e: {
name = "${e.name}-setup";
value = {
value = {
description = "Run ${e.name} setup";
after = [ "podman-${e.name}-${e.trigger}.service" ];
wants = [ "podman-${e.name}-${e.trigger}.service" ];
partOf = [ "podman-${e.name}-${e.trigger}.service" ];
wantedBy = [ "multi-user.target" ];
restartTriggers = [ e.script ];
serviceConfig = {
Type = "oneshot";
TimeoutStartSec = "360s";
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 959 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

+4
View File
@@ -28,6 +28,10 @@ in with lib; {
type = types.oneOf [ types.bool (types.submodule { options = import ./server.nix {inherit lib;}; }) ];
default = false;
};
media = mkOption {
type = types.submodule { options = import ./media.nix { inherit lib; }; };
default = {};
};
extra = import ./extra.nix {inherit lib;};
};
}
+51
View File
@@ -0,0 +1,51 @@
{ lib, ... }:
let
mediaDir = ../media;
mainDir = mediaDir + "/main";
maybePath = rel:
let path = mediaDir + "/${rel}";
in if builtins.pathExists path then path else null;
mainImages =
let
names = builtins.attrNames (builtins.readDir mainDir);
isImage = name: builtins.match ".*\\.(png|jpg|jpeg|webp|svg)$" name != null;
in
map (name: mainDir + "/${name}") (lib.sort builtins.lessThan (lib.filter isImage names));
in
with lib; {
logo = {
ico = mkOption {
type = types.path;
default = mediaDir + "/logo.ico";
};
svg = mkOption {
type = types.path;
default = mediaDir + "/logo.svg";
};
png = mkOption {
type = types.path;
default = mediaDir + "/logo.png";
};
};
banner = {
svg = mkOption {
type = types.nullOr types.path;
default = maybePath "banner.svg";
};
png = mkOption {
type = types.nullOr types.path;
default = maybePath "banner.png";
};
};
main = mkOption {
type = types.listOf types.path;
default = if mainImages != [ ] then mainImages else lib.optional (maybePath "main/bg.png" != null) (mediaDir + "/main/bg.png");
};
bg = mkOption {
type = types.path;
default = let bg = maybePath "main/bg.png"; in if bg != null then bg else builtins.head mainImages;
};
}
+1 -1
View File
@@ -5,7 +5,7 @@ let
in forEachSystem (system:
let
overlays = import ../overlays { inherit inputs pkgs; };
overrides = { custom = import ../pkgs { inherit pkgs; }; };
overrides = { custom = import ../packages { inherit pkgs; }; };
pkgs = import inputs.nixpkgs { inherit system overlays; } // overrides;
in {
default = import ./devsh { inherit pkgs; };
+3
View File
@@ -0,0 +1,3 @@
{ ... }:{
}
-14
View File
@@ -1,14 +0,0 @@
AUTHENTIK_DB_PASSWORD=NTQRO0rhPCd4L3HLNK4AT09Npz+ks1jyRC6AOyo5u+k=
AUTHENTIK_SECRET_KEY=9Zw8Sy8257iJmRdBhUKGiq3d7uYAkhC9smuDUClE8aR1iPdpHHds+K2D1Zy3lwj2Hjnasu5jnopkhwnABWDu8A==
AUTHENTIK_EMAIL_PASSWORD=w+g:cPU+e.<q,f<mj3DFPxXxo4h2SVS9.;,T<!Sra>y!mNcAsiAp4jPCLTmjte2d
ETHERPAD_DB_PASSWORD=d43352c3906516bf4c34d63316509cb4b1621167af84c81b60689779a62b2348
ETHERPAD_ADMIN_PASSWORD=Hackme55#
COLLABORA_USER=...
COLLABORA_PASSWORD=...
+35
View File
@@ -61,6 +61,41 @@
suwayomi.subdomain = "manga";
calibre.subdomain = "books";
selfmark = { subdomain = "arr"; subpath = "selfmark"; };
favicon.extra = {
mappings = {
"home" = {
label = "H";
background = "#0f172a";
foreground = "#f8fafc";
accent = "#22c55e";
};
"cloud" = {
label = "C";
background = "#0b1220";
foreground = "#f8fafc";
accent = "#38bdf8";
};
"sso" = {
label = "S";
background = "#1f1630";
foreground = "#fff7ed";
accent = "#f59e0b";
};
"metrum" = {
label = "M";
background = "#1a1d29";
foreground = "#f8fafc";
accent = "#a78bfa";
};
};
default = {
label = "?";
background = "#111827";
foreground = "#f9fafb";
accent = "#6b7280";
};
};
};
};
};