feat: your spotify modules
This commit is contained in:
parent
dfc62da346
commit
6fe5d90427
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
.pre-commit-config.yaml
|
.pre-commit-config.yaml
|
||||||
.direnv
|
.direnv
|
||||||
|
result
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
{config, ...}: {
|
{config, ...}: {
|
||||||
imports = [./your_spotify_m.nix];
|
imports = [./your_spotify_m.nix];
|
||||||
age.secrets.spotify = {
|
age.secrets.spotify = {
|
||||||
owner = config.services.your_spotify.user;
|
inherit (config.services.your_spotify) user group;
|
||||||
group = config.services.your_spotify.group;
|
|
||||||
rekeyFile = ../../secrets/your_spotify.age;
|
rekeyFile = ../../secrets/your_spotify.age;
|
||||||
};
|
};
|
||||||
services.your_spotify = {
|
services.your_spotify = {
|
||||||
enable = true;
|
enable = true;
|
||||||
config = {
|
config = {
|
||||||
clientEndpoint = "https://spotify.${config.secrets.secrets.global.domains.web}";
|
clientEndpoint = "https://spotify.${config.secrets.secrets.global.domains.web}";
|
||||||
|
apiEndpoint = "https://api.spotify.${config.secrets.secrets.global.domains.web}";
|
||||||
};
|
};
|
||||||
environmentFile = config.age.secrets.spotify.path;
|
environmentFile = config.age.secrets.spotify.path;
|
||||||
};
|
};
|
||||||
environment.persistence."/persist".directories = [
|
environment.persistence."/persist".directories = [
|
||||||
{
|
{
|
||||||
|
inherit (config.services.mongodb) user;
|
||||||
directory = config.services.mongodb.dbpath;
|
directory = config.services.mongodb.dbpath;
|
||||||
user = config.services.mongodb.user;
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,52 +9,21 @@
|
||||||
boolToString
|
boolToString
|
||||||
concatMapAttrs
|
concatMapAttrs
|
||||||
concatStrings
|
concatStrings
|
||||||
elem
|
|
||||||
foldl'
|
|
||||||
head
|
|
||||||
isBool
|
isBool
|
||||||
isList
|
|
||||||
lowerChars
|
|
||||||
mapAttrsToList
|
mapAttrsToList
|
||||||
mdDoc
|
|
||||||
mkEnableOption
|
mkEnableOption
|
||||||
mkIf
|
mkIf
|
||||||
mkOption
|
mkOption
|
||||||
mkPackageOption
|
mkPackageOption
|
||||||
optional
|
optional
|
||||||
optionalAttrs
|
optionalAttrs
|
||||||
optionalString
|
|
||||||
stringLength
|
|
||||||
substring
|
|
||||||
toUpper
|
|
||||||
types
|
types
|
||||||
;
|
;
|
||||||
cfg = config.services.your_spotify;
|
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:
|
configEnv = concatMapAttrs (name: value:
|
||||||
optionalAttrs (value != null) {
|
optionalAttrs (value != null) {
|
||||||
${nameToEnvVar name} =
|
name =
|
||||||
if isBool value
|
if isBool value
|
||||||
then boolToString value
|
then boolToString value
|
||||||
else toString value;
|
else toString value;
|
||||||
|
@ -64,112 +33,114 @@
|
||||||
configFile = pkgs.writeText "your_spotify.env" (concatStrings (mapAttrsToList (name: value: "${name}=${value}\n") configEnv));
|
configFile = pkgs.writeText "your_spotify.env" (concatStrings (mapAttrsToList (name: value: "${name}=${value}\n") configEnv));
|
||||||
in {
|
in {
|
||||||
options.services.your_spotify = let
|
options.services.your_spotify = let
|
||||||
inherit (types) nullOr int str bool package;
|
inherit (types) nullOr port str bool package;
|
||||||
in {
|
in {
|
||||||
enable = mkEnableOption (lib.mdDoc "your_spotify");
|
enable = mkEnableOption "your_spotify";
|
||||||
user = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "your_spotify";
|
|
||||||
description = lib.mdDoc "User account under which your_spotify runs.";
|
|
||||||
};
|
|
||||||
|
|
||||||
group = mkOption {
|
enableLocalDB = mkEnableOption "a local mongodb instance";
|
||||||
type = types.str;
|
enableNginxVirtualHost = mkEnableOption "a ngnix virtual Host for your client";
|
||||||
default = "your_spotify";
|
|
||||||
description = lib.mdDoc "Group account under which your_spotify runs.";
|
|
||||||
};
|
|
||||||
|
|
||||||
package = mkPackageOption pkgs "your_spotify" {};
|
package = mkPackageOption pkgs "your_spotify" {};
|
||||||
|
|
||||||
clientPackage = mkOption {
|
clientPackage = mkOption {
|
||||||
type = package;
|
type = package;
|
||||||
default = cfg.package.client.override {apiEndpoint = cfg.config.apiEndpoint;};
|
default = cfg.package.client.override {inherit (cfg.config) apiEndpoint;};
|
||||||
description = lib.mdDoc "Client package to use.";
|
description = "Client package to use.";
|
||||||
};
|
};
|
||||||
|
|
||||||
config = {
|
settings = mkOption {
|
||||||
clientEndpoint = mkOption {
|
type = types.submodule {
|
||||||
type = str;
|
freeformType = types.attrOf types.str;
|
||||||
description = "The endpoint of your web application";
|
options = {
|
||||||
example = "https://your_spotify.example.org";
|
clientEndpoint = mkOption {
|
||||||
};
|
type = str;
|
||||||
apiEndpoint = mkOption {
|
description = "The endpoint of your web application";
|
||||||
type = str;
|
example = "https://your_spotify.example.org";
|
||||||
description = "The endpoint of your server";
|
};
|
||||||
default = "http://localhost:8080";
|
apiEndpoint = mkOption {
|
||||||
};
|
type = str;
|
||||||
spotifyPublic = mkOption {
|
description = ''
|
||||||
type = nullOr str;
|
The endpoint of your server
|
||||||
description = mdDoc ''
|
This api has to be reachable from the device you use the website from not from the server.
|
||||||
The public key of your Spotify application
|
This means that for example you may need two nginx virtual hosts if you want to expose this on the
|
||||||
[Creating the Spotify Application](https://github.com/Yooooomi/your_spotify#creating-the-spotify-application)
|
internet.
|
||||||
'';
|
'';
|
||||||
default = null;
|
default = "http://localhost:8080";
|
||||||
};
|
};
|
||||||
spotifySecret = mkOption {
|
spotifyPublic = mkOption {
|
||||||
type = nullOr str;
|
type = nullOr str;
|
||||||
description = mdDoc ''
|
description = ''
|
||||||
The secret key of your Spotify application
|
The public key of your Spotify application
|
||||||
[Creating the Spotify Application](https://github.com/Yooooomi/your_spotify#creating-the-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;
|
||||||
'';
|
};
|
||||||
default = null;
|
spotifySecret = mkOption {
|
||||||
};
|
type = nullOr str;
|
||||||
cors = mkOption {
|
description = ''
|
||||||
type = nullOr str;
|
The secret key of your Spotify application
|
||||||
description = mdDoc ''
|
[Creating the Spotify Application](https://github.com/Yooooomi/your_spotify#creating-the-spotify-application)
|
||||||
List of comma-separated origin allowed, or nothing to allow any origin
|
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;
|
'';
|
||||||
};
|
default = null;
|
||||||
maxImportCacheSize = mkOption {
|
};
|
||||||
type = str;
|
cors = mkOption {
|
||||||
description = mdDoc ''
|
type = nullOr str;
|
||||||
The maximum element in the cache when importing data from an outside source,
|
description = ''
|
||||||
more cache means less requests to Spotify, resulting in faster imports
|
List of comma-separated origin allowed, or nothing to allow any origin
|
||||||
'';
|
'';
|
||||||
default = "Infinite";
|
default = null;
|
||||||
};
|
};
|
||||||
mongoEndpoint = mkOption {
|
maxImportCacheSize = mkOption {
|
||||||
type = str;
|
type = str;
|
||||||
description = mdDoc ''
|
description = ''
|
||||||
The endpoint of the Mongo database.
|
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 = "mongodb://localhost:27017/your_spotify";
|
'';
|
||||||
};
|
default = "Infinite";
|
||||||
port = mkOption {
|
};
|
||||||
type = int;
|
mongoEndpoint = mkOption {
|
||||||
description = "The port of the server";
|
type = str;
|
||||||
default = 8080;
|
description = ''
|
||||||
};
|
The endpoint of the Mongo database.
|
||||||
timezone = mkOption {
|
'';
|
||||||
type = str;
|
default = "mongodb://localhost:27017/your_spotify";
|
||||||
description = mdDoc ''
|
};
|
||||||
The timezone of your stats, only affects read requests since data is saved with UTC time
|
port = mkOption {
|
||||||
'';
|
type = port;
|
||||||
default = "Europe/Paris";
|
description = "The port of the api server";
|
||||||
};
|
default = 8080;
|
||||||
logLevel = mkOption {
|
};
|
||||||
type = str;
|
timezone = mkOption {
|
||||||
description = mdDoc ''
|
type = str;
|
||||||
The log level, debug is useful if you encouter any bugs
|
description = ''
|
||||||
'';
|
The timezone of your stats, only affects read requests since data is saved with UTC time
|
||||||
default = "info";
|
'';
|
||||||
};
|
default = "Europe/Paris";
|
||||||
cookieValidityMs = mkOption {
|
};
|
||||||
type = str;
|
logLevel = mkOption {
|
||||||
description = mdDoc ''
|
type = str;
|
||||||
Validity time of the authentication cookie
|
description = ''
|
||||||
'';
|
The log level, debug is useful if you encouter any bugs
|
||||||
default = "1h";
|
'';
|
||||||
};
|
default = "info";
|
||||||
mongoNoAdminRights = mkOption {
|
};
|
||||||
type = bool;
|
cookieValidityMs = mkOption {
|
||||||
description = mdDoc ''
|
type = str;
|
||||||
Do not ask for admin right on the Mongo database
|
description = ''
|
||||||
'';
|
Validity time of the authentication cookie
|
||||||
default = true;
|
'';
|
||||||
|
default = "1h";
|
||||||
|
};
|
||||||
|
mongoNoAdminRights = mkOption {
|
||||||
|
type = bool;
|
||||||
|
description = ''
|
||||||
|
Do not ask for admin right on the Mongo database
|
||||||
|
'';
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -177,7 +148,7 @@ in {
|
||||||
type = with types; nullOr path;
|
type = with types; nullOr path;
|
||||||
default = null;
|
default = null;
|
||||||
example = "/var/lib/your_spotify.env";
|
example = "/var/lib/your_spotify.env";
|
||||||
description = lib.mdDoc ''
|
description = ''
|
||||||
Additional environment file as defined in {manpage}`systemd.exec(5)`.
|
Additional environment file as defined in {manpage}`systemd.exec(5)`.
|
||||||
|
|
||||||
Secrets like {env}`SPOTIFY_SECRET`
|
Secrets like {env}`SPOTIFY_SECRET`
|
||||||
|
@ -190,24 +161,19 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
users.users.${cfg.user} = {
|
|
||||||
inherit (cfg) group;
|
|
||||||
isSystemUser = true;
|
|
||||||
};
|
|
||||||
users.groups.${cfg.group} = {};
|
|
||||||
|
|
||||||
systemd.services.your_spotify = {
|
systemd.services.your_spotify = {
|
||||||
after = ["network.target"];
|
after = ["network.target"];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
User = cfg.user;
|
User = "your_spotify";
|
||||||
Group = cfg.group;
|
Group = "your_spotify";
|
||||||
|
DynamicUser = true;
|
||||||
EnvironmentFile = [configFile] ++ optional (cfg.environmentFile != null) cfg.environmentFile;
|
EnvironmentFile = [configFile] ++ optional (cfg.environmentFile != null) cfg.environmentFile;
|
||||||
ExecStartPre = "${pkgs.your_spotify}/bin/your_spotify_migrate";
|
ExecStartPre = "${pkgs.your_spotify}/bin/your_spotify_migrate";
|
||||||
ExecStart = "${pkgs.your_spotify}/bin/your_spotify_server";
|
ExecStart = "${pkgs.your_spotify}/bin/your_spotify_server";
|
||||||
LimitNOFILE = "1048576";
|
LimitNOFILE = "1048576";
|
||||||
PrivateTmp = "true";
|
PrivateTmp = true;
|
||||||
PrivateDevices = "true";
|
PrivateDevices = true;
|
||||||
ProtectHome = "true";
|
ProtectHome = true;
|
||||||
ProtectSystem = "strict";
|
ProtectSystem = "strict";
|
||||||
StateDirectory = "your_spotify";
|
StateDirectory = "your_spotify";
|
||||||
StateDirectoryMode = "0700";
|
StateDirectoryMode = "0700";
|
||||||
|
@ -215,6 +181,15 @@ in {
|
||||||
};
|
};
|
||||||
wantedBy = ["multi-user.target"];
|
wantedBy = ["multi-user.target"];
|
||||||
};
|
};
|
||||||
services.mongodb.enable = true;
|
services.nginx = {
|
||||||
|
enable = true;
|
||||||
|
virtualHosts.${cfg.clientEndpoint}.root = cfg.clientPackage;
|
||||||
|
};
|
||||||
|
services.mongodb = mkIf cfg.enableLocalDB {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
# nginx gaten
|
||||||
|
# systemd hardening(e.g. esphome)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue