220 lines
5.6 KiB
Nix
220 lines
5.6 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
registry,
|
|
...
|
|
}:
|
|
let
|
|
cfg = config.modules.services.homepage;
|
|
|
|
mkRegistryPath = registryName: host: entry: "registry.${registryName}.${host}.${entry}";
|
|
|
|
homepageEntries = lib.concatLists (
|
|
lib.mapAttrsToList (
|
|
host: entries:
|
|
lib.mapAttrsToList (entry: homepage: {
|
|
inherit host entry homepage;
|
|
composite = registry.join host entry;
|
|
}) entries
|
|
) registry.homepage
|
|
);
|
|
|
|
mkHref = item:
|
|
if item.homepage.href != null then
|
|
item.homepage.href
|
|
else if (item.composite.route or null) != null then
|
|
if item.composite.route.port != null then
|
|
"https://${item.composite.route.domain}"
|
|
else if item.composite.route.http.port != null then
|
|
"http://${item.composite.route.domain}"
|
|
else
|
|
throw "${mkRegistryPath "homepage" item.host item.entry} needs href or a matching TCP route entry"
|
|
else
|
|
throw "${mkRegistryPath "homepage" item.host item.entry} needs href or a matching route entry";
|
|
|
|
mkServiceAttrs = item:
|
|
lib.filterAttrs (_: value: value != null) {
|
|
href = mkHref item;
|
|
inherit (item.homepage) description icon;
|
|
};
|
|
|
|
sortedEntries = lib.sort (left: right: left.homepage.name < right.homepage.name) homepageEntries;
|
|
|
|
insertEntry = acc: item:
|
|
acc
|
|
// {
|
|
${item.homepage.tab} = (acc.${item.homepage.tab} or { })
|
|
// {
|
|
${item.homepage.section} = (acc.${item.homepage.tab}.${item.homepage.section} or [ ]) ++ [
|
|
{ ${item.homepage.name} = mkServiceAttrs item; }
|
|
];
|
|
};
|
|
};
|
|
|
|
entriesByTab = lib.foldl' insertEntry { } sortedEntries;
|
|
|
|
tabOrder = [
|
|
"Apps"
|
|
"Download"
|
|
"System"
|
|
];
|
|
|
|
orderedTabs = tabOrder ++ lib.filter (tab: !lib.elem tab tabOrder) (lib.attrNames entriesByTab);
|
|
|
|
sectionOrderByTab = {
|
|
Apps = [
|
|
"Media"
|
|
"Files"
|
|
"Other"
|
|
];
|
|
Download = [
|
|
"Library Managers"
|
|
"Other Utilities"
|
|
"Download Clients"
|
|
];
|
|
System = [
|
|
"Monitoring"
|
|
"Network"
|
|
"Machines"
|
|
"Docker"
|
|
];
|
|
};
|
|
|
|
orderedSections = tab: sections:
|
|
let
|
|
sectionOrder = sectionOrderByTab.${tab} or [ ];
|
|
in
|
|
sectionOrder ++ lib.filter (section: !lib.elem section sectionOrder) (lib.attrNames sections);
|
|
|
|
mkTab = tab:
|
|
let
|
|
sections = entriesByTab.${tab};
|
|
in
|
|
{
|
|
${tab} = map (section: { ${section} = sections.${section}; }) (orderedSections tab sections);
|
|
};
|
|
|
|
homepageServices = map mkTab (lib.filter (tab: lib.hasAttr tab entriesByTab) orderedTabs);
|
|
|
|
selfEntry = registry.join cfg.hostName cfg.entryName;
|
|
selfAddress = selfEntry.address or null;
|
|
selfRoute = selfEntry.route or null;
|
|
|
|
derivedAllowedHosts = lib.unique (
|
|
lib.optional (selfRoute != null) selfRoute.domain
|
|
++ lib.optionals (selfAddress != null) [
|
|
selfAddress
|
|
"${selfAddress}:${toString cfg.listenPort}"
|
|
]
|
|
);
|
|
in
|
|
{
|
|
options.modules.services.homepage = {
|
|
enable = lib.mkEnableOption "Homepage Dashboard";
|
|
|
|
listenPort = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 8082;
|
|
description = "Port for Homepage to bind to.";
|
|
};
|
|
|
|
hostName = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "Host registry key that owns the Homepage guest entry.";
|
|
};
|
|
|
|
entryName = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "homepage";
|
|
description = "Registry entry name for this Homepage service.";
|
|
};
|
|
|
|
allowedHosts = lib.mkOption {
|
|
type = lib.types.nullOr (lib.types.listOf lib.types.str);
|
|
default = null;
|
|
description = "Host headers accepted by Homepage. Defaults to matching address and route registry entries.";
|
|
};
|
|
|
|
environmentFile = lib.mkOption {
|
|
type = lib.types.path;
|
|
default = "/run/secrets/homepage.env";
|
|
description = "Environment file passed to Homepage.";
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
services.homepage-dashboard = {
|
|
enable = true;
|
|
openFirewall = true;
|
|
inherit (cfg) listenPort;
|
|
environmentFiles = [ cfg.environmentFile ];
|
|
allowedHosts = lib.concatStringsSep "," (if cfg.allowedHosts == null then derivedAllowedHosts else cfg.allowedHosts);
|
|
services = homepageServices;
|
|
widgets = [
|
|
{
|
|
greeting = {
|
|
text_size = "xl";
|
|
text = "tracion";
|
|
};
|
|
}
|
|
{
|
|
openmeteo = {
|
|
units = "metric";
|
|
latitude = "{{HOMEPAGE_VAR_LATITUDE}}";
|
|
longitude = "{{HOMEPAGE_VAR_LONGITUDE}}";
|
|
cache = 5;
|
|
};
|
|
}
|
|
{
|
|
datetime = {
|
|
text_size = "xl";
|
|
locale = "en";
|
|
format = {
|
|
dateStyle = "full";
|
|
timeStyle = "medium";
|
|
hour12 = false;
|
|
};
|
|
};
|
|
}
|
|
];
|
|
settings = {
|
|
title = "Tracion";
|
|
theme = "dark";
|
|
color = "slate";
|
|
showStats = false;
|
|
hideVersion = true;
|
|
bookmarksStyle = "icons";
|
|
useEqualHeights = true;
|
|
layout = {
|
|
Apps = {
|
|
style = "row";
|
|
columns = 1;
|
|
tab = "Apps";
|
|
Media = {
|
|
style = "row";
|
|
columns = 2;
|
|
};
|
|
Files = {
|
|
style = "row";
|
|
columns = 2;
|
|
};
|
|
Other = {
|
|
style = "row";
|
|
columns = 3;
|
|
iconsOnly = true;
|
|
};
|
|
};
|
|
Download = {
|
|
style = "row";
|
|
columns = 1;
|
|
tab = "Download";
|
|
};
|
|
System = {
|
|
tab = "System";
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
}
|