aboutsummaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorAkshay Nair <phenax5@gmail.com>2025-06-10 13:06:48 +0530
committerAkshay Nair <phenax5@gmail.com>2025-06-10 18:58:25 +0530
commit2a73a5899124fa15c3962b9bd385ebd415a0ccf9 (patch)
tree3b0c29a29cf9ef4c844afc90aafcd61ef8122d21 /modules
parent279241632b566fd26011409672277ca02ed697d1 (diff)
downloadhomeserver-nixos-config-2a73a5899124fa15c3962b9bd385ebd415a0ccf9.tar.gz
homeserver-nixos-config-2a73a5899124fa15c3962b9bd385ebd415a0ccf9.zip
Add dashboard for linking services
Diffstat (limited to 'modules')
-rw-r--r--modules/dashboard/bacchus-dashboard.service.nix46
-rw-r--r--modules/dashboard/dashboard-template.nix114
-rw-r--r--modules/dashboard/default.nix40
3 files changed, 200 insertions, 0 deletions
diff --git a/modules/dashboard/bacchus-dashboard.service.nix b/modules/dashboard/bacchus-dashboard.service.nix
new file mode 100644
index 0000000..c14111b
--- /dev/null
+++ b/modules/dashboard/bacchus-dashboard.service.nix
@@ -0,0 +1,46 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+ cfg = config.services.bacchus-dashboard;
+
+ rootHTML = import ./dashboard-template.nix cfg;
+ indexHTMLFile = pkgs.writeText "index.html" rootHTML;
+
+ pageDir = pkgs.stdenv.mkDerivation {
+ pname = "dashboard-page";
+ version = "0.0.0";
+ unpackPhase = "true"; # no source
+ installPhase = ''
+ mkdir -p $out;
+ cp ${indexHTMLFile} $out/index.html;
+ '';
+ };
+in
+{
+ options.services.bacchus-dashboard = {
+ enable = mkEnableOption "Enable dashboard";
+ title = mkOption { type = types.str; default = "Dashboard"; };
+ links = mkOption {
+ type = types.listOf (types.attrs);
+ default = [];
+ };
+ openFirewall = mkEnableOption "Open firewall ports";
+ host = mkOption { type = types.str; default = "_"; };
+ port = mkOption { type = types.int; default = 80; };
+ };
+
+ config = {
+ services.nginx = mkIf cfg.enable {
+ enable = true;
+ virtualHosts."${cfg.host}" = {
+ listen = [ { addr = toString cfg.port; } ];
+ locations."/" = {
+ root = pageDir;
+ tryFiles = "$uri /index.html";
+ };
+ };
+ };
+
+ networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ 80 ];
+ };
+}
diff --git a/modules/dashboard/dashboard-template.nix b/modules/dashboard/dashboard-template.nix
new file mode 100644
index 0000000..a1df813
--- /dev/null
+++ b/modules/dashboard/dashboard-template.nix
@@ -0,0 +1,114 @@
+{ title, links, ... }:
+with builtins;
+let
+ withLinkAttr = link: attr: def: value:
+ if hasAttr attr link then value else def;
+
+ linkHTML = link: ''
+ <div>
+ <a href="${link.url}" class="card" style="${withLinkAttr link "color" "" "--color-card-accent: ${link.color}"}">
+ ${link.title} ${if hasAttr "key" link then "(${link.key})" else ""}
+ <div class="card-link">${link.url}</div>
+ </a>
+ </div>
+ '';
+
+ script = ''
+ window.addEventListener('keydown', (event) => {
+ const noMod = !event.ctrlKey && !event.altKey;
+ ${concatStringsSep "\n" (map (link:
+ withLinkAttr link "key" "" ''
+ if (event.key.toLowerCase() == ${toJSON link.key} && noMod) {
+ event.preventDefault();
+ if (event.shiftKey)
+ window.open(${toJSON link.url});
+ else
+ window.location.href = ${toJSON link.url};
+ return;
+ }
+ ''
+ ) links)}
+ });
+ '';
+
+ styles = ''
+ :root {
+ font-size: 16px;
+ color: #dbe0f9;
+ font-family: JetBrains Mono, monospace;
+ }
+ html, body {
+ background-color: #0f0c19;
+ padding: 0;
+ margin: 0;
+ }
+ body * { box-sizing: border-box; }
+ header {
+ padding: 1rem 2rem;
+ background-color: #000;
+ }
+ .links-container {
+ display: grid;
+ gap: 1rem;
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
+ padding: 0 1rem;
+ width: 100%;
+ margin: 1rem auto;
+ max-width: 1200px
+ }
+ .card {
+ --color-card-accent: #8e7ae3;
+ display: block;
+ padding: 1rem 1.5rem;
+ font-size: 1.2rem;
+ color: var(--color-card-accent);
+ text-decoration: none;
+ border: 2px solid #1a1824;
+ position: relative;
+ }
+ .card::before {
+ content: " ";
+ position: absolute;
+ left: 0; top: 0;
+ width: 4px; height: 100%;
+ background-color: var(--color-card-accent);
+ }
+ .card:hover {
+ border-color: var(--color-card-accent);
+ background-color: rgba(255,255,255,0.05);
+ }
+ .card:focus {
+ border-color: var(--color-card-accent);
+ outline: none;
+ }
+ .card-link {
+ font-size: 0.5em;
+ padding-top: 1em;
+ color: gray;
+ }
+ '';
+
+ headerHTML = ''
+ <header>
+ ${title}
+ </header>
+ '';
+in
+''
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <title>${title}</title>
+ <style>${styles}</style>
+ </head>
+ <body>
+ ${headerHTML}
+ <section class="links-container">
+ ${concatStringsSep "" (map linkHTML links)}
+ </section>
+ <script>${script}</script>
+ </body>
+</html>
+''
diff --git a/modules/dashboard/default.nix b/modules/dashboard/default.nix
new file mode 100644
index 0000000..54db0d9
--- /dev/null
+++ b/modules/dashboard/default.nix
@@ -0,0 +1,40 @@
+{ lib, ... }:
+let
+ settings = import ../../settings.nix { inherit lib; };
+ ports = settings.network.ports;
+in
+{
+ imports = [ ./bacchus-dashboard.service.nix ];
+
+ services.bacchus-dashboard = {
+ enable = true;
+ openFirewall = true;
+ title = "Dashboard";
+ links = [
+ {
+ title = "Jellyfin";
+ key = "j";
+ url = "http://${settings.network.host}:${toString ports.jellyfin}";
+ color = "#AA5CC3";
+ }
+ {
+ title = "Sonarr";
+ key = "s";
+ url = "http://${settings.network.host}:${toString ports.sonarr}";
+ color = "#4c82cf";
+ }
+ {
+ title = "Radarr";
+ key = "r";
+ url = "http://${settings.network.host}:${toString ports.radarr}";
+ color = "#fcbd00";
+ }
+ {
+ title = "Prowlarr";
+ key = "p";
+ url = "http://${settings.network.host}:${toString ports.prowlarr}";
+ color = "#ff9a7e";
+ }
+ ];
+ };
+}