aboutsummaryrefslogtreecommitdiff
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
parent279241632b566fd26011409672277ca02ed697d1 (diff)
downloadhomeserver-nixos-config-2a73a5899124fa15c3962b9bd385ebd415a0ccf9.tar.gz
homeserver-nixos-config-2a73a5899124fa15c3962b9bd385ebd415a0ccf9.zip
Add dashboard for linking services
-rw-r--r--.gitignore3
-rwxr-xr-xbuild.sh18
-rw-r--r--configuration.nix7
-rw-r--r--modules/dashboard/bacchus-dashboard.service.nix46
-rw-r--r--modules/dashboard/dashboard-template.nix114
-rw-r--r--modules/dashboard/default.nix40
-rw-r--r--notes.org5
-rw-r--r--scripts/init.sh8
8 files changed, 222 insertions, 19 deletions
diff --git a/.gitignore b/.gitignore
index febbc25..66b3819 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-*.private.nix
+*.private.*
+*.ignore.*
diff --git a/build.sh b/build.sh
index 4ba3b7b..cd67bbe 100755
--- a/build.sh
+++ b/build.sh
@@ -5,16 +5,16 @@ getconfig() {
nix eval --impure --expr "(import ./settings.nix { lib = (import <nixpkgs> {}).lib; })$@" | jq -r
}
-SSH_HOST="$(getconfig .network.host)"
+HOME_HOST="$(getconfig .network.host)"
SSH_PORT="$(getconfig .network.ports.ssh)"
-SSH_TARGET="bacchus@$SSH_HOST"
+SSH_TARGET="bacchus@$HOME_HOST"
SCRIPT_PATH="$(readlink -f "$0")"
run_ssh() { ssh -t "$SSH_TARGET" -p "$SSH_PORT" "$@"; }
copy() { rsync -ravh --delete --exclude '.git' -e "ssh -p $SSH_PORT" "$@"; }
waittostart() {
echo "Waiting...";
- until timeout 2 nc -z "$SSH_HOST" "$SSH_PORT" 2> /dev/null; do
+ until timeout 2 nc -z "$HOME_HOST" "$SSH_PORT" 2> /dev/null; do
echo -n '.';
sleep 1
done;
@@ -39,6 +39,13 @@ chaincmds() {
done
}
+gen_test_dash() {
+ nix eval --impure \
+ --expr 'import ./modules/dashboard/dashboard-template.nix { title = "Dashboard"; links = [{ title = "Google"; url = "https://google.com"; key = "g"; } { title = "DuckDuckGo"; url = "https://duckduckgo.com"; key = "d"; color = "#4c82cf"; }]; }' \
+ | jq -r > index.ignore.html
+}
+open_test_dash() { brave "file://$PWD/index.ignore.html"; }
+
cmd="$1"; shift;
case "$cmd" in
sync) config_sync ;;
@@ -49,7 +56,10 @@ case "$cmd" in
clean) run_ssh sudo nix-collect-garbage -d ;;
top) run_ssh btm ;;
fs) run_ssh lf ;;
+ test-dash) gen_test_dash ;;
+ open-test-dash) open_test_dash ;;
+ dash) brave "http://$HOME_HOST" ;;
_) chaincmds "$@" ;;
- *) echo "Invalid cmd: $1" && exit 1 ;;
+ *) echo "Invalid cmd: $cmd" && exit 1 ;;
esac
diff --git a/configuration.nix b/configuration.nix
index 9d1b067..45b34ab 100644
--- a/configuration.nix
+++ b/configuration.nix
@@ -2,14 +2,15 @@
# your system. Help is available in the configuration.nix(5) man page
# and in the NixOS manual (accessible by running ‘nixos-help’).
-{ config, pkgs, ... }:
+{ pkgs, ... }:
{
imports = [
./modules/hardware.nix
./modules/users.nix
- ./modules/network/default.nix
- ./modules/media/default.nix
+ ./modules/network
+ ./modules/media
+ ./modules/dashboard
];
nixpkgs.config.allowUnfree = true;
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";
+ }
+ ];
+ };
+}
diff --git a/notes.org b/notes.org
index 4086974..7efeff1 100644
--- a/notes.org
+++ b/notes.org
@@ -1,14 +1,13 @@
** Current
-- [ ] Home dashboard ([[https://github.com/tariqbuilds/linux-dash]])
-- [ ] Make autoconnect via networkmanager work consistently (higher frequency)
- [ ] DNS server (dnsmasq, bind, octodns, nsd, [[https://github.com/nix-community/dns.nix]])
+- [ ] Syncthing
** Later
-- [ ] Syncthing
- [ ] Jellyfin library for photos (shared/private)
- [ ] NAS
- [ ] [[https://ntfy.sh]]
- [ ] Think of where to place the server
+- [ ] Make autoconnect via networkmanager work consistently (higher frequency)
** Maybe
- [ ] Git bare repos (pass/notes)
diff --git a/scripts/init.sh b/scripts/init.sh
deleted file mode 100644
index a0e1eab..0000000
--- a/scripts/init.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env sh
-set -euo pipefail
-
-sudo chown -R bacchus:multimedia /media
-sudo chown -R sonarr:multimedia /media/tv
-sudo chown -R radarr:multimedia /media/movies
-sudo chown -R transmission:multimedia /media/_downloads
-