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;
user = cfg.user;
group = cfg.group;
listenPort = 2005;
listenPort = 2050;
};
postgresql = {

View file

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

View file

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

View file

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

View file

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

View file

@ -17,7 +17,7 @@ in
auth_enabled = false;
server = {
http_listen_port = 9300;
http_listen_port = 9030;
};
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 {
services.prometheus = {
enable = true;
port = 9200;
port = 9020;
extraFlags = optionals config.${namespace}.services.observability.alloy.enable [
"--web.enable-remote-write-receiver"
];
@ -24,7 +24,7 @@ in
{
job_name = "prometheus";
static_configs = [
{ targets = [ "localhost:9200" ]; }
{ targets = [ "localhost:9020" ]; }
];
}
@ -39,7 +39,7 @@ in
{
job_name = "alloy";
static_configs = [
{ targets = [ "localhost:9700" ]; }
{ targets = [ "localhost:9070" ]; }
];
}
]
@ -47,7 +47,7 @@ in
{
job_name = "tempo";
static_configs = [
{ targets = [ "localhost:9600" ]; }
{ targets = [ "localhost:9060" ]; }
];
}
];
@ -55,13 +55,13 @@ in
exporters = {
node = {
enable = true;
port = 9201;
port = 9021;
enabledCollectors = [ "systemd" ];
openFirewall = true;
};
};
};
networking.firewall.allowedTCPPorts = [ 9200 ];
networking.firewall.allowedTCPPorts = [ 9020 ];
};
}

View file

@ -25,7 +25,7 @@ in {
configuration = {
server = {
http_listen_port = 9400;
http_listen_port = 9040;
grpc_listen_port = 0;
};
@ -35,7 +35,7 @@ in {
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;
httpPort = 9600;
grpcPort = 9601;
otlpGrpcPort = 9602;
otlpHttpPort = 9603;
httpPort = 9060;
grpcPort = 9061;
otlpGrpcPort = 9062;
otlpHttpPort = 9063;
in
{
options.${namespace}.services.observability.tempo = {

View file

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

View file

@ -53,6 +53,26 @@
service_name = "arrtrix";
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 {
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),
}
proc.Add(NewHelpHandler(proc))
proc.Add(NewDownloadHandler())
proc.Add(NewSubscriptionsHandler())
out := formatHelp(proc, &Context{
Bridge: &bridgev2.Bridge{

View file

@ -9,6 +9,8 @@ import (
"testing"
"maunium.net/go/mautrix/id"
"sneeuwvlok/packages/arrtrix/pkg/arr"
)
type stubRoomResolver struct {
@ -26,12 +28,21 @@ type stubNoticeSender struct {
err error
}
type stubSubscriptionFilter struct {
allowed bool
err error
}
func (s *stubNoticeSender) SendNotice(_ context.Context, roomID id.RoomID, message string) error {
s.roomID = roomID
s.message = message
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) {
router := http.NewServeMux()
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)
}
}
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
inputs.clan-core.packages.${stdenv.hostPlatform.system}.clan-cli
nix-output-monitor
dos2unix
];
}

View file

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