refactor #6

Merged
cnst merged 3 commits from refactor into main 2025-10-14 21:56:13 +02:00
18 changed files with 653 additions and 612 deletions
Showing only changes of commit 63f495fa0d - Show all commits

View File

@@ -1,8 +1,7 @@
{ {
description = "cnix nix"; description = "cnix nix";
outputs = outputs = inputs:
inputs:
inputs.flake-parts.lib.mkFlake {inherit inputs;} { inputs.flake-parts.lib.mkFlake {inherit inputs;} {
systems = [ systems = [
"x86_64-linux" "x86_64-linux"
@@ -17,13 +16,11 @@
./fmt-hooks.nix ./fmt-hooks.nix
]; ];
perSystem = perSystem = {
{
config, config,
pkgs, pkgs,
... ...
}: }: {
{
devShells.default = pkgs.mkShell { devShells.default = pkgs.mkShell {
packages = [ packages = [
pkgs.git pkgs.git

View File

@@ -4,10 +4,8 @@
homeImports, homeImports,
self, self,
... ...
}: }: {
{ flake.nixosConfigurations = let
flake.nixosConfigurations =
let
cLib = import ../lib inputs.nixpkgs.lib; cLib = import ../lib inputs.nixpkgs.lib;
userConfig = "${self}/home"; userConfig = "${self}/home";
systemConfig = "${self}/system"; systemConfig = "${self}/system";
@@ -37,8 +35,7 @@
smodPath smodPath
; ;
}; };
in in {
{
kima = nixosSystem { kima = nixosSystem {
inherit specialArgs; inherit specialArgs;
modules = [ modules = [

View File

@@ -55,160 +55,159 @@
}; };
services = { services = {
# homepage-dashboard = { homepage-dashboard = {
# enable = true; enable = true;
# subdomain = ""; subdomain = "";
# port = "8082"; port = 8082;
# }; };
# n8n = { n8n = {
# enable = true; enable = true;
# subdomain = "n8n"; subdomain = "n8n";
# port = "5678"; port = 5678;
# homepage = { homepage = {
# name = "n8n"; name = "n8n";
# description = "A workflow automation platform"; description = "A workflow automation platform";
# icon = "n8n.svg"; icon = "n8n.svg";
# category = "Services"; category = "Services";
# }; };
# }; };
# bazarr = { bazarr = {
# enable = true; enable = true;
# subdomain = "bazarr"; subdomain = "bazarr";
# port = 6767; port = 6767;
# homepage = { homepage = {
# name = "Bazarr"; name = "Bazarr";
# description = "Subtitle manager"; description = "Subtitle manager";
# icon = "bazarr.svg"; icon = "bazarr.svg";
# category = "Arr"; category = "Arr";
# }; };
# }; };
# prowlarr = { prowlarr = {
# enable = true; enable = true;
# subdomain = "prowlarr"; subdomain = "prowlarr";
# port = 9696; port = 9696;
# homepage = { homepage = {
# name = "prowlarr"; name = "prowlarr";
# description = "PVR indexer"; description = "PVR indexer";
# icon = "prowlarr.svg"; icon = "prowlarr.svg";
# category = "Arr"; category = "Arr";
# }; };
# }; };
# flaresolverr = { flaresolverr = {
# enable = true; enable = true;
# subdomain = "flaresolverr"; subdomain = "flaresolverr";
# port = 8191; port = 8191;
# homepage = { homepage = {
# name = "FlareSolverr"; name = "FlareSolverr";
# description = "Proxy to bypass Cloudflare/DDoS-GUARD protection"; description = "Proxy to bypass Cloudflare/DDoS-GUARD protection";
# icon = "flaresolverr.svg"; icon = "flaresolverr.svg";
# category = "Arr"; category = "Arr";
# }; };
# }; };
# lidarr = { lidarr = {
# enable = true; enable = true;
# subdomain = "lidarr"; subdomain = "lidarr";
# port = 8686; port = 8686;
# homepage = { homepage = {
# name = "Lidarr"; name = "Lidarr";
# description = "Music collection manager"; description = "Music collection manager";
# icon = "lidarr.svg"; icon = "lidarr.svg";
# category = "Arr"; category = "Arr";
# }; };
# }; };
# sonarr = { sonarr = {
# enable = true; enable = true;
# subdomain = "sonarr"; subdomain = "sonarr";
# port = 8989; port = 8989;
# homepage = { homepage = {
# name = "Sonarr"; name = "Sonarr";
# description = "Internet PVR for Usenet and Torrents"; description = "Internet PVR for Usenet and Torrents";
# icon = "sonarr.svg"; icon = "sonarr.svg";
# category = "Arr"; category = "Arr";
# }; };
# }; };
# radarr = { radarr = {
# enable = true; enable = true;
# subdomain = "radarr"; subdomain = "radarr";
# port = 7878; port = 7878;
# homepage = { homepage = {
# name = "Radarr"; name = "Radarr";
# description = "Movie collection manager"; description = "Movie collection manager";
# icon = "radarr.svg"; icon = "radarr.svg";
# category = "Arr"; category = "Arr";
# }; };
# }; };
# jellyseerr = { jellyseerr = {
# enable = true; enable = true;
# subdomain = "jellyseerr"; subdomain = "jellyseerr";
# port = 5055; port = 5055;
# homepage = { homepage = {
# name = "Jellyseerr"; name = "Jellyseerr";
# description = "Media request and discovery manager"; description = "Media request and discovery manager";
# icon = "jellyserr.svg"; icon = "jellyserr.svg";
# category = "Arr"; category = "Arr";
# }; };
# }; };
# jellyfin = { jellyfin = {
# enable = true; enable = true;
# subdomain = "fin"; subdomain = "fin";
# exposure = "tailscale"; exposure = "tailscale";
# port = 8096; port = 8096;
# homepage = { homepage = {
# name = "Jellyfin"; name = "Jellyfin";
# description = "The Free Software Media System"; description = "The Free Software Media System";
# icon = "jellyfin.svg"; icon = "jellyfin.svg";
# category = "Media"; category = "Media";
# }; };
# }; };
# uptime-kuma = { uptime-kuma = {
# enable = true; enable = true;
# subdomain = "uptime"; subdomain = "uptime";
# port = 3001; port = 3001;
# homepage = { homepage = {
# name = "Uptime Kuma"; name = "Uptime Kuma";
# description = "Service monitoring tool"; description = "Service monitoring tool";
# icon = "uptime-kuma.svg"; icon = "uptime-kuma.svg";
# category = "Services"; category = "Services";
# }; };
# }; };
# gitea = { gitea = {
# enable = true; enable = true;
# subdomain = "git"; subdomain = "git";
# exposure = "tunnel"; exposure = "tunnel";
# port = 5003; port = 5003;
# cloudflared = { cloudflared = {
# tunnelId = "33e2fb8e-ecef-4d42-b845-6d15e216e448"; tunnelId = "33e2fb8e-ecef-4d42-b845-6d15e216e448";
# credentialsFile = config.age.secrets.giteaCloudflared.path; credentialsFile = config.age.secrets.giteaCloudflared.path;
# }; };
# homepage = { homepage = {
# name = "Gitea"; name = "Gitea";
# description = "Git with a cup of tea"; description = "Git with a cup of tea";
# icon = "gitea.svg"; icon = "gitea.svg";
# category = "Services"; category = "Services";
# }; };
# }; };
vaultwarden = { vaultwarden = {
enable = true; enable = true;
# subdomain = "vault"; subdomain = "vault";
# exposure = "tunnel"; exposure = "tunnel";
# port = 8222; port = 8222;
# cloudflared = { cloudflared = {
# tunnelId = "fdd98086-6a4c-44f2-bba0-eb86b833cce5"; tunnelId = "fdd98086-6a4c-44f2-bba0-eb86b833cce5";
# credentialsFile = config.age.secrets.vaultwardenCloudflared.path; credentialsFile = config.age.secrets.vaultwardenCloudflared.path;
# }; };
# homepage = { homepage = {
# name = "Vaultwarden"; name = "Vaultwarden";
# description = "Password manager"; description = "Password manager";
# icon = "vaultwarden.svg"; icon = "vaultwarden.svg";
# category = "Services"; category = "Services";
# }; };
}; };
nextcloud = { nextcloud = {
enable = true; enable = true;
subdomain = "cloud"; subdomain = "cloud";
exposure = "local"; exposure = "local";
port = 8182; port = 8182;
adminpassFile = config.age.secrets.nextcloudAdminPass.path;
homepage = { homepage = {
name = "Nextcloud"; name = "Nextcloud";
description = "A safe home for all your data"; description = "A safe home for all your data";
@@ -216,40 +215,40 @@
category = "Services"; category = "Services";
}; };
}; };
# qbittorrent = { qbittorrent = {
# enable = true; enable = true;
# subdomain = "qbt"; subdomain = "qbt";
# port = 8080; port = 8080;
# homepage = { homepage = {
# name = "qBittorrent"; name = "qBittorrent";
# description = "Torrent client"; description = "Torrent client";
# icon = "qbittorrent.svg"; icon = "qbittorrent.svg";
# category = "Downloads"; category = "Downloads";
# }; };
# }; };
# slskd = { slskd = {
# enable = true; enable = true;
# subdomain = "slskd"; subdomain = "slskd";
# port = 5030; port = 5030;
# homepage = { homepage = {
# name = "Soulseek"; name = "Soulseek";
# description = "Web-based Soulseek client"; description = "Web-based Soulseek client";
# icon = "slskd.svg"; icon = "slskd.svg";
# category = "Downloads"; category = "Downloads";
# }; };
# }; };
# pihole = { pihole = {
# enable = true; enable = true;
# subdomain = "pihole"; subdomain = "pihole";
# port = 8053; port = 8053;
# homepage = { homepage = {
# name = "PiHole"; name = "PiHole";
# description = "Adblocking and DNS service"; description = "Adblocking and DNS service";
# icon = "pi-hole.svg"; icon = "pi-hole.svg";
# category = "Services"; category = "Services";
# path = "/admin"; path = "/admin";
# }; };
# }; };
}; };
}; };
} }

5
lib/server/default.nix Normal file
View File

@@ -0,0 +1,5 @@
{
imports = [
./serviceurl
];
}

View File

@@ -0,0 +1,23 @@
{
lib,
config,
...
}: let
mkServiceUrl' = import ./serviceurl.nix {inherit config;};
in {
options.clib = {
server = {
mkServiceUrl = lib.mkOption {
type = lib.types.function;
readOnly = true;
description = "Helper function to generate a service URL.";
};
};
};
config.clib = {
server = {
mkServiceUrl = mkServiceUrl';
};
};
}

View File

@@ -0,0 +1,11 @@
{config}: service: let
mainDomain = config.server.networking.domain;
tailscaleDomain = "ts.${mainDomain}";
domain =
if service.exposure == "tunnel"
then mainDomain
else if service.exposure == "tailscale"
then tailscaleDomain
else (service.domain or mainDomain);
in "${service.subdomain}.${domain}"

View File

@@ -123,31 +123,6 @@
server = { server = {
imports = [ imports = [
./server ./server
./server/infra/authentik
./server/infra/fail2ban
./server/infra/keepalived
./server/infra/podman
./server/infra/postgres
./server/infra/tailscale
./server/infra/traefik
./server/infra/unbound
./server/infra/www
./server/services/bazarr
./server/services/flaresolverr
./server/services/gitea
./server/services/homepage-dashboard
./server/services/jellyfin
./server/services/jellyseerr
./server/services/lidarr
./server/services/n8n
./server/services/nextcloud
./server/services/prowlarr
./server/services/radarr
./server/services/sonarr
./server/services/uptime-kuma
./server/services/vaultwarden
]; ];
}; };
settings = { settings = {

View File

@@ -5,8 +5,7 @@
osConfig, osConfig,
cLib, cLib,
... ...
}: }: let
let
inherit (lib) mkIf mkEnableOption; inherit (lib) mkIf mkEnableOption;
cfg = osConfig.nixos.programs.hyprland; cfg = osConfig.nixos.programs.hyprland;
@@ -15,8 +14,7 @@ let
# #
bg = osConfig.settings.theme.background; bg = osConfig.settings.theme.background;
inherit (cLib.theme.bgs) resolve; inherit (cLib.theme.bgs) resolve;
in in {
{
config = mkIf cfg.enable { config = mkIf cfg.enable {
programs.hyprlock = { programs.hyprlock = {
enable = true; enable = true;

View File

@@ -5,8 +5,7 @@
osConfig, osConfig,
cLib, cLib,
... ...
}: }: let
let
inherit (lib) mkIf; inherit (lib) mkIf;
cfg = osConfig.nixos.programs.hyprland; cfg = osConfig.nixos.programs.hyprland;
@@ -32,8 +31,7 @@ let
bg = bg.primary; bg = bg.primary;
} }
]; ];
in in {
{
config = mkIf cfg.enable { config = mkIf cfg.enable {
services.hyprpaper = { services.hyprpaper = {
enable = true; enable = true;

View File

@@ -1,186 +1,8 @@
{ {self, ...}: {
lib, imports = [
config, "${self}/lib/server"
pkgs, ./options.nix
... ./infra
}: let ./services
hardDrives = [
"/dev/disk/by-label/data"
]; ];
inherit (lib) mkOption types;
cfg = config.server;
ifTheyExist = groups: builtins.filter (group: builtins.hasAttr group config.users.groups) groups;
in {
options.server = {
enable = lib.mkEnableOption "The server services and configuration variables";
email = mkOption {
default = "";
type = types.str;
description = ''
Email name to be used to access the server services via Caddy reverse proxy
'';
};
domain = mkOption {
default = "";
type = types.str;
description = ''
Domain name to be used to access the server services via Caddy reverse proxy
'';
};
ip = lib.mkOption {
type = lib.types.str;
default = "127.0.0.1";
description = "The local IP of the service.";
};
user = lib.mkOption {
default = "share";
type = lib.types.str;
description = ''
User to run the server services as
'';
};
group = lib.mkOption {
default = "share";
type = lib.types.str;
description = ''
Group to run the server services as
'';
};
uid = lib.mkOption {
default = 1000;
type = lib.types.int;
description = ''
UID to run the server services as
'';
};
gid = lib.mkOption {
default = 1000;
type = lib.types.int;
description = ''
GID to run the server services as
'';
};
timeZone = lib.mkOption {
default = "Europe/Stockholm";
type = lib.types.str;
description = ''
Time zone to be used for the server services
'';
};
services = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({name, ...}: {
options = {
enable = lib.mkEnableOption "the service";
subdomain = lib.mkOption {
type = lib.types.str;
default = "";
description = "The subdomain for the service (e.g., 'jellyfin')";
};
exposure = lib.mkOption {
type = lib.types.enum ["local" "tunnel" "tailscale"];
default = "local";
description = "Controls where the service is exposed";
};
port = lib.mkOption {
type = lib.types.int;
default = 80;
description = "The port to host service on.";
};
configDir = lib.mkOption {
type = lib.types.path;
default = "/var/lib/${name}";
description = "Configuration directory for ${name}.";
};
cloudflared = lib.mkOption {
type = lib.types.submodule {
options = {
credentialsFile = lib.mkOption {
type = lib.types.str;
example = "/path/to/cloudflare-credentials.json";
# example = lib.literalExpression ''
# pkgs.writeText "cloudflare-credentials.json" '''
# {"AccountTag":"secret","TunnelSecret":"secret","TunnelID":"secret"}
# '''
# '';
};
tunnelId = lib.mkOption {
type = lib.types.str;
example = "00000000-0000-0000-0000-000000000000";
};
};
};
description = "Cloudflare tunnel configuration for this service.";
};
homepage = lib.mkOption {
type = lib.types.submodule {
options = {
name = lib.mkOption {
type = lib.types.str;
default = "";
description = "Display name on the homepage.";
};
description = lib.mkOption {
type = lib.types.str;
default = "";
description = "A short description for the homepage tile.";
};
icon = lib.mkOption {
type = lib.types.str;
default = "Zervices c00l stuff";
description = "Icon file name for the homepage tile.";
};
category = lib.mkOption {
type = lib.types.str;
default = "";
description = "Homepage category grouping.";
};
path = lib.mkOption {
type = lib.types.str;
default = "";
example = "/admin";
description = "Optional path suffix for homepage links (e.g. /admin).";
};
};
};
description = "Homepage metadata for this service.";
};
};
}));
};
};
config = lib.mkIf cfg.enable {
users = {
groups.${cfg.group} = {
gid = cfg.gid;
};
users.${cfg.user} = {
uid = cfg.uid;
isSystemUser = true;
group = cfg.group;
extraGroups = ifTheyExist [
"audio"
"video"
"docker"
"libvirtd"
"qemu-libvirtd"
"rtkit"
"fail2ban"
"vaultwarden"
"qbittorrent"
"lidarr"
"prowlarr"
"bazarr"
"sonarr"
"radarr"
"media"
"share"
"render"
"input"
"authentik"
"traefik"
];
};
};
};
} }

View File

@@ -0,0 +1,13 @@
{
imports = [
./authentik
./fail2ban
./keepalived
./podman
./postgres
./tailscale
./traefik
./unbound
./www
];
}

View File

@@ -11,23 +11,34 @@
srv = config.server; srv = config.server;
# Generates all Traefik routers from the central service list # Generates all Traefik routers from the central service list
# generateRouters = services:
# lib.mapAttrs' (
# name: service: let
# domain =
# if service.exposure == "tunnel"
# then "cnst.dev"
# else if service.exposure == "tailscale"
# then "ts.cnst.dev"
# else srv.domain;
# in
# lib.nameValuePair "${service.subdomain}" {
# entryPoints = ["websecure"];
# rule = "Host(`${service.subdomain}.${domain}`)";
# service = service.subdomain;
# tls.certResolver = "letsencrypt";
# }
# ) (lib.filterAttrs (name: service: service.enable) services);
generateRouters = services: generateRouters = services:
lib.mapAttrs' ( lib.mapAttrs' (
name: service: let name: service:
domain =
if service.exposure == "tunnel"
then "cnst.dev"
else if service.exposure == "tailscale"
then "ts.cnst.dev"
else srv.domain;
in
lib.nameValuePair "${service.subdomain}" { lib.nameValuePair "${service.subdomain}" {
entryPoints = ["websecure"]; entryPoints = ["websecure"];
rule = "Host(`${service.subdomain}.${domain}`)"; rule = "Host(`${config.clib.server.mkServiceUrl service}`)";
service = service.subdomain; service = service.subdomain;
tls.certResolver = "letsencrypt"; tls.certResolver = "letsencrypt";
} }
) (lib.filterAttrs (name: service: service.enable) services); ) (lib.filterAttrs (_: s: s.enable) services);
# Generates all Traefik backend services # Generates all Traefik backend services
generateServices = services: generateServices = services:

180
modules/server/options.nix Normal file
View File

@@ -0,0 +1,180 @@
{
lib,
config,
...
}: let
inherit (lib) mkOption types;
ifTheyExist = groups: builtins.filter (group: builtins.hasAttr group config.users.groups) groups;
cfg = config.server;
in {
options.server = {
enable = lib.mkEnableOption "The server services and configuration variables";
email = mkOption {
default = "";
type = types.str;
description = ''
Email name to be used to access the server services via Caddy reverse proxy
'';
};
domain = mkOption {
default = "";
type = types.str;
description = ''
Domain name to be used to access the server services via Caddy reverse proxy
'';
};
ip = lib.mkOption {
type = lib.types.str;
default = "127.0.0.1";
description = "The local IP of the service.";
};
user = lib.mkOption {
default = "share";
type = lib.types.str;
description = ''
User to run the server services as
'';
};
group = lib.mkOption {
default = "share";
type = lib.types.str;
description = ''
Group to run the server services as
'';
};
uid = lib.mkOption {
default = 1000;
type = lib.types.int;
description = ''
UID to run the server services as
'';
};
gid = lib.mkOption {
default = 1000;
type = lib.types.int;
description = ''
GID to run the server services as
'';
};
timeZone = lib.mkOption {
default = "Europe/Stockholm";
type = lib.types.str;
description = ''
Time zone to be used for the server services
'';
};
services = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({name, ...}: {
options = {
enable = lib.mkEnableOption "the service";
subdomain = lib.mkOption {
type = lib.types.str;
default = "";
description = "The subdomain for the service (e.g., 'jellyfin')";
};
exposure = lib.mkOption {
type = lib.types.enum ["local" "tunnel" "tailscale"];
default = "local";
description = "Controls where the service is exposed";
};
port = lib.mkOption {
type = lib.types.int;
default = 80;
description = "The port to host service on.";
};
configDir = lib.mkOption {
type = lib.types.path;
default = "/var/lib/${name}";
description = "Configuration directory for ${name}.";
};
cloudflared = lib.mkOption {
type = lib.types.submodule {
options = {
credentialsFile = lib.mkOption {
type = lib.types.str;
example = lib.literalExpression ''
pkgs.writeText "cloudflare-credentials.json" '''
{"AccountTag":"secret","TunnelSecret":"secret","TunnelID":"secret"}
'''
'';
};
tunnelId = lib.mkOption {
type = lib.types.str;
example = "00000000-0000-0000-0000-000000000000";
};
};
};
description = "Cloudflare tunnel configuration for this service.";
};
homepage = lib.mkOption {
type = lib.types.submodule {
options = {
name = lib.mkOption {
type = lib.types.str;
default = "";
description = "Display name on the homepage.";
};
description = lib.mkOption {
type = lib.types.str;
default = "";
description = "A short description for the homepage tile.";
};
icon = lib.mkOption {
type = lib.types.str;
default = "Zervices c00l stuff";
description = "Icon file name for the homepage tile.";
};
category = lib.mkOption {
type = lib.types.str;
default = "";
description = "Homepage category grouping.";
};
path = lib.mkOption {
type = lib.types.str;
default = "";
example = "/admin";
description = "Optional path suffix for homepage links (e.g. /admin).";
};
};
};
description = "Homepage metadata for this service.";
};
};
}));
};
};
config = lib.mkIf cfg.enable {
users = {
groups.${cfg.group} = {
gid = cfg.gid;
};
users.${cfg.user} = {
uid = cfg.uid;
isSystemUser = true;
group = cfg.group;
extraGroups = ifTheyExist [
"audio"
"video"
"docker"
"libvirtd"
"qemu-libvirtd"
"rtkit"
"fail2ban"
"vaultwarden"
"qbittorrent"
"lidarr"
"prowlarr"
"bazarr"
"sonarr"
"radarr"
"media"
"share"
"render"
"input"
"authentik"
"traefik"
];
};
};
};
}

View File

@@ -0,0 +1,18 @@
{
imports = [
./bazarr
./flaresolverr
./gitea
./homepage-dashboard
./jellyfin
./jellyseerr
./lidarr
./n8n
./nextcloud
./prowlarr
./radarr
./sonarr
./uptime-kuma
./vaultwarden
];
}

View File

@@ -1,4 +1,3 @@
# "inspired" by @jtojnar <3
{ {
config, config,
lib, lib,
@@ -7,21 +6,23 @@
}: let }: let
unit = "gitea"; unit = "gitea";
cfg = config.server.services.${unit}; cfg = config.server.services.${unit};
domain = "${cfg.subdomain}.${config.server.infra.www.url}";
in { in {
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
age.secrets = { age.secrets.giteaCloudflared.file = "${self}/secrets/giteaCloudflared.age";
giteaCloudflared.file = "${self}/secrets/giteaCloudflared.age";
};
server.infra = { server.infra = {
fail2ban = { fail2ban.jails.unit = {
jails = { serviceName = "${unit}";
gitea = { failRegex = ''
serviceName = "gitea"; .*(Failed authentication attempt|invalid credentials|Attempted access of unknown user).*
failRegex = ''.*(Failed authentication attempt|invalid credentials|Attempted access of unknown user).* from <HOST>''; from <HOST>
}; '';
};
}; };
postgresql.databases = [
{database = unit;}
];
}; };
services = { services = {
@@ -30,11 +31,11 @@ in {
tunnels.${cfg.cloudflared.tunnelId} = { tunnels.${cfg.cloudflared.tunnelId} = {
credentialsFile = cfg.cloudflared.credentialsFile; credentialsFile = cfg.cloudflared.credentialsFile;
default = "http_status:404"; default = "http_status:404";
ingress."${cfg.url}".service = "http://localhost:${toString cfg.port}"; ingress."${domain}".service = "http://localhost:${toString cfg.port}";
}; };
}; };
${unit} = { gitea = {
enable = true; enable = true;
appName = "cnix code forge"; appName = "cnix code forge";
@@ -46,63 +47,51 @@ in {
createDatabase = false; createDatabase = false;
}; };
lfs = { lfs.enable = true;
enable = true;
};
settings = { settings = {
cors = { cors = {
ENABLED = true; ENABLED = true;
SCHEME = "https"; SCHEME = "https";
ALLOW_DOMAIN = cfg.url; ALLOW_DOMAIN = domain;
};
log = {
MODE = "console";
}; };
log.MODE = "console";
mailer = { mailer = {
ENABLED = false; ENABLED = false;
MAILER_TYPE = "sendmail"; MAILER_TYPE = "sendmail";
FROM = "noreply+adam@cnst.dev"; FROM = "noreply+adam@cnst.dev";
SENDMAIL_PATH = "/run/wrappers/bin/sendmail"; SENDMAIL_PATH = "/run/wrappers/bin/sendmail";
}; };
picture = {
DISABLE_GRAVATAR = true; picture.DISABLE_GRAVATAR = true;
};
repository = { repository = {
DEFAULT_BRANCH = "main"; DEFAULT_BRANCH = "main";
DEFAULT_REPO_UNITS = "repo.code,repo.issues,repo.pulls"; DEFAULT_REPO_UNITS = "repo.code,repo.issues,repo.pulls";
DISABLE_DOWNLOAD_SOURCE_ARCHIVES = true; DISABLE_DOWNLOAD_SOURCE_ARCHIVES = true;
}; };
indexer = {
REPO_INDEXER_ENABLED = true; indexer.REPO_INDEXER_ENABLED = true;
};
oauth2_client = { oauth2_client = {
ENABLE_AUTO_REGISTRATION = true; ENABLE_AUTO_REGISTRATION = true;
ACCOUNT_LINKING = "auto"; ACCOUNT_LINKING = "auto";
}; };
server = { server = {
DOMAIN = cfg.url; DOMAIN = domain;
LANDING_PAGE = "explore"; LANDING_PAGE = "explore";
HTTP_PORT = cfg.port; HTTP_PORT = cfg.port;
ROOT_URL = "https://${cfg.url}/"; ROOT_URL = "https://${domain}/";
};
security = {
DISABLE_GIT_HOOKS = false;
};
service = {
DISABLE_REGISTRATION = true;
};
session = {
COOKIE_SECURE = true;
};
};
};
}; };
server.infra.postgresql.databases = [ security.DISABLE_GIT_HOOKS = false;
{ service.DISABLE_REGISTRATION = true;
database = "gitea"; session.COOKIE_SECURE = true;
} };
]; };
};
}; };
} }

View File

@@ -5,7 +5,7 @@
... ...
}: let }: let
unit = "homepage-dashboard"; unit = "homepage-dashboard";
cfg = config.server.services.homepage-dashboard; cfg = config.server.services.${unit};
srv = config.server; srv = config.server;
in { in {
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
@@ -14,11 +14,14 @@ in {
file = "${self}/secrets/homepageEnvironment.age"; file = "${self}/secrets/homepageEnvironment.age";
}; };
}; };
services = { services = {
glances.enable = true; glances.enable = true;
${unit} = { ${unit} = {
enable = true; enable = true;
environmentFile = config.age.secrets.homepageEnvironment.path; environmentFile = config.age.secrets.homepageEnvironment.path;
settings = { settings = {
layout = [ layout = [
{ {
@@ -53,10 +56,12 @@ in {
}; };
} }
]; ];
headerStyle = "clean"; headerStyle = "clean";
statusStyle = "dot"; statusStyle = "dot";
hideVersion = "true"; hideVersion = "true";
}; };
widgets = [ widgets = [
{ {
openmeteo = { openmeteo = {
@@ -77,6 +82,7 @@ in {
}; };
} }
]; ];
services = let services = let
homepageCategories = [ homepageCategories = [
"Arr" "Arr"
@@ -84,21 +90,30 @@ in {
"Downloads" "Downloads"
"Services" "Services"
]; ];
hl = config.server.services;
homepageServices = x: (lib.attrsets.filterAttrs ( allServices = srv.services;
_name: value: value ? homepage && value.homepage.category == x
homepageServicesFor = category:
lib.filterAttrs
(
name: value:
name
!= unit
&& value ? homepage
&& value.homepage.category == category
) )
srv.services); allServices;
in in
lib.lists.forEach homepageCategories (cat: { lib.lists.forEach homepageCategories (cat: {
"${cat}" = "${cat}" =
lib.lists.forEach (lib.attrsets.mapAttrsToList (name: _value: name) (homepageServices "${cat}")) lib.lists.forEach
(lib.attrsets.mapAttrsToList (name: _value: name) (homepageServicesFor cat))
(x: { (x: {
"${hl.${x}.homepage.name}" = { "${allServices.${x}.homepage.name}" = {
icon = hl.${x}.homepage.icon; icon = allServices.${x}.homepage.icon;
description = hl.${x}.homepage.description; description = allServices.${x}.homepage.description;
href = "https://${hl.${x}.url}"; href = "https://${allServices.${x}.url}";
siteMonitor = "https://${hl.${x}.url}"; siteMonitor = "https://${allServices.${x}.url}";
}; };
}); });
}) })

View File

@@ -15,9 +15,7 @@ in {
nextcloudCloudflared.file = "${self}/secrets/nextcloudCloudflared.age"; nextcloudCloudflared.file = "${self}/secrets/nextcloudCloudflared.age";
}; };
server.infra.fail2ban = lib.mkIf srv.infra.fail2ban.enable { server.infra.fail2ban.jails.nextcloud = {
jails = {
nextcloud = {
serviceName = "${unit}"; serviceName = "${unit}";
_groupsre = ''(?:(?:,?\s*"\w+":(?:"[^"]+"|\w+))*)''; _groupsre = ''(?:(?:,?\s*"\w+":(?:"[^"]+"|\w+))*)'';
failRegex = '' failRegex = ''
@@ -27,8 +25,6 @@ in {
''; '';
datePattern = '',?\s*"time"\s*:\s*"%%Y-%%m-%%d[T ]%%H:%%M:%%S(%%z)?"''; datePattern = '',?\s*"time"\s*:\s*"%%Y-%%m-%%d[T ]%%H:%%M:%%S(%%z)?"'';
}; };
};
};
services = { services = {
${unit} = { ${unit} = {

View File

@@ -7,7 +7,7 @@
}: let }: let
unit = "vaultwarden"; unit = "vaultwarden";
cfg = config.server.services.${unit}; cfg = config.server.services.${unit};
www = config.server.infra.www; domain = "${cfg.subdomain}.${config.server.infra.www.url}";
in { in {
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
age.secrets = { age.secrets = {
@@ -15,16 +15,10 @@ in {
vaultwardenEnvironment.file = "${self}/secrets/vaultwardenEnvironment.age"; vaultwardenEnvironment.file = "${self}/secrets/vaultwardenEnvironment.age";
}; };
server.infra = { server.infra.fail2ban.jails.${unit} = {
fail2ban = {
jails = {
vaultwarden = {
serviceName = "${unit}"; serviceName = "${unit}";
failRegex = ''^.*?Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$''; failRegex = ''^.*?Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$'';
}; };
};
};
};
services = { services = {
cloudflared = { cloudflared = {
@@ -32,7 +26,7 @@ in {
tunnels.${cfg.cloudflared.tunnelId} = { tunnels.${cfg.cloudflared.tunnelId} = {
credentialsFile = cfg.cloudflared.credentialsFile; credentialsFile = cfg.cloudflared.credentialsFile;
default = "http_status:404"; default = "http_status:404";
ingress."${cfg.url}".service = "http://localhost:${toString cfg.port}"; ingress."${domain}".service = "http://localhost:${toString cfg.port}";
}; };
}; };
@@ -43,7 +37,7 @@ in {
backupDir = "/var/backup/vaultwarden"; backupDir = "/var/backup/vaultwarden";
config = { config = {
DOMAIN = "https://vault.${www.url}"; DOMAIN = "https://${domain}";
SIGNUPS_ALLOWED = false; SIGNUPS_ALLOWED = false;
ROCKET_ADDRESS = "127.0.0.1"; ROCKET_ADDRESS = "127.0.0.1";
ROCKET_PORT = cfg.port; ROCKET_PORT = cfg.port;