This commit is contained in:
Chris Kruining 2026-04-16 11:00:38 +02:00
parent e26e25b566
commit be2843ca80
No known key found for this signature in database
GPG key ID: EB894A3560CCCAD2
65 changed files with 187 additions and 39 deletions

View file

@ -64,7 +64,7 @@ in {
openFirewall = true; openFirewall = true;
user = cfg.user; user = cfg.user;
group = cfg.group; group = cfg.group;
listenPort = 2005; listenPort = 2050;
}; };
postgresql = { postgresql = {

View file

@ -22,7 +22,7 @@ in {
services.mydia = { services.mydia = {
enable = true; enable = true;
port = 2010; port = 2100;
listenAddress = "0.0.0.0"; listenAddress = "0.0.0.0";
openFirewall = true; openFirewall = true;
@ -54,7 +54,7 @@ in {
qbittorrent = { qbittorrent = {
type = "qbittorrent"; type = "qbittorrent";
host = "localhost"; host = "localhost";
port = 2008; port = 2080;
username = "admin"; username = "admin";
passwordFile = config.sops.secrets."mydia/qbittorrent_password".path; passwordFile = config.sops.secrets."mydia/qbittorrent_password".path;
useSsl = false; useSsl = false;

View file

@ -79,7 +79,7 @@ in {
qbittorrent = { qbittorrent = {
enable = true; enable = true;
openFirewall = true; openFirewall = true;
webuiPort = 2008; webuiPort = 2080;
serverConfig = lib.mkForce {}; serverConfig = lib.mkForce {};
user = "qbittorrent"; user = "qbittorrent";
@ -100,7 +100,7 @@ in {
settings = { settings = {
misc = { misc = {
host = "0.0.0.0"; host = "0.0.0.0";
port = 2009; port = 2090;
host_whitelist = "${config.networking.hostName}"; host_whitelist = "${config.networking.hostName}";
permissions = "770"; permissions = "770";
@ -126,7 +126,7 @@ in {
flaresolverr = { flaresolverr = {
enable = true; enable = true;
openFirewall = true; openFirewall = true;
port = 2007; port = 2070;
}; };
postgresql = let postgresql = let
@ -239,7 +239,7 @@ in {
username = "admin"; username = "admin";
password = lib.tfRef "var.qbittorrent_api_key"; password = lib.tfRef "var.qbittorrent_api_key";
url_base = "/"; url_base = "/";
port = 2008; port = 2080;
}; };
}; };
@ -251,7 +251,7 @@ in {
host = "localhost"; host = "localhost";
api_key = lib.tfRef "var.sabnzbd_api_key"; api_key = lib.tfRef "var.sabnzbd_api_key";
url_base = "/"; url_base = "/";
port = 2009; port = 2090;
}; };
}; };
} }

View file

@ -5,10 +5,10 @@ let
cfg = config.${namespace}.services.observability.alloy; cfg = config.${namespace}.services.observability.alloy;
httpPort = 9700; httpPort = 9070;
otlpGrpcPort = 9701; otlpGrpcPort = 9071;
otlpHttpPort = 9702; otlpHttpPort = 9072;
tempoOtlpGrpcPort = 9602; tempoOtlpGrpcPort = 9062;
in in
{ {
options.${namespace}.services.observability.alloy = { options.${namespace}.services.observability.alloy = {

View file

@ -25,7 +25,7 @@ in {
settings = { settings = {
server = { server = {
http_port = 9100; http_port = 9010;
http_addr = "0.0.0.0"; http_addr = "0.0.0.0";
domain = "ulmo"; domain = "ulmo";
}; };
@ -106,7 +106,7 @@ in {
name = "Prometheus"; name = "Prometheus";
uid = "prometheus"; uid = "prometheus";
type = "prometheus"; type = "prometheus";
url = "http://localhost:9200"; url = "http://localhost:9020";
isDefault = true; isDefault = true;
editable = false; editable = false;
} }
@ -115,7 +115,7 @@ in {
name = "Loki"; name = "Loki";
uid = "loki"; uid = "loki";
type = "loki"; type = "loki";
url = "http://localhost:9300"; url = "http://localhost:9030";
editable = false; editable = false;
} }
@ -123,7 +123,7 @@ in {
name = "Tempo"; name = "Tempo";
uid = "tempo"; uid = "tempo";
type = "tempo"; type = "tempo";
url = "http://localhost:9600"; url = "http://localhost:9060";
editable = false; editable = false;
jsonData = { jsonData = {
nodeGraph.enabled = true; nodeGraph.enabled = true;

View file

@ -17,7 +17,7 @@ in
auth_enabled = false; auth_enabled = false;
server = { server = {
http_listen_port = 9300; http_listen_port = 9030;
}; };
common = { common = {
@ -44,6 +44,6 @@ in
}; };
}; };
networking.firewall.allowedTCPPorts = [ 9300 ]; networking.firewall.allowedTCPPorts = [ 9030 ];
}; };
} }

View file

@ -13,7 +13,7 @@ in
config = mkIf cfg.enable { config = mkIf cfg.enable {
services.prometheus = { services.prometheus = {
enable = true; enable = true;
port = 9200; port = 9020;
extraFlags = optionals config.${namespace}.services.observability.alloy.enable [ extraFlags = optionals config.${namespace}.services.observability.alloy.enable [
"--web.enable-remote-write-receiver" "--web.enable-remote-write-receiver"
]; ];
@ -24,7 +24,7 @@ in
{ {
job_name = "prometheus"; job_name = "prometheus";
static_configs = [ static_configs = [
{ targets = [ "localhost:9200" ]; } { targets = [ "localhost:9020" ]; }
]; ];
} }
@ -39,7 +39,7 @@ in
{ {
job_name = "alloy"; job_name = "alloy";
static_configs = [ static_configs = [
{ targets = [ "localhost:9700" ]; } { targets = [ "localhost:9070" ]; }
]; ];
} }
] ]
@ -47,7 +47,7 @@ in
{ {
job_name = "tempo"; job_name = "tempo";
static_configs = [ static_configs = [
{ targets = [ "localhost:9600" ]; } { targets = [ "localhost:9060" ]; }
]; ];
} }
]; ];
@ -55,13 +55,13 @@ in
exporters = { exporters = {
node = { node = {
enable = true; enable = true;
port = 9201; port = 9021;
enabledCollectors = [ "systemd" ]; enabledCollectors = [ "systemd" ];
openFirewall = true; openFirewall = true;
}; };
}; };
}; };
networking.firewall.allowedTCPPorts = [ 9200 ]; networking.firewall.allowedTCPPorts = [ 9020 ];
}; };
} }

View file

@ -25,7 +25,7 @@ in {
configuration = { configuration = {
server = { server = {
http_listen_port = 9400; http_listen_port = 9040;
grpc_listen_port = 0; grpc_listen_port = 0;
}; };
@ -35,7 +35,7 @@ in {
clients = [ clients = [
{ {
url = "http://[::1]:9300/loki/api/v1/push"; url = "http://[::1]:9030/loki/api/v1/push";
} }
]; ];
@ -60,6 +60,6 @@ in {
}; };
}; };
networking.firewall.allowedTCPPorts = [9400]; networking.firewall.allowedTCPPorts = [9040];
}; };
} }

View file

@ -4,10 +4,10 @@ let
cfg = config.${namespace}.services.observability.tempo; cfg = config.${namespace}.services.observability.tempo;
httpPort = 9600; httpPort = 9060;
grpcPort = 9601; grpcPort = 9061;
otlpGrpcPort = 9602; otlpGrpcPort = 9062;
otlpHttpPort = 9603; otlpHttpPort = 9063;
in in
{ {
options.${namespace}.services.observability.tempo = { options.${namespace}.services.observability.tempo = {

View file

@ -15,11 +15,11 @@ in
enable = true; enable = true;
settings = { settings = {
PORT = toString 9500; PORT = toString 9050;
HOST = "0.0.0.0"; HOST = "0.0.0.0";
}; };
}; };
networking.firewall.allowedTCPPorts = [ 9500 ]; networking.firewall.allowedTCPPorts = [ 9050 ];
}; };
} }

View file

@ -53,6 +53,26 @@
service_name = "arrtrix"; service_name = "arrtrix";
resource_attributes = {}; resource_attributes = {};
}; };
network.content = {
movies = {
url = "";
api_key = "";
root_folder_path = "";
quality_profile_id = 0;
minimum_availability = "released";
search_on_add = true;
};
series = {
url = "";
api_key = "";
root_folder_path = "";
quality_profile_id = 0;
language_profile_id = 0;
season_folder = true;
series_type = "standard";
search_on_add = true;
};
};
}; };
in { in {
options.services.arrtrix = { options.services.arrtrix = {

View file

@ -0,0 +1,23 @@
package arr
import "testing"
func TestParseContentType(t *testing.T) {
contentType, err := ParseContentType("Movies")
if err != nil {
t.Fatalf("ParseContentType returned error: %v", err)
}
if contentType != ContentTypeMovies {
t.Fatalf("expected movies content type, got %q", contentType)
}
}
func TestParseEventType(t *testing.T) {
eventType, err := ParseEventType(ContentTypeSeries, "download")
if err != nil {
t.Fatalf("ParseEventType returned error: %v", err)
}
if eventType != "Download" {
t.Fatalf("expected Download event type, got %q", eventType)
}
}

View file

@ -0,0 +1,23 @@
package connector
import "testing"
func TestValidateConfigRejectsPartialMoviesConfig(t *testing.T) {
conn := &ArrtrixConnector{
Config: Config{
Content: ContentConfig{},
},
}
conn.Config.Content.Movies.URL = "http://radarr.test"
if err := conn.ValidateConfig(); err == nil {
t.Fatal("expected partial movies config to fail validation")
}
}
func TestValidateConfigAllowsEmptyContentConfig(t *testing.T) {
conn := &ArrtrixConnector{}
if err := conn.ValidateConfig(); err != nil {
t.Fatalf("ValidateConfig returned error: %v", err)
}
}

View file

@ -18,6 +18,8 @@ func TestFormatHelpManagementRoom(t *testing.T) {
alias: make(map[string]string), alias: make(map[string]string),
} }
proc.Add(NewHelpHandler(proc)) proc.Add(NewHelpHandler(proc))
proc.Add(NewDownloadHandler())
proc.Add(NewSubscriptionsHandler())
out := formatHelp(proc, &Context{ out := formatHelp(proc, &Context{
Bridge: &bridgev2.Bridge{ Bridge: &bridgev2.Bridge{

View file

@ -9,6 +9,8 @@ import (
"testing" "testing"
"maunium.net/go/mautrix/id" "maunium.net/go/mautrix/id"
"sneeuwvlok/packages/arrtrix/pkg/arr"
) )
type stubRoomResolver struct { type stubRoomResolver struct {
@ -26,12 +28,21 @@ type stubNoticeSender struct {
err error err error
} }
type stubSubscriptionFilter struct {
allowed bool
err error
}
func (s *stubNoticeSender) SendNotice(_ context.Context, roomID id.RoomID, message string) error { func (s *stubNoticeSender) SendNotice(_ context.Context, roomID id.RoomID, message string) error {
s.roomID = roomID s.roomID = roomID
s.message = message s.message = message
return s.err return s.err
} }
func (s stubSubscriptionFilter) Allows(context.Context, id.UserID, arr.ContentType, string) (bool, error) {
return s.allowed, s.err
}
func TestMountArrRequiresBridge(t *testing.T) { func TestMountArrRequiresBridge(t *testing.T) {
router := http.NewServeMux() router := http.NewServeMux()
if err := MountArr(router, nil, nil); err == nil { if err := MountArr(router, nil, nil); err == nil {
@ -112,3 +123,23 @@ func TestArrHandlerRejectsMissingEventType(t *testing.T) {
t.Fatalf("expected bad request status, got %d", rec.Code) t.Fatalf("expected bad request status, got %d", rec.Code)
} }
} }
func TestArrHandlerFiltersDisabledSubscriptions(t *testing.T) {
sender := &stubNoticeSender{}
handler := &ArrHandler{
resolver: stubRoomResolver{target: managementTarget{UserID: "@user:test", RoomID: "!room:test"}},
sender: sender,
subscriptions: stubSubscriptionFilter{allowed: false},
}
req := httptest.NewRequest(http.MethodPost, ArrWebhookPath, strings.NewReader(`{"eventType":"Download","movie":{"title":"Dune","year":2021}}`))
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
if rec.Code != http.StatusAccepted {
t.Fatalf("expected accepted status, got %d", rec.Code)
}
if sender.roomID != "" {
t.Fatalf("expected no notice to be sent, got room %q", sender.roomID)
}
}

View file

@ -18,5 +18,6 @@ mkShell {
openssl openssl
inputs.clan-core.packages.${stdenv.hostPlatform.system}.clan-cli inputs.clan-core.packages.${stdenv.hostPlatform.system}.clan-cli
nix-output-monitor nix-output-monitor
dos2unix
]; ];
} }

View file

@ -140,13 +140,13 @@
}; };
mydia = { mydia = {
redirectUris = ["http://localhost:2010/auth/oidc/callback"]; redirectUris = ["http://localhost:2100/auth/oidc/callback"];
grantTypes = ["authorizationCode"]; grantTypes = ["authorizationCode"];
responseTypes = ["code"]; responseTypes = ["code"];
}; };
grafana = { grafana = {
redirectUris = ["http://localhost:9100/login/generic_oauth"]; redirectUris = ["http://localhost:9010/login/generic_oauth"];
grantTypes = ["authorizationCode"]; grantTypes = ["authorizationCode"];
responseTypes = ["code"]; responseTypes = ["code"];
}; };
@ -224,7 +224,7 @@
media.servarr = { media.servarr = {
radarr = { radarr = {
enable = true; enable = true;
port = 2001; port = 2010;
rootFolders = [ rootFolders = [
"/var/media/movies" "/var/media/movies"
]; ];
@ -233,7 +233,7 @@
sonarr = { sonarr = {
enable = true; enable = true;
# debug = true; # debug = true;
port = 2002; port = 2020;
rootFolders = [ rootFolders = [
"/var/media/series" "/var/media/series"
]; ];
@ -242,7 +242,7 @@
lidarr = { lidarr = {
enable = true; enable = true;
debug = true; debug = true;
port = 2003; port = 2030;
rootFolders = [ rootFolders = [
"/var/media/music" "/var/media/music"
]; ];
@ -251,7 +251,7 @@
prowlarr = { prowlarr = {
enable = true; enable = true;
# debug = true; # debug = true;
port = 2004; port = 2040;
}; };
}; };

View file

@ -0,0 +1 @@
../../../../../../sops/machines/ulmo

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/machines/ulmo

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/machines/ulmo

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/machines/ulmo

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/machines/ulmo

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/machines/ulmo

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/machines/ulmo

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/machines/ulmo

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/machines/ulmo

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/machines/ulmo

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/machines/ulmo

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/machines/ulmo

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/machines/ulmo

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/machines/ulmo

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/machines/ulmo

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/machines/ulmo

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/machines/ulmo

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/machines/ulmo

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/machines/ulmo

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris

View file

@ -0,0 +1 @@
../../../../../../sops/machines/ulmo

View file

@ -0,0 +1 @@
../../../../../../sops/users/chris