feat: your_spotify module
This commit is contained in:
parent
d885e47cec
commit
d36fa802d3
|
@ -15,6 +15,7 @@
|
|||
paperlessdomain = "ppl.${config.secrets.secrets.global.domains.web}";
|
||||
ttrssdomain = "rss.${config.secrets.secrets.global.domains.web}";
|
||||
vaultwardendomain = "pw.${config.secrets.secrets.global.domains.web}";
|
||||
spotifydomain = "spotify.${config.secrets.secrets.global.domains.web}";
|
||||
ipOf = hostName: lib.net.cidr.host config.secrets.secrets.global.net.ips."${config.guests.${hostName}.nodeName}" config.secrets.secrets.global.net.privateSubnetv4;
|
||||
in {
|
||||
services.nginx = {
|
||||
|
@ -96,6 +97,7 @@ in {
|
|||
proxyPass = "http://ollama";
|
||||
proxyWebsockets = true;
|
||||
};
|
||||
|
||||
extraConfig = ''
|
||||
allow ${config.secrets.secrets.global.net.privateSubnetv4};
|
||||
allow ${config.secrets.secrets.global.net.privateSubnetv6};
|
||||
|
@ -162,6 +164,22 @@ in {
|
|||
'';
|
||||
};
|
||||
|
||||
upstreams.spotify = {
|
||||
servers."${ipOf "your_spotify"}:80" = {};
|
||||
|
||||
extraConfig = ''
|
||||
zone spotify 64k ;
|
||||
keepalive 5 ;
|
||||
'';
|
||||
};
|
||||
virtualHosts.${spotifydomain} = {
|
||||
forceSSL = true;
|
||||
useACMEHost = "web";
|
||||
locations."/".proxyPass = "http://spotify";
|
||||
extraConfig = ''
|
||||
'';
|
||||
};
|
||||
|
||||
upstreams.nextcloud = {
|
||||
servers."${ipOf "nextcloud"}:80" = {};
|
||||
|
||||
|
@ -266,6 +284,7 @@ in {
|
|||
// mkContainer "ddclient" {}
|
||||
// mkContainer "ollama" {}
|
||||
// mkContainer "ttrss" {}
|
||||
// mkContainer "your_spotify" {}
|
||||
// mkContainer "nextcloud" {
|
||||
enablePanzer = true;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
maddy = uidGid 218;
|
||||
tt_rss = uidGid 219;
|
||||
freshrss = uidGid 220;
|
||||
mongodb = uidGid 221;
|
||||
your_spotify = uidGid 222;
|
||||
paperless = uidGid 315;
|
||||
systemd-oom = uidGid 300;
|
||||
systemd-coredump = uidGid 301;
|
||||
|
|
21
modules/services/your_spotify.nix
Normal file
21
modules/services/your_spotify.nix
Normal file
|
@ -0,0 +1,21 @@
|
|||
{config, ...}: {
|
||||
imports = [./your_spotify_m.nix];
|
||||
age.secrets.spotify = {
|
||||
owner = config.services.your_spotify.user;
|
||||
group = config.services.your_spotify.group;
|
||||
rekeyFile = ../../secrets/your_spotify.age;
|
||||
};
|
||||
services.your_spotify = {
|
||||
enable = true;
|
||||
config = {
|
||||
clientEndpoint = "https://spotify.${config.secrets.secrets.global.domains.web}";
|
||||
};
|
||||
environmentFile = config.age.secrets.spotify.path;
|
||||
};
|
||||
environment.persistence."/persist".directories = [
|
||||
{
|
||||
directory = config.services.mongodb.dbpath;
|
||||
user = config.services.mongodb.user;
|
||||
}
|
||||
];
|
||||
}
|
220
modules/services/your_spotify_m.nix
Normal file
220
modules/services/your_spotify_m.nix
Normal file
|
@ -0,0 +1,220 @@
|
|||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}: let
|
||||
inherit
|
||||
(lib)
|
||||
boolToString
|
||||
concatMapAttrs
|
||||
concatStrings
|
||||
elem
|
||||
foldl'
|
||||
head
|
||||
isBool
|
||||
isList
|
||||
lowerChars
|
||||
mapAttrsToList
|
||||
mdDoc
|
||||
mkEnableOption
|
||||
mkIf
|
||||
mkOption
|
||||
mkPackageOption
|
||||
optional
|
||||
optionalAttrs
|
||||
optionalString
|
||||
stringLength
|
||||
substring
|
||||
toUpper
|
||||
types
|
||||
;
|
||||
cfg = config.services.your_spotify;
|
||||
|
||||
# Convert name from camel case (e.g. disable2FARemember) to upper case snake case (e.g. DISABLE_2FA_REMEMBER).
|
||||
nameToEnvVar = name: let
|
||||
parts = builtins.split "([A-Z0-9]+)" name;
|
||||
partsToEnvVar = parts:
|
||||
foldl' (key: x: let
|
||||
last = stringLength key - 1;
|
||||
in
|
||||
if isList x
|
||||
then key + optionalString (key != "" && substring last 1 key != "_") "_" + head x
|
||||
else if key != "" && elem (substring 0 1 x) lowerChars
|
||||
then # to handle e.g. [ "disable" [ "2FAR" ] "emember" ]
|
||||
substring 0 last key + optionalString (substring (last - 1) 1 key != "_") "_" + substring last 1 key + toUpper x
|
||||
else key + toUpper x) ""
|
||||
parts;
|
||||
in
|
||||
if builtins.match "[A-Z0-9_]+" name != null
|
||||
then name
|
||||
else partsToEnvVar parts;
|
||||
|
||||
# Due to the different naming schemes allowed for config keys,
|
||||
# we can only check for values consistently after converting them to their corresponding environment variable name.
|
||||
configEnv = concatMapAttrs (name: value:
|
||||
optionalAttrs (value != null) {
|
||||
${nameToEnvVar name} =
|
||||
if isBool value
|
||||
then boolToString value
|
||||
else toString value;
|
||||
})
|
||||
cfg.config;
|
||||
|
||||
configFile = pkgs.writeText "your_spotify.env" (concatStrings (mapAttrsToList (name: value: "${name}=${value}\n") configEnv));
|
||||
in {
|
||||
options.services.your_spotify = let
|
||||
inherit (types) nullOr int str bool package;
|
||||
in {
|
||||
enable = mkEnableOption (lib.mdDoc "your_spotify");
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "your_spotify";
|
||||
description = lib.mdDoc "User account under which your_spotify runs.";
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = "your_spotify";
|
||||
description = lib.mdDoc "Group account under which your_spotify runs.";
|
||||
};
|
||||
|
||||
package = mkPackageOption pkgs "your_spotify" {};
|
||||
|
||||
clientPackage = mkOption {
|
||||
type = package;
|
||||
default = cfg.package.client.override {apiEndpoint = cfg.config.apiEndpoint;};
|
||||
description = lib.mdDoc "Client package to use.";
|
||||
};
|
||||
|
||||
config = {
|
||||
clientEndpoint = mkOption {
|
||||
type = str;
|
||||
description = "The endpoint of your web application";
|
||||
example = "https://your_spotify.example.org";
|
||||
};
|
||||
apiEndpoint = mkOption {
|
||||
type = str;
|
||||
description = "The endpoint of your server";
|
||||
default = "http://localhost:8080";
|
||||
};
|
||||
spotifyPublic = mkOption {
|
||||
type = nullOr str;
|
||||
description = mdDoc ''
|
||||
The public key of your Spotify application
|
||||
[Creating the Spotify Application](https://github.com/Yooooomi/your_spotify#creating-the-spotify-application)
|
||||
'';
|
||||
default = null;
|
||||
};
|
||||
spotifySecret = mkOption {
|
||||
type = nullOr str;
|
||||
description = mdDoc ''
|
||||
The secret key of your Spotify application
|
||||
[Creating the Spotify Application](https://github.com/Yooooomi/your_spotify#creating-the-spotify-application)
|
||||
Note that you may want to set this using the `environmentFile` config option to prevent
|
||||
your secret from being world-readable in the nix store.
|
||||
'';
|
||||
default = null;
|
||||
};
|
||||
cors = mkOption {
|
||||
type = nullOr str;
|
||||
description = mdDoc ''
|
||||
List of comma-separated origin allowed, or nothing to allow any origin
|
||||
'';
|
||||
default = null;
|
||||
};
|
||||
maxImportCacheSize = mkOption {
|
||||
type = str;
|
||||
description = mdDoc ''
|
||||
The maximum element in the cache when importing data from an outside source,
|
||||
more cache means less requests to Spotify, resulting in faster imports
|
||||
'';
|
||||
default = "Infinite";
|
||||
};
|
||||
mongoEndpoint = mkOption {
|
||||
type = str;
|
||||
description = mdDoc ''
|
||||
The endpoint of the Mongo database.
|
||||
'';
|
||||
default = "mongodb://localhost:27017/your_spotify";
|
||||
};
|
||||
port = mkOption {
|
||||
type = int;
|
||||
description = "The port of the server";
|
||||
default = 8080;
|
||||
};
|
||||
timezone = mkOption {
|
||||
type = str;
|
||||
description = mdDoc ''
|
||||
The timezone of your stats, only affects read requests since data is saved with UTC time
|
||||
'';
|
||||
default = "Europe/Paris";
|
||||
};
|
||||
logLevel = mkOption {
|
||||
type = str;
|
||||
description = mdDoc ''
|
||||
The log level, debug is useful if you encouter any bugs
|
||||
'';
|
||||
default = "info";
|
||||
};
|
||||
cookieValidityMs = mkOption {
|
||||
type = str;
|
||||
description = mdDoc ''
|
||||
Validity time of the authentication cookie
|
||||
'';
|
||||
default = "1h";
|
||||
};
|
||||
mongoNoAdminRights = mkOption {
|
||||
type = bool;
|
||||
description = mdDoc ''
|
||||
Do not ask for admin right on the Mongo database
|
||||
'';
|
||||
default = true;
|
||||
};
|
||||
};
|
||||
|
||||
environmentFile = mkOption {
|
||||
type = with types; nullOr path;
|
||||
default = null;
|
||||
example = "/var/lib/your_spotify.env";
|
||||
description = lib.mdDoc ''
|
||||
Additional environment file as defined in {manpage}`systemd.exec(5)`.
|
||||
|
||||
Secrets like {env}`SPOTIFY_SECRET`
|
||||
may be passed to the service without adding them to the world-readable Nix store.
|
||||
|
||||
Note that this file needs to be available on the host on which
|
||||
`your_spotify` is running.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
users.users.${cfg.user} = {
|
||||
inherit (cfg) group;
|
||||
isSystemUser = true;
|
||||
};
|
||||
users.groups.${cfg.group} = {};
|
||||
|
||||
systemd.services.your_spotify = {
|
||||
after = ["network.target"];
|
||||
serviceConfig = {
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
EnvironmentFile = [configFile] ++ optional (cfg.environmentFile != null) cfg.environmentFile;
|
||||
ExecStartPre = "${pkgs.your_spotify}/bin/your_spotify_migrate";
|
||||
ExecStart = "${pkgs.your_spotify}/bin/your_spotify_server";
|
||||
LimitNOFILE = "1048576";
|
||||
PrivateTmp = "true";
|
||||
PrivateDevices = "true";
|
||||
ProtectHome = "true";
|
||||
ProtectSystem = "strict";
|
||||
StateDirectory = "your_spotify";
|
||||
StateDirectoryMode = "0700";
|
||||
Restart = "always";
|
||||
};
|
||||
wantedBy = ["multi-user.target"];
|
||||
};
|
||||
services.mongodb.enable = true;
|
||||
};
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
makeWrapper,
|
||||
nodejs,
|
||||
lib,
|
||||
apiEndpoint ? "localhost:8080",
|
||||
}: let
|
||||
version = "1.7.3";
|
||||
src_o = fetchFromGitHub {
|
||||
|
@ -13,6 +14,33 @@
|
|||
rev = "refs/tags/${version}";
|
||||
hash = "sha256-/0xKktywwGcqsuwLytWBJ3O6ADHg1nP6BdMRlkW5ErY=";
|
||||
};
|
||||
client = mkYarnPackage rec {
|
||||
inherit version;
|
||||
pname = "your_spotify_client";
|
||||
src = "${src_o}/client";
|
||||
offlineCache = fetchYarnDeps {
|
||||
yarnLock = src + "/yarn.lock";
|
||||
hash = "sha256-9UfRVv7M9311lesnr19oThYnzB9cK23XNZejJY/Fd24=";
|
||||
};
|
||||
postPatch = ''
|
||||
substituteInPlace tsconfig.json --replace-quiet '"extends": "../tsconfig.json",' ""
|
||||
'';
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
pushd ./deps/client_ts
|
||||
yarn --offline run build
|
||||
popd
|
||||
runHook postBuild
|
||||
'';
|
||||
nativeBuildInputs = [makeWrapper];
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
cp -r ./deps/client_ts/build/* $out
|
||||
substituteInPlace $out/variables-template.js --replace-quiet '__API_ENDPOINT__' "${apiEndpoint}"
|
||||
mv $out/variables-template.js $out/variables.js
|
||||
'';
|
||||
doDist = false;
|
||||
};
|
||||
in
|
||||
mkYarnPackage rec {
|
||||
inherit version;
|
||||
|
@ -23,7 +51,7 @@ in
|
|||
hash = "sha256-3ZK+p3RoHHjPu53MLGSho7lEroZ77vUrZ2CjDwIUQTs=";
|
||||
};
|
||||
postPatch = ''
|
||||
substituteInPlace tsconfig.json --replace '"extends": "../tsconfig.json",' ""
|
||||
substituteInPlace tsconfig.json --replace-quiet '"extends": "../tsconfig.json",' ""
|
||||
'';
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
|
@ -44,4 +72,7 @@ in
|
|||
--add-flags "$out/lib/bin/www.js"
|
||||
'';
|
||||
doDist = false;
|
||||
passthru = {
|
||||
inherit client;
|
||||
};
|
||||
}
|
||||
|
|
1
result
Symbolic link
1
result
Symbolic link
|
@ -0,0 +1 @@
|
|||
/nix/store/7izsg14mhdm1a75an92gdgmpjd0pq1ww-client_ts-1.7.3
|
Binary file not shown.
15
secrets/your_spotify.age
Normal file
15
secrets/your_spotify.age
Normal file
|
@ -0,0 +1,15 @@
|
|||
age-encryption.org/v1
|
||||
-> X25519 ggSXy/sQbB5RIx9y+7b9gx+Osn4CDC1llDZpEurSUlQ
|
||||
eaRyyBSWaPjuY0VQOIKef+jeJrKP/bjn0A3ptY1Yi1c
|
||||
-> piv-p256 XTQkUA A712bv8pNfgCw6BY8uko50ZT6ctKw0aKGMzw21ntFoH9
|
||||
Od/YRbbeDhrsjrydRLpbJ29fb7FVVLNdHrqHIqADD90
|
||||
-> piv-p256 ZFgiIw A7KV41jrxMfKZvJVInfcLH0SX22uRKrGx3Ce1RBK1ba0
|
||||
o6DUQEhob61zHAj6o4l0wPLudMjsg8w2qyanKWn7ZsQ
|
||||
-> piv-p256 5vmPtQ AjgfvHuq6ZktpH4hS5aMnT8OJnFLN0D0+ELXNvuaNyi/
|
||||
ALCCRjJYI6CExt9Di4/p5Gcok5IO/nmuFV5wN7ZJYx0
|
||||
-> piv-p256 ZFgiIw Amn+6yW9k49wRmdkooDqE185U/oZq69mcP2NbOq4l5Ty
|
||||
aQbjyUaiBbf34Fg6HXxgcuVy5s69j4nhmZKelxlGx2Y
|
||||
-> Zb-grease Kj7
|
||||
L0fTYguif9Le7qsrbF1YsD43CgE
|
||||
--- lGupwRLVGo0u7OcziXOmEFo6kA7NsvnMuCLWiIRdqA0
|
||||
#yä^¢uŽf1ʶ¦®í6D²ëA›ôVIÕnhzÅG&ÄÀeGVðýºg&Mìq‰¬TííÉÜÉ]ð<>95ÑÕë/3µ9–ÐÑýõ<C3BD>.ßè=ŒÎôŸw~4»Û§Ãw~H3\c>ù؆Κš¯ñZÙ’
|
Loading…
Reference in a new issue