Rename web-service dir

This commit is contained in:
SebastianStork 2025-10-11 16:12:50 +02:00
parent b2680db359
commit 14fa3f89c1
18 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,58 @@
{ config, lib, ... }:
let
cfg = config.custom.services.actualbudget;
inherit (config.services.actual.settings) dataDir;
in
{
options.custom.services.actualbudget = {
enable = lib.mkEnableOption "";
domain = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "";
};
port = lib.mkOption {
type = lib.types.port;
default = 5006;
};
doBackups = lib.mkEnableOption "";
};
config = lib.mkIf cfg.enable {
meta = {
domains.list = [ cfg.domain ];
ports.tcp.list = [ cfg.port ];
};
users = {
users.actual = {
isSystemUser = true;
group = config.users.groups.actual.name;
};
groups.actual = { };
};
systemd.services.actual.serviceConfig = {
DynamicUser = lib.mkForce false;
PrivateTmp = true;
RemoveIPC = true;
};
services.actual = {
enable = true;
settings = {
hostname = "localhost";
inherit (cfg) port;
};
};
custom = {
services.resticBackups.actual = lib.mkIf cfg.doBackups {
conflictingService = "actual.service";
paths = [ dataDir ];
};
persist.directories = [ dataDir ];
};
};
}

View file

@ -0,0 +1,172 @@
{ config, lib, ... }:
let
cfg = config.custom.services.alloy;
in
{
options.custom.services.alloy = {
enable = lib.mkEnableOption "";
domain = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "";
};
port = lib.mkOption {
type = lib.types.port;
default = 12345;
};
metricsEndpoint = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "https://metrics.${config.custom.services.tailscale.domain}/prometheus/api/v1/write";
};
logsEndpoint = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "https://logs.${config.custom.services.tailscale.domain}/insert/loki/api/v1/push";
};
collect = {
metrics = {
system = lib.mkEnableOption "" // {
default = true;
};
victorialogs = lib.mkEnableOption "" // {
default = config.services.victorialogs.enable;
};
caddy = lib.mkEnableOption "" // {
default = config.services.caddy.enable;
};
crowdsec = lib.mkEnableOption "" // {
default = config.services.crowdsec.enable;
};
};
logs.openssh = lib.mkEnableOption "" // {
default = config.services.openssh.enable;
};
};
};
config = lib.mkIf cfg.enable {
assertions =
let
metricsAssertions =
cfg.collect.metrics
|> lib.attrNames
|> lib.filter (name: name != "system")
|> lib.map (name: {
assertion = cfg.collect.metrics.${name} -> config.services.${name}.enable;
message = "Collecting ${name} metrics requires the ${name} service to be enabled.";
});
logsAssertions =
cfg.collect.logs
|> lib.attrNames
|> lib.map (name: {
assertion = cfg.collect.logs.${name} -> config.services.${name}.enable;
message = "Collecting ${name} logs requires the ${name} service to be enabled.";
});
in
metricsAssertions ++ logsAssertions;
meta = {
domains.list = [ cfg.domain ];
ports.tcp.list = [ cfg.port ];
};
services.alloy = {
enable = true;
extraFlags = [
"--server.http.listen-addr=localhost:${toString cfg.port}"
"--disable-reporting"
];
};
environment.etc =
let
isTrue = x: x;
anyIsTrue = attrs: attrs |> lib.attrValues |> lib.any isTrue;
in
{
"alloy/metrics-endpoint.alloy" = {
enable = cfg.collect.metrics |> anyIsTrue;
text = ''
prometheus.remote_write "default" {
endpoint {
url = "${cfg.metricsEndpoint}"
}
}
'';
};
"alloy/logs-endpoint.alloy" = {
enable = cfg.collect.logs |> anyIsTrue;
text = ''
loki.write "default" {
endpoint {
url = "${cfg.logsEndpoint}"
}
}
'';
};
"alloy/system-metrics.alloy" = {
enable = cfg.collect.metrics.system;
text = ''
prometheus.exporter.unix "default" {
enable_collectors = ["systemd"]
}
prometheus.scrape "node_exporter" {
targets = prometheus.exporter.unix.default.targets
forward_to = [prometheus.remote_write.default.receiver]
scrape_interval = "15s"
}
'';
};
"alloy/victorialogs-metrics.alloy" = {
enable = cfg.collect.metrics.victorialogs;
text = ''
prometheus.scrape "victorialogs" {
targets = [{
__address__ = "localhost:${toString config.custom.services.victorialogs.port}",
job = "victorialogs",
instance = constants.hostname,
}]
forward_to = [prometheus.remote_write.default.receiver]
scrape_interval = "15s"
}
'';
};
"alloy/caddy-metrics.alloy" = {
enable = cfg.collect.metrics.caddy;
text = ''
prometheus.scrape "caddy" {
targets = [{
__address__ = "localhost:${toString config.custom.services.caddy.metricsPort}",
job = "caddy",
instance = constants.hostname,
}]
forward_to = [prometheus.remote_write.default.receiver]
scrape_interval = "15s"
}
'';
};
"alloy/crowdsec-metrics.alloy" = {
enable = cfg.collect.metrics.crowdsec;
text = ''
prometheus.scrape "crowdsec" {
targets = [{
__address__ = "localhost:${toString config.custom.services.crowdsec.prometheusPort}",
job = "crowdsec",
instance = constants.hostname,
}]
forward_to = [prometheus.remote_write.default.receiver]
scrape_interval = "15s"
}
'';
};
"alloy/sshd-logs.alloy" = {
enable = cfg.collect.logs.openssh;
text = ''
loki.source.journal "sshd" {
matches = "_SYSTEMD_UNIT=sshd.service"
forward_to = [loki.write.default.receiver]
}
'';
};
};
};
}

View file

@ -0,0 +1,54 @@
{
config,
modulesPath,
inputs,
lib,
...
}:
let
cfg = config.custom.services.filebrowser;
dataDir = "/var/lib/filebrowser";
in
{
imports = [ "${inputs.nixpkgs-unstable}/nixos/modules/services/web-apps/filebrowser.nix" ];
options.custom.services.filebrowser = {
enable = lib.mkEnableOption "";
domain = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "";
};
port = lib.mkOption {
type = lib.types.port;
default = 44093;
};
doBackups = lib.mkEnableOption "";
};
config = lib.mkIf cfg.enable {
warnings = lib.optional (lib.pathExists "${modulesPath}/services/web-apps/filebrowser.nix") "TODO: Use filebrowser module from stable nixpkgs";
meta = {
domains.list = [ cfg.domain ];
ports.tcp.list = [ cfg.port ];
};
services.filebrowser = {
enable = true;
settings = {
inherit (cfg) port;
noauth = true;
};
};
custom = {
services.resticBackups.filebrowser = lib.mkIf cfg.doBackups {
conflictingService = "filebrowser.service";
paths = [ dataDir ];
};
persist.directories = [ dataDir ];
};
};
}

View file

@ -0,0 +1,142 @@
{
config,
pkgs,
pkgs-unstable,
lib,
...
}:
let
cfg = config.custom.services.forgejo;
pinnedVersion = "12.0.4";
in
{
options.custom.services.forgejo = {
enable = lib.mkEnableOption "";
domain = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "";
};
port = lib.mkOption {
type = lib.types.port;
default = 3003;
};
doBackups = lib.mkEnableOption "";
};
config = lib.mkIf cfg.enable {
warnings = lib.optional (
(lib.versionAtLeast pkgs.forgejo.version pinnedVersion) && (pkgs.forgejo.version != pinnedVersion)
) "TODO: Use forgejo package from stable nixpkgs";
meta = {
domains.list = [ cfg.domain ];
ports.tcp.list = [ cfg.port ];
};
sops.secrets."forgejo/admin-password" = {
owner = config.users.users.git.name;
restartUnits = [ "forgejo.service" ];
};
users = {
users.git = {
isSystemUser = true;
useDefaultShell = true;
group = config.users.groups.git.name;
home = config.services.forgejo.stateDir;
};
groups.git = { };
};
services.forgejo = {
enable = true;
package = pkgs-unstable.forgejo.overrideAttrs {
version = pinnedVersion;
src = pkgs.fetchFromGitea {
domain = "codeberg.org";
owner = "forgejo";
repo = "forgejo";
rev = "refs/tags/v${pinnedVersion}";
hash = "sha256-g6PNJYiGR7tUpurVL1gvGzJzDoMCLmkGiLLsSZfkbYQ=";
};
};
user = "git";
group = "git";
lfs.enable = true;
settings = {
server = {
DOMAIN = cfg.domain;
ROOT_URL = "https://${cfg.domain}/";
HTTP_PORT = cfg.port;
LANDING_PAGE = "/SebastianStork";
};
service.DISABLE_REGISTRATION = true;
session.PROVIDER = "db";
mirror.DEFAULT_INTERVAL = "1h";
other = {
SHOW_FOOTER_VERSION = false;
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false;
SHOW_FOOTER_POWERED_BY = false;
};
cron.ENABLED = true;
"cron.git_gc_repos".ENABLED = true;
repository.ENABLE_PUSH_CREATE_USER = true;
# https://forgejo.org/docs/latest/admin/recommendations
database.SQLITE_JOURNAL_MODE = "WAL";
cache = {
ADAPTER = "twoqueue";
HOST = lib.strings.toJSON {
size = 100;
recent_ratio = 0.25;
ghost_ratio = 0.5;
};
};
"repository.signing".DEFAULT_TRUST_MODEL = "committer";
security.LOGIN_REMEMBER_DAYS = 365;
};
};
systemd.services.forgejo.preStart =
let
userCmd = "${lib.getExe config.services.forgejo.package} admin user";
in
''
username="SebastianStork"
password="$(cat ${config.sops.secrets."forgejo/admin-password".path})"
create_user() {
${userCmd} create \
--username "$username" \
--password "$password" \
--email "sebastian.stork@pm.me" \
--admin
}
reset_password() {
${userCmd} change-password \
--username "$username" \
--password "$password" \
--must-change-password=false
}
if ! create_user; then
reset_password
fi
'';
custom = {
services.resticBackups.forgejo = lib.mkIf cfg.doBackups {
conflictingService = "forgejo.service";
paths = [ config.services.forgejo.stateDir ];
};
persist.directories = [ config.services.forgejo.stateDir ];
};
};
}

View file

@ -0,0 +1,31 @@
{ config, lib, ... }:
let
cfg = config.custom.services.forgejo.ssh;
in
{
options.custom.services.forgejo.ssh = {
enable = lib.mkEnableOption "";
port = lib.mkOption {
type = lib.types.port;
default = 22;
};
};
config = lib.mkIf cfg.enable {
meta.ports.tcp.list = [ cfg.port ];
services.forgejo.settings.server.SSH_PORT = cfg.port;
services.openssh = {
enable = true;
ports = lib.mkForce [ cfg.port ];
authorizedKeysFiles = lib.mkForce [ "${config.services.forgejo.stateDir}/.ssh/authorized_keys" ];
settings = {
PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
PermitRootLogin = "no";
AllowUsers = [ config.services.forgejo.user ];
};
};
};
}

View file

@ -0,0 +1,211 @@
{
config,
self,
pkgs-unstable,
lib,
...
}:
let
cfg = config.custom.services.gatus;
dataDir = "/var/lib/gatus";
in
{
options.custom.services.gatus = {
enable = lib.mkEnableOption "";
domain = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "";
};
port = lib.mkOption {
type = lib.types.port;
default = 8080;
};
generateDefaultEndpoints = lib.mkEnableOption "";
endpoints = lib.mkOption {
type = lib.types.attrsOf (
lib.types.submodule (
{ name, ... }:
{
options = {
name = lib.mkOption {
type = lib.types.nonEmptyStr;
default = name;
};
group = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "";
};
protocol = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "https";
};
domain = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "";
};
path = lib.mkOption {
type = lib.types.str;
default = "";
};
interval = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "30s";
};
extraConditions = lib.mkOption {
type = lib.types.listOf lib.types.nonEmptyStr;
default = [ ];
};
enableAlerts = lib.mkEnableOption "" // {
default = true;
};
};
}
)
);
default = { };
};
};
config = lib.mkIf cfg.enable {
warnings = lib.optional (lib.versionAtLeast lib.version "25.11") "TODO: Use gatus package from stable nixpkgs";
meta = {
domains.list = [ cfg.domain ];
ports.tcp.list = [ cfg.port ];
};
sops = {
secrets."healthchecks/ping-key" = { };
templates."gatus.env" = {
content = "HEALTHCHECKS_PING_KEY=${config.sops.placeholder."healthchecks/ping-key"}";
owner = config.users.users.gatus.name;
restartUnits = [ "gatus.service" ];
};
};
users = {
users.gatus = {
isSystemUser = true;
group = config.users.groups.gatus.name;
};
groups.gatus = { };
};
systemd.services.gatus.serviceConfig = {
DynamicUser = lib.mkForce false;
ProtectSystem = "strict";
ProtectHome = "read-only";
PrivateTmp = true;
RemoveIPC = true;
};
custom.services.gatus.endpoints =
let
getSubdomain = domain: domain |> lib.splitString "." |> lib.head;
defaultEndpoints =
self.nixosConfigurations
|> lib.mapAttrs (_: value: value.config.meta.domains.list)
|> lib.concatMapAttrs (
hostName: domains:
domains
|> lib.filter (domain: domain != cfg.domain)
|> lib.map (
domain:
lib.nameValuePair (getSubdomain domain) {
inherit domain;
group = hostName;
}
)
|> lib.listToAttrs
);
in
lib.mkIf cfg.generateDefaultEndpoints (
defaultEndpoints
// {
"healthchecks.io" = {
group = "external";
domain = "hc-ping.com";
path = "/\${HEALTHCHECKS_PING_KEY}/${config.networking.hostName}-gatus-uptime?create=1";
interval = "2h";
};
}
);
services.gatus = {
enable = true;
package = pkgs-unstable.gatus; # Unstable for the new UI
environmentFile = config.sops.templates."gatus.env".path;
settings = {
web = {
address = "localhost";
port = cfg.port;
};
storage = {
type = "sqlite";
path = "${dataDir}/data.db";
};
connectivity.checker.target = "1.1.1.1:53"; # Cloudflare DNS
alerting.ntfy = {
topic = "uptime";
url = "https://alerts.${config.custom.services.tailscale.domain}";
click = "https://${cfg.domain}";
default-alert = {
enable = true;
failure-threshold = 8;
success-threshold = 4;
send-on-resolved = true;
};
overrides = [
{
group = "Monitoring";
topic = "stork-atlas";
url = "https://ntfy.sh";
default-alert = {
failure-threshold = 4;
success-threshold = 2;
};
}
];
};
ui.default-sort-by = "group";
maintenance = {
start = "03:00";
duration = "1h";
timezone = "Europe/Berlin";
};
endpoints =
let
mkEndpoint = value: {
inherit (value) name group interval;
url = "${value.protocol}://${value.domain}${value.path}";
alerts = lib.mkIf value.enableAlerts [ { type = "ntfy"; } ];
ssh = lib.mkIf (value.protocol == "ssh") {
username = "";
password = "";
};
conditions = lib.concatLists [
value.extraConditions
(lib.optional (lib.elem value.protocol [
"http"
"https"
]) "[STATUS] == 200")
(lib.optional (lib.elem value.protocol [
"tcp"
"ssh"
"icmp"
]) "[CONNECTED] == true")
];
};
in
cfg.endpoints |> lib.attrValues |> lib.map (entry: mkEndpoint entry);
};
};
systemd.services.gatus.environment.GATUS_DELAY_START_SECONDS = "5";
custom.persist.directories = [ dataDir ];
};
}

View file

@ -0,0 +1,153 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.custom.services.grafana;
in
{
options.custom.services.grafana = {
enable = lib.mkEnableOption "";
domain = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "";
};
port = lib.mkOption {
type = lib.types.port;
default = 3000;
};
datasources = {
prometheus = {
enable = lib.mkEnableOption "";
url = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "https://metrics.${config.custom.services.tailscale.domain}";
};
};
victoriametrics = {
enable = lib.mkEnableOption "";
url = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "https://metrics.${config.custom.services.tailscale.domain}";
};
};
victorialogs = {
enable = lib.mkEnableOption "";
url = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "https://logs.${config.custom.services.tailscale.domain}";
};
};
};
dashboards = {
nodeExporter = lib.mkEnableOption "";
victoriametrics = lib.mkEnableOption "";
victorialogs = lib.mkEnableOption "";
};
};
config = lib.mkIf cfg.enable {
meta = {
domains.list = [ cfg.domain ];
ports.tcp.list = [ cfg.port ];
};
sops.secrets."grafana/admin-password" = {
owner = config.users.users.grafana.name;
restartUnits = [ "grafana.service" ];
};
services.grafana = {
enable = true;
settings = {
server = {
inherit (cfg) domain;
http_port = cfg.port;
enforce_domain = true;
enable_gzip = true;
};
security.admin_password = "$__file{${config.sops.secrets."grafana/admin-password".path}}";
users.default_theme = "system";
analytics.reporting_enabled = false;
};
provision = {
enable = true;
dashboards.settings = {
providers = lib.singleton {
name = "Dashboards";
options.path = "/etc/grafana-dashboards";
};
};
datasources.settings = {
prune = true;
datasources =
(lib.optional cfg.datasources.prometheus.enable {
name = "Prometheus";
type = "prometheus";
inherit (cfg.datasources.prometheus) url;
isDefault = true;
jsonData = {
prometheusType = "Prometheus";
prometheusVersion = "2.50.0";
};
})
++ (lib.optional cfg.datasources.victoriametrics.enable {
name = "VictoriaMetrics";
type = "victoriametrics-metrics-datasource";
inherit (cfg.datasources.victoriametrics) url;
isDefault = false;
})
++ (lib.optional cfg.datasources.victorialogs.enable {
name = "VictoriaLogs";
type = "victoriametrics-logs-datasource";
inherit (cfg.datasources.victorialogs) url;
isDefault = false;
});
};
};
declarativePlugins =
let
plugins = pkgs.grafanaPlugins;
in
[
(lib.optional cfg.datasources.victoriametrics.enable plugins.victoriametrics-metrics-datasource)
(lib.optional cfg.datasources.victorialogs.enable plugins.victoriametrics-logs-datasource)
]
|> lib.concatLists;
};
environment.etc = {
"grafana-dashboards/node-exporter-full.json" = {
enable = cfg.dashboards.nodeExporter;
source = pkgs.fetchurl {
name = "node-exporter-full.json";
url = "https://grafana.com/api/dashboards/1860/revisions/41/download";
hash = "sha256-EywgxEayjwNIGDvSmA/S56Ld49qrTSbIYFpeEXBJlTs=";
};
};
"grafana-dashboards/victoriametrics-single-node.json" = {
enable = cfg.dashboards.victoriametrics;
source = pkgs.fetchurl {
name = "victoriametrics-single-node.json";
url = "https://grafana.com/api/dashboards/10229/revisions/41/download";
hash = "sha256-mwtah8A2w81WZjf5bUXoTJfS1R9UX+tua2PiDrBKJCQ=";
};
};
"grafana-dashboards/victorialogs-single-node.json" = {
enable = cfg.dashboards.victorialogs;
source = pkgs.fetchurl {
name = "victorialogs-single-node.json";
url = "https://grafana.com/api/dashboards/22084/revisions/8/download";
hash = "sha256-/a3Rbp/6oyiLBnQtGupyFZW+fIHQfkyKRRTyfofxVTM=";
};
};
};
};
}

View file

@ -0,0 +1,87 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.custom.services.hedgedoc;
dataDir = "/var/lib/hedgedoc";
in
{
options.custom.services.hedgedoc = {
enable = lib.mkEnableOption "";
domain = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "";
};
port = lib.mkOption {
type = lib.types.port;
default = 3000;
};
doBackups = lib.mkEnableOption "";
};
config = lib.mkIf cfg.enable {
meta = {
domains.list = [ cfg.domain ];
ports.tcp.list = [ cfg.port ];
};
sops = {
secrets."hedgedoc/gitlab-auth-secret" = { };
templates."hedgedoc/environment" = {
owner = config.users.users.hedgedoc.name;
content = "GITLAB_CLIENTSECRET=${config.sops.placeholder."hedgedoc/gitlab-auth-secret"}";
restartUnits = [ "hedgedoc.service" ];
};
};
services.hedgedoc = {
enable = true;
environmentFile = config.sops.templates."hedgedoc/environment".path;
settings = {
inherit (cfg) domain port;
protocolUseSSL = true;
allowAnonymous = false;
email = false;
defaultPermission = "limited";
sessionSecret = "$SESSION_SECRET";
gitlab = {
baseURL = "https://code.fbi.h-da.de";
clientID = "dc71d7ec1525ce3b425d7d41d602f67e1a06cef981259605a87841a6be62cc58";
clientSecret = "$GITLAB_CLIENTSECRET";
};
};
};
# Ensure session-secret
systemd.services.hedgedoc.preStart = lib.mkBefore ''
secret_file="${dataDir}/session-secret"
if [ ! -f $secret_file ]; then
${lib.getExe pkgs.pwgen} -s 64 1 > $secret_file
fi
SESSION_SECRET="$(cat $secret_file)"
export SESSION_SECRET
'';
custom = {
services.resticBackups.hedgedoc = lib.mkIf cfg.doBackups {
conflictingService = "hedgedoc.service";
paths =
let
inherit (config.services.hedgedoc) settings;
in
[
settings.uploadsPath
settings.db.storage
];
};
persist.directories = [ dataDir ];
};
};
}

View file

@ -0,0 +1,36 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.custom.services.it-tools;
in
{
options.custom.services.it-tools = {
enable = lib.mkEnableOption "";
domain = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "";
};
port = lib.mkOption {
type = lib.types.port;
default = 8787;
};
};
config = lib.mkIf cfg.enable {
meta = {
domains.list = [ cfg.domain ];
ports.tcp.list = [ cfg.port ];
};
services.static-web-server = {
enable = true;
listen = "[::]:${toString cfg.port}";
root = "${pkgs.it-tools}/lib";
configuration.general.health = true;
};
};
}

View file

@ -0,0 +1,59 @@
{
config,
options,
modulesPath,
inputs,
pkgs-unstable,
lib,
...
}:
let
cfg = config.custom.services.memos;
dataDir = config.services.memos.settings.MEMOS_DATA;
in
{
imports = [ "${inputs.nixpkgs-unstable}/nixos/modules/services/misc/memos.nix" ];
options.custom.services.memos = {
enable = lib.mkEnableOption "";
domain = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "";
};
port = lib.mkOption {
type = lib.types.port;
default = 5230;
};
doBackups = lib.mkEnableOption "";
};
config = lib.mkIf cfg.enable {
warnings =
lib.optional (lib.pathExists "${modulesPath}/services/misc/memos.nix") "TODO: Use memos module from stable nixpkgs"
++ lib.optional (lib.versionAtLeast lib.version "25.11") "TODO: Use memos package from stable nixpkgs";
meta = {
domains.list = [ cfg.domain ];
ports.tcp.list = [ cfg.port ];
};
services.memos = {
enable = true;
package = pkgs-unstable.memos;
settings = options.services.memos.settings.default // {
MEMOS_PORT = toString cfg.port;
MEMOS_INSTANCE_URL = "https://${cfg.domain}";
};
};
custom = {
services.resticBackups.memos = lib.mkIf cfg.doBackups {
conflictingService = "memos.service";
paths = [ dataDir ];
};
persist.directories = [ dataDir ];
};
};
}

View file

@ -0,0 +1,34 @@
{ config, lib, ... }:
let
cfg = config.custom.services.ntfy;
in
{
options.custom.services.ntfy = {
enable = lib.mkEnableOption "";
domain = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "";
};
port = lib.mkOption {
type = lib.types.port;
default = 2586;
};
};
config = lib.mkIf cfg.enable {
meta = {
domains.list = [ cfg.domain ];
ports.tcp.list = [ cfg.port ];
};
services.ntfy-sh = {
enable = true;
settings = lib.mkForce {
base-url = "https://${cfg.domain}";
listen-http = "localhost:${toString cfg.port}";
behind-proxy = true;
web-root = "disable";
};
};
};
}

View file

@ -0,0 +1,30 @@
{ config, lib, ... }:
let
cfg = config.custom.services.openspeedtest;
in
{
options.custom.services.openspeedtest = {
enable = lib.mkEnableOption "";
domain = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "";
};
port = lib.mkOption {
type = lib.types.port;
default = 4479;
};
};
config = lib.mkIf cfg.enable {
meta = {
domains.list = [ cfg.domain ];
ports.tcp.list = [ cfg.port ];
};
virtualisation.oci-containers.containers.openspeedtest = {
image = "openspeedtest/latest";
ports = [ "127.0.0.1:${toString cfg.port}:3000" ];
pull = "newer";
};
};
}

View file

@ -0,0 +1,91 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.custom.services.outline;
in
{
options.custom.services.outline = {
enable = lib.mkEnableOption "";
domain = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "";
};
port = lib.mkOption {
type = lib.types.port;
default = 32886;
};
doBackups = lib.mkEnableOption "";
};
config = lib.mkIf cfg.enable {
meta = {
domains.list = [ cfg.domain ];
ports.tcp.list = [ cfg.port ];
};
sops.secrets."outline/gitlab-auth-secret" = {
owner = config.users.users.outline.name;
restartUnits = [ "outline.service" ];
};
services.outline = {
enable = true;
publicUrl = "https://${cfg.domain}";
inherit (cfg) port;
forceHttps = false;
storage.storageType = "local";
# See https://docs.getoutline.com/s/hosting/doc/rate-limiter-HSqErsUgXH
rateLimiter = {
enable = true;
requests = 1000;
};
# See https://docs.getoutline.com/s/hosting/doc/gitlab-GjNVvyv7vW
oidcAuthentication =
let
baseURL = "https://code.fbi.h-da.de/oauth";
in
{
clientId = "3153f617736d608c0c10ddc4dd83f16c6a1f1b92d7f09a0493a924d389244295";
clientSecretFile = config.sops.secrets."outline/gitlab-auth-secret".path;
authUrl = "${baseURL}/authorize";
tokenUrl = "${baseURL}/token";
userinfoUrl = "${baseURL}/userinfo";
usernameClaim = "username";
displayName = "GitLab";
scopes = [
"openid"
"email"
];
};
};
systemd.services.outline.enableStrictShellChecks = false;
custom =
let
dataDir = "/var/lib/outline";
inherit (config.services.outline) user;
in
{
services.resticBackups.outline = lib.mkIf cfg.doBackups {
conflictingService = "outline.service";
paths = [ dataDir ];
extraConfig.backupPrepareCommand = ''
${lib.getExe pkgs.sudo} --user=${user} ${lib.getExe' config.services.postgresql.package "pg_dump"} outline --format=custom --file=${dataDir}/db.dump
'';
restoreCommand.postRestore = "sudo --user=${user} pg_restore --clean --if-exists --dbname outline ${dataDir}/db.dump";
};
persist.directories = [
dataDir
config.services.postgresql.dataDir
];
};
};
}

View file

@ -0,0 +1,57 @@
{
config,
pkgs-unstable,
lib,
...
}:
let
cfg = config.custom.services.privatebin;
in
{
options.custom.services.privatebin = {
enable = lib.mkEnableOption "";
domain = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "";
};
port = lib.mkOption {
type = lib.types.port;
default = 61009;
};
branding.name = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "PrivateBin";
};
};
config = lib.mkIf cfg.enable {
warnings = lib.optional (lib.versionAtLeast lib.version "25.11") "TODO: Use privatebin package from stable nixpkgs";
meta = {
domains.list = [ cfg.domain ];
ports.tcp.list = [ cfg.port ];
};
services = {
privatebin = {
enable = true;
package = pkgs-unstable.privatebin; # Unstable to get version 2.0
enableNginx = true;
virtualHost = "privatebin";
settings.main = {
basepath = "https://${cfg.domain}";
inherit (cfg.branding) name;
};
};
nginx.virtualHosts.privatebin.listen = [
{
addr = "localhost";
inherit (cfg) port;
}
];
};
custom.persist.directories = [ config.services.privatebin.dataDir ];
};
}

View file

@ -0,0 +1,117 @@
{
config,
inputs,
pkgs,
lib,
...
}:
let
cfg = config.custom.services.radicale;
dataDir = config.services.radicale.settings.storage.filesystem_folder;
in
{
options.custom.services.radicale = {
enable = lib.mkEnableOption "";
domain = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "";
};
port = lib.mkOption {
type = lib.types.port;
default = 5232;
};
doBackups = lib.mkEnableOption "";
};
config = lib.mkIf cfg.enable {
meta = {
domains.list = [ cfg.domain ];
ports.tcp.list = [ cfg.port ];
};
sops.secrets."radicale/htpasswd" = {
owner = config.users.users.radicale.name;
restartUnits = [ "radicale.service" ];
};
services.radicale = {
enable = true;
settings = {
server.hosts = "localhost:${toString cfg.port}";
auth = {
type = "htpasswd";
htpasswd_filename = config.sops.secrets."radicale/htpasswd".path;
htpasswd_encryption = "bcrypt";
};
storage.filesystem_folder = "/var/lib/radicale/collections";
storage.hook =
let
hookScript = pkgs.writeShellApplication {
name = "radicale-git-hook";
runtimeInputs = [
pkgs.git
pkgs.gawk
(pkgs.python3.withPackages (python-pkgs: [
python-pkgs.dateutil
python-pkgs.vobject
]))
];
text = ''
username="$1"
create_birthday_calendar="${inputs.radicale-birthday-calendar}/create_birthday_calendar.py"
git status --porcelain | awk '{print $2}' | python3 $create_birthday_calendar
git add -A
if ! git diff --cached --quiet; then
git commit --message "Changes by $username"
fi
'';
};
in
"${lib.getExe hookScript} %(user)s";
};
};
systemd.services.radicale.serviceConfig.ExecStartPre =
let
gitignore = pkgs.writeText "radicale-collection-gitignore" ''
.Radicale.cache
.Radicale.lock
.Radicale.tmp-*
'';
in
lib.getExe (
pkgs.writeShellApplication {
name = "radicale-git-init";
runtimeInputs = [ pkgs.git ];
text = ''
cd ${dataDir}
if [[ ! -e .git ]]; then
git init --initial-branch main
fi
git config user.name "Radicale"
git config user.email "radicale@${config.networking.hostName}"
cat ${gitignore} > .gitignore
git add .gitignore
if ! git diff --cached --quiet; then
git commit --message "Update .gitignore"
fi
'';
}
);
custom = {
services.resticBackups.radicale = lib.mkIf cfg.doBackups {
conflictingService = "radicale.service";
paths = [ dataDir ];
};
persist.directories = [ dataDir ];
};
};
}

View file

@ -0,0 +1,48 @@
{ config, lib, ... }:
let
cfg = config.custom.services.stirling-pdf;
in
{
options.custom.services.stirling-pdf = {
enable = lib.mkEnableOption "";
domain = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "";
};
port = lib.mkOption {
type = lib.types.port;
default = 56191;
};
branding = {
name = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "Stirling PDF";
};
description = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "Your locally hosted one-stop-shop for all your PDF needs.";
};
};
};
config = lib.mkIf cfg.enable {
meta = {
domains.list = [ cfg.domain ];
ports.tcp.list = [ cfg.port ];
};
services.stirling-pdf = {
enable = true;
environment = {
SERVER_ADDRESS = "127.0.0.1";
SERVER_PORT = cfg.port;
SYSTEM_ENABLEANALYTICS = "false";
LANGS = "de_DE";
UI_APPNAME = cfg.branding.name;
UI_APPNAVBARNAME = cfg.branding.name;
UI_HOMEDESCRIPTION = cfg.branding.description;
};
};
};
}

View file

@ -0,0 +1,64 @@
{
config,
pkgs-unstable,
lib,
...
}:
let
cfg = config.custom.services.victorialogs;
in
{
options.custom.services.victorialogs = {
enable = lib.mkEnableOption "";
domain = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "";
};
port = lib.mkOption {
type = lib.types.port;
default = 9428;
};
maxDiskSpaceUsage = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "10GiB";
};
};
config = lib.mkIf cfg.enable {
warnings = lib.optional (lib.versionAtLeast lib.version "25.11") "TODO: Use victorialogs package from stable nixpkgs";
meta = {
domains.list = [ cfg.domain ];
ports.tcp.list = [ cfg.port ];
};
users = {
users.victorialogs = {
isSystemUser = true;
group = config.users.groups.victoriametrics.name;
};
groups.victorialogs = { };
};
systemd.services.victorialogs.serviceConfig = {
DynamicUser = lib.mkForce false;
User = config.users.users.victorialogs.name;
Group = config.users.groups.victorialogs.name;
};
services.victorialogs = {
enable = true;
# The victorialogs grafana-dashboard expects the version label to have the format `victoria-logs-*`
package = pkgs-unstable.victorialogs.overrideAttrs (
_: previousAttrs: {
version = "victoria-logs-${previousAttrs.version}";
__intentionallyOverridingVersion = true;
}
);
listenAddress = "localhost:${toString cfg.port}";
extraOptions = [ "-retention.maxDiskSpaceUsageBytes=${cfg.maxDiskSpaceUsage}" ];
};
custom.persist.directories = [ "/var/lib/${config.services.victorialogs.stateDir}" ];
};
}

View file

@ -0,0 +1,64 @@
{
config,
pkgs-unstable,
lib,
...
}:
let
cfg = config.custom.services.victoriametrics;
in
{
options.custom.services.victoriametrics = {
enable = lib.mkEnableOption "";
domain = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "";
};
port = lib.mkOption {
type = lib.types.port;
default = 8428;
};
};
config = lib.mkIf cfg.enable {
warnings = lib.optional (lib.versionAtLeast lib.version "25.11") "TODO: Use victoriametrics package from stable nixpkgs";
meta = {
domains.list = [ cfg.domain ];
ports.tcp.list = [ cfg.port ];
};
users = {
users.victoriametrics = {
isSystemUser = true;
group = config.users.groups.victoriametrics.name;
};
groups.victoriametrics = { };
};
systemd.services.victoriametrics.serviceConfig = {
DynamicUser = lib.mkForce false;
User = config.users.users.victoriametrics.name;
Group = config.users.groups.victoriametrics.name;
};
services.victoriametrics = {
enable = true;
# The victoriametrics grafana-dashboard expects the version label to have the format `victoria-metrics-*`
package = pkgs-unstable.victoriametrics.overrideAttrs (
_: previousAttrs: {
version = "victoria-metrics-${previousAttrs.version}";
__intentionallyOverridingVersion = true;
}
);
listenAddress = "localhost:${toString cfg.port}";
extraOptions = [
"-selfScrapeInterval=15s"
"-selfScrapeJob=victoriametrics"
"-selfScrapeInstance=${config.networking.hostName}"
];
};
custom.persist.directories = [ "/var/lib/${config.services.victoriametrics.stateDir}" ];
};
}