diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index fde70a6..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "aur"] - path = aur - url = ssh://aur@aur.archlinux.org/sanic.git diff --git a/aur b/aur deleted file mode 160000 index 8e38dfb..0000000 --- a/aur +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8e38dfb90aa5b09a7ac6c762a4f5cc19a94b8b1d diff --git a/aur/PKGBUILD b/aur/PKGBUILD new file mode 100644 index 0000000..3f7f4f9 --- /dev/null +++ b/aur/PKGBUILD @@ -0,0 +1,53 @@ +# Maintainer: Ricardo Band + +pkgname=sanic +pkgver=0.0.1 +pkgrel=1 +pkgdesc="chaos music control inspired by relaxx player" +arch=('any') +url=https://git.berlin.ccc.de/cccb/sanic +license=('custom:MIT') +makedepends=('go') +source=("$pkgname.service" + "$pkgname.sysusers" + "$pkgname.tmpfiles" + "$url/archive/v$pkgver.tar.gz") +sha256sums=("1337deadbeef" + "1337deadbeef" + "1337deadbeef" + "1337deadbeef") + +prepare() { + cd "$pkgname-$pkgver" + + mkdir -p build/ +} + +build() { + cd "$pkgname-$pkgver" + + export CGO_CPPFLAGS="$CPPFLAGS" + export CGO_CFLAGS="$CFLAGS" + export CGO_CXXFLAGS="$CXXFLAGS" + export CGO_LDFLAGS="$LDFLAGS" + export GOFLAGS="-buildmode=pie -trimpath -ldflags=-linkmode=external -mod=readonly -modcacherw" + + go build -o build/ . +} + +check() { + cd "$pkgname-$pkgver" + + go test ./... +} + +package() { + cd "$pkgname-$pkgver" + + install -Dm644 "LICENSE" "$pkgdir/usr/share/licenses/$pkgname/LICENSE" + install -Dm755 build/$pkgname "$pkgdir"/usr/bin/$pkgname + install -Dm644 "../$pkgname.service" "$pkgdir/usr/lib/systemd/system/$pkgname.service" + install -Dm644 "../$pkgname.sysusers" "$pkgdir/usr/lib/sysusers.d/$pkgname.conf" + install -Dm644 "../$pkgname.tmpfiles" "$pkgdir/usr/lib/tmpfiles.d/$pkgname.conf" +} + diff --git a/aur/sanic.service b/aur/sanic.service new file mode 100644 index 0000000..0afa7e9 --- /dev/null +++ b/aur/sanic.service @@ -0,0 +1,28 @@ +[Unit] +Description=chaos music control +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=sanic +Group=sanic +ExecStart=/usr/bin/sanic +Restart=always +# security +NoNewPrivileges=true +ProtectSystem=strict +ProtectHome=yes +StateDirectory=sanic +StateDirectoryMode=0750 +ConfigurationDirectory=sanic +ConfigurationDirectoryMode=0750 +PrivateTmp=true +ProtectKernelTunables=true +ProtectKernelModules=true +ProtectKernelLogs=true +ProtectControlGroups=true + +[Install] +WantedBy=multi-user.target + diff --git a/aur/sanic.sysusers b/aur/sanic.sysusers new file mode 100644 index 0000000..a286acc --- /dev/null +++ b/aur/sanic.sysusers @@ -0,0 +1,3 @@ +u sanic - "chaos music control" /run/sanic /usr/bin/nologin +g sanic - - + diff --git a/aur/sanic.tmpfiles b/aur/sanic.tmpfiles new file mode 100644 index 0000000..cff6c68 --- /dev/null +++ b/aur/sanic.tmpfiles @@ -0,0 +1,3 @@ +d /etc/sanic 0750 sanic sanic +d /run/sanic 0750 sanic sanic + diff --git a/flake.nix b/flake.nix index 8bc75c6..80aa871 100644 --- a/flake.nix +++ b/flake.nix @@ -37,7 +37,119 @@ ]; }; packages.default = sanic; - nixosModules.default = import ./option.nix; + nixosModules.default = { config, lib, pkgs, options, ... }: + let + cfg = config.services.sanic; + configFile = pkgs.writeText "config.ini" (pkgs.lib.generators.toINI {} cfg); + execCommand = "${cfg.package}/bin/sanic -c '${configFile}'"; + in + { + options.services.sanic = { + enable = lib.mkEnableOption "Enables the sanic systemd service."; + package = lib.mkOption { + description = "Package to use."; + type = lib.types.package; + default = sanic; + }; + ui = lib.mkOption { + description = "Setting for HTTP(S) UI."; + example = lib.literalExpression '' + { + host = "[::1]"; + port = 8443; + tls = true; + certificate = "${config.security.acme.certs."sanic.example.com".directory}/fullchain.pem"; + key = "${config.security.acme.certs."sanic.example.com".directory}/key.pem"; + } + ''; + default = { + host = "[::1]"; + port = 80; + tls = false; + }; + type = lib.types.submodule { + options = { + host = lib.mkOption { + type = lib.types.str; + default = "[::1]"; + description = "Host to bind to."; + }; + port = lib.mkOption { + type = lib.types.port; + default = 8080; + description = "Port to listen on."; + }; + tls = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Enables HTTPS."; + }; + certificate = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = "Path to TLS certificate for HTTPS."; + }; + key = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = "Path to TLS key for HTTPS."; + }; + }; + }; + }; + backend = lib.mkOption { + description = "Configure MPD backend."; + example = lib.literalExpression '' + { + host = "localhost"; + port = 6600; + } + ''; + default = { + host = "localhost"; + port = 6600; + }; + type = lib.types.submodule { + options = { + host = lib.mkOption { + type = lib.types.str; + default = "localhost"; + description = "Hostname or IP of MPD instance."; + }; + port = lib.mkOption { + type = lib.types.port; + default = 6600; + description = "Port of MPD instance."; + }; + }; + }; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services."sanic" = { + description = "sanic - chaos music control"; + wants = [ "network-online.target" ]; + after = [ "network-online.target" ]; + serviceConfig = { + Restart = "always"; + RestartSec = 30; + ExecStart = execCommand; + User = "sanic"; + Group = "sanic"; + AmbientCapabilities = lib.mkIf (cfg.ui.port < 1000) [ "CAP_NET_BIND_SERVICE" ]; + CapabilityBoundingSet = lib.mkIf (cfg.ui.port < 1000) [ "CAP_NET_BIND_SERVICE" ]; + NoNewPrivileges = true; + }; + wantedBy = [ "multi-user.target" ]; + }; + }; + + #meta = { + # maintainers = with lib.maintainers; [ xengi ]; + # doc = ./default.xml; + #}; + }; } ); } diff --git a/go.mod b/go.mod index 9577231..71d5a15 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ require ( github.com/fhs/gompd/v2 v2.3.0 github.com/labstack/echo-contrib v0.17.1 github.com/labstack/echo/v4 v4.12.0 - github.com/tdewolff/minify/v2 v2.24.0 gopkg.in/ini.v1 v1.67.0 ) @@ -22,12 +21,11 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/tdewolff/parse/v2 v2.8.3 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect golang.org/x/crypto v0.26.0 // indirect golang.org/x/net v0.28.0 // indirect - golang.org/x/sys v0.30.0 // indirect + golang.org/x/sys v0.23.0 // indirect golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.6.0 // indirect google.golang.org/protobuf v1.34.2 // indirect diff --git a/go.sum b/go.sum index d337aa6..f056cf3 100644 --- a/go.sum +++ b/go.sum @@ -35,12 +35,6 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tdewolff/minify/v2 v2.24.0 h1:m6j8VXvgUtmkavubzHbaNTXi9tw3hjIMZbdc57SRdvI= -github.com/tdewolff/minify/v2 v2.24.0/go.mod h1:uqtSu3w0+anqk4ofcsuLPZ8tV8yAZL1r/ILWYYl2j3c= -github.com/tdewolff/parse/v2 v2.8.3 h1:5VbvtJ83cfb289A1HzRA9sf02iT8YyUwN84ezjkdY1I= -github.com/tdewolff/parse/v2 v2.8.3/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo= -github.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE= -github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= @@ -51,8 +45,8 @@ golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= diff --git a/mpd.go b/mpd.go index 6268441..5e4cf37 100644 --- a/mpd.go +++ b/mpd.go @@ -1,13 +1,12 @@ package main import ( - "fmt" - "net/http" - "strconv" - "time" - - "github.com/fhs/gompd/v2/mpd" - "github.com/labstack/echo/v4" + "fmt" + "github.com/fhs/gompd/v2/mpd" + "github.com/labstack/echo/v4" + "net/http" + "strconv" + "time" ) // MPD API calls diff --git a/option.nix b/option.nix deleted file mode 100644 index 4ce3e1e..0000000 --- a/option.nix +++ /dev/null @@ -1,114 +0,0 @@ -{ config, lib, pkgs, options, ... }: - -let - cfg = config.services.sanic; - configFile = pkgs.writeText "config.ini" (pkgs.lib.generators.toINI {} cfg); - execCommand = "${cfg.package}/bin/sanic -c '${configFile}'"; -in -{ - options.services.sanic = { - enable = lib.mkEnableOption "Enables the sanic systemd service."; - package = lib.mkOption { - description = "Package to use."; - type = lib.types.package; - default = sanic; - }; - ui = lib.mkOption { - description = "Setting for HTTP(S) UI."; - example = lib.literalExpression '' - { - host = "[::1]"; - port = 8443; - tls = true; - certificate = "${config.security.acme.certs."sanic.example.com".directory}/fullchain.pem"; - key = "${config.security.acme.certs."sanic.example.com".directory}/key.pem"; - } - ''; - default = { - host = "[::1]"; - port = 80; - tls = false; - }; - type = lib.types.submodule { - options = { - host = lib.mkOption { - type = lib.types.str; - default = "[::1]"; - description = "Host to bind to."; - }; - port = lib.mkOption { - type = lib.types.port; - default = 8080; - description = "Port to listen on."; - }; - tls = lib.mkOption { - type = lib.types.bool; - default = false; - description = "Enables HTTPS."; - }; - certificate = lib.mkOption { - type = lib.types.nullOr lib.types.path; - default = null; - description = "Path to TLS certificate for HTTPS."; - }; - key = lib.mkOption { - type = lib.types.nullOr lib.types.path; - default = null; - description = "Path to TLS key for HTTPS."; - }; - }; - }; - }; - backend = lib.mkOption { - description = "Configure MPD backend."; - example = lib.literalExpression '' - { - host = "localhost"; - port = 6600; - } - ''; - default = { - host = "localhost"; - port = 6600; - }; - type = lib.types.submodule { - options = { - host = lib.mkOption { - type = lib.types.str; - default = "localhost"; - description = "Hostname or IP of MPD instance."; - }; - port = lib.mkOption { - type = lib.types.port; - default = 6600; - description = "Port of MPD instance."; - }; - }; - }; - }; - }; - - config = lib.mkIf cfg.enable { - systemd.services."sanic" = { - description = "sanic - chaos music control"; - wants = [ "network-online.target" ]; - after = [ "network-online.target" ]; - serviceConfig = { - Restart = "always"; - RestartSec = 30; - ExecStart = execCommand; - User = "sanic"; - Group = "sanic"; - AmbientCapabilities = lib.mkIf (cfg.ui.port < 1000) [ "CAP_NET_BIND_SERVICE" ]; - CapabilityBoundingSet = lib.mkIf (cfg.ui.port < 1000) [ "CAP_NET_BIND_SERVICE" ]; - NoNewPrivileges = true; - }; - wantedBy = [ "multi-user.target" ]; - }; - }; - - #meta = { - # maintainers = with lib.maintainers; [ xengi ]; - # doc = ./default.xml; - #}; -} diff --git a/sanic.container b/sanic.container index add32d5..2f6983a 100644 --- a/sanic.container +++ b/sanic.container @@ -26,3 +26,4 @@ TimeoutStartSec=900 [Install] WantedBy=multi-user.target default.target + diff --git a/server.go b/server.go index a8760ae..233c4dc 100644 --- a/server.go +++ b/server.go @@ -1,29 +1,24 @@ package main import ( - "embed" - "fmt" - "net/http" - "os" - "os/exec" - - "github.com/labstack/echo-contrib/echoprometheus" - "github.com/labstack/echo/v4" - "github.com/labstack/echo/v4/middleware" - "gopkg.in/ini.v1" + "fmt" + "github.com/labstack/echo-contrib/echoprometheus" + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" + "gopkg.in/ini.v1" + "net/http" + "os" + "os/exec" ) -//go:embed static/* -var staticFS embed.FS - -// Config holds the configuration for the mpd backend connection and for the web server. +// Config holds the configuration for the mpd connection and for the web server. type Config struct { - BACKEND struct { + MPD struct { Hostname string `ini:"hostname"` Port int `ini:"port"` Username string `ini:"username"` Password string `ini:"password"` - } `ini:"backend"` + } `ini:"mpd"` UI struct { Hostname string `ini:"hostname"` Port int `ini:"port"` diff --git a/services.nix b/services.nix new file mode 100644 index 0000000..5303b42 --- /dev/null +++ b/services.nix @@ -0,0 +1,30 @@ +{ self, ...}: {config, lib, pkgs, ...}: + +let + cfg = config.services.sanic; + format = pkgs.formats.ini { }; +in +{ + options.services.sanic = { + enable = mkEnableOption (lib.mdDoc "sanic"); + settings = mkOption { + type = format.type; + default = { }; + description = lib.mkDoc '' + ''; + }; + }; + + config = mkIf cfg.enable { + systemd.services.sanic = { + description = "chaos music control"; + wantedBy = [ "multi-user.target" "default.target" ]; + serviceConfig = { + DynamicUser = true; + ExecStart = "${self.packages.${pkgs.system}.default}/bin/sanic"; + Restart = "on-failure"; + AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; + }; + }; + }; +} diff --git a/sse.go b/sse.go index 554c3ad..97fa230 100644 --- a/sse.go +++ b/sse.go @@ -1,14 +1,13 @@ package main import ( - "bytes" - "encoding/json" - "fmt" - "io" - "time" - - "github.com/fhs/gompd/v2/mpd" - "github.com/labstack/echo/v4" + "bytes" + "encoding/json" + "fmt" + "github.com/fhs/gompd/v2/mpd" + "github.com/labstack/echo/v4" + "io" + "time" ) // Event represents Server-Sent Event. diff --git a/static/index.html b/static/index.html index 7515cd7..531509d 100644 --- a/static/index.html +++ b/static/index.html @@ -1,38 +1,11 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Sanic - - - + + + @@ -44,7 +17,7 @@ - +
@@ -90,20 +63,20 @@
-
+
- + - +
@@ -206,7 +179,7 @@ - +
actions diff --git a/static/js/index.js b/static/index.js similarity index 99% rename from static/js/index.js rename to static/index.js index 20980e6..5bebb7a 100644 --- a/static/js/index.js +++ b/static/index.js @@ -157,6 +157,7 @@ dialog_save_playlist_close.addEventListener("click", () => { dialog_save_playlist.close() }); + // Add API calls to controls control_search_submit.addEventListener("click", event => { @@ -214,6 +215,8 @@ control_delete_playlist.addEventListener("click", () => { }); }); +// Add API calls to controls + control_update_db.addEventListener("click", (event) => { console.log("Issuing database update"); fetch(`${API_URL}/update_db`).then(async r => { diff --git a/static/js/sse.js b/static/sse.js similarity index 100% rename from static/js/sse.js rename to static/sse.js diff --git a/static/css/style.css b/static/style.css similarity index 100% rename from static/css/style.css rename to static/style.css diff --git a/static/css/treeview.css b/static/treeview.css similarity index 100% rename from static/css/treeview.css rename to static/treeview.css