230 lines
7.1 KiB
Nix
230 lines
7.1 KiB
Nix
{
|
|
pkgs,
|
|
config,
|
|
lib,
|
|
...
|
|
}: let
|
|
inherit
|
|
(lib)
|
|
boolToString
|
|
concatMapAttrs
|
|
concatStrings
|
|
isBool
|
|
mapAttrsToList
|
|
mkEnableOption
|
|
mkIf
|
|
mkOption
|
|
mkPackageOption
|
|
optionalAttrs
|
|
types
|
|
;
|
|
cfg = config.services.your_spotify;
|
|
|
|
configEnv = concatMapAttrs (name: value:
|
|
optionalAttrs (value != null) {
|
|
${name} =
|
|
if isBool value
|
|
then boolToString value
|
|
else toString value;
|
|
})
|
|
cfg.settings;
|
|
|
|
configFile = pkgs.writeText "your_spotify.env" (concatStrings (mapAttrsToList (name: value: "${name}=${value}\n") configEnv));
|
|
in {
|
|
options.services.your_spotify = let
|
|
inherit (types) nullOr port str path bool package;
|
|
in {
|
|
enable = mkEnableOption "your_spotify";
|
|
|
|
enableLocalDB = mkEnableOption "a local mongodb instance";
|
|
nginxVirtualHost = mkOption {
|
|
type = nullOr str;
|
|
default = null;
|
|
description = ''
|
|
If set creates an nginx virtual host for the client.
|
|
In most cases this should be the CLIENT_ENDPOINT without
|
|
protocol prefix.
|
|
'';
|
|
};
|
|
|
|
package = mkPackageOption pkgs "your_spotify" {};
|
|
|
|
clientPackage = mkOption {
|
|
type = package;
|
|
default = cfg.package.client.override {apiEndpoint = cfg.settings.API_ENDPOINT;};
|
|
description = "Client package to use.";
|
|
};
|
|
spotifyPublicFile = mkOption {
|
|
type = path;
|
|
description = ''
|
|
The public key of your Spotify application
|
|
[Creating the Spotify Application](https://github.com/Yooooomi/your_spotify#creating-the-spotify-application)
|
|
'';
|
|
};
|
|
|
|
spotifySecretFile = mkOption {
|
|
type = path;
|
|
description = ''
|
|
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.
|
|
'';
|
|
};
|
|
|
|
settings = mkOption {
|
|
type = types.submodule {
|
|
freeformType = types.attrsOf types.str;
|
|
options = {
|
|
CLIENT_ENDPOINT = mkOption {
|
|
type = str;
|
|
description = ''
|
|
The endpoint of your web application
|
|
Has to include a protocol Prefix (e.g. `http://`)
|
|
'';
|
|
example = "https://your_spotify.example.org";
|
|
};
|
|
API_ENDPOINT = mkOption {
|
|
type = str;
|
|
description = ''
|
|
The endpoint of your server
|
|
This api has to be reachable from the device you use the website from not from the server.
|
|
This means that for example you may need two nginx virtual hosts if you want to expose this on the
|
|
internet.
|
|
Has to include a protocol Prefix (e.g. `http://`)
|
|
'';
|
|
default = "https://localhost:3000";
|
|
};
|
|
CORS = mkOption {
|
|
type = nullOr str;
|
|
description = ''
|
|
List of comma-separated origin allowed, or nothing to allow any origin
|
|
'';
|
|
default = null;
|
|
};
|
|
MAX_IMPORT_CACHESIZE = mkOption {
|
|
type = str;
|
|
description = ''
|
|
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";
|
|
};
|
|
MONGO_ENDPOINT = mkOption {
|
|
type = str;
|
|
description = ''
|
|
The endpoint of the Mongo database.
|
|
'';
|
|
default = "mongodb://localhost:27017/your_spotify";
|
|
};
|
|
PORT = mkOption {
|
|
type = port;
|
|
description = "The port of the api server";
|
|
default = 3000;
|
|
};
|
|
TIMEZONE = mkOption {
|
|
type = str;
|
|
description = ''
|
|
The timezone of your stats, only affects read requests since data is saved with UTC time
|
|
'';
|
|
default = "Europe/Paris";
|
|
};
|
|
LOG_LEVEL = mkOption {
|
|
type = str;
|
|
description = ''
|
|
The log level, debug is useful if you encouter any bugs
|
|
'';
|
|
default = "info";
|
|
};
|
|
COOKIE_VALIDITY_MS = mkOption {
|
|
type = str;
|
|
description = ''
|
|
Validity time of the authentication cookie
|
|
'';
|
|
default = "1h";
|
|
};
|
|
MONGO_NO_ADMIN_RIGHTS = mkOption {
|
|
type = bool;
|
|
description = ''
|
|
Do not ask for admin right on the Mongo database
|
|
'';
|
|
default = false;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
systemd.services.your_spotify = {
|
|
after = ["network.target"];
|
|
script = ''
|
|
export SPOTIFY_PUBLIC=$(< "$CREDENTIALS_DIRECTORY/SPOTIFY_PUBLIC")
|
|
export SPOTIFY_SECRET=$(< "$CREDENTIALS_DIRECTORY/SPOTIFY_SECRET")
|
|
${pkgs.your_spotify}/bin/your_spotify_migrate
|
|
exec ${pkgs.your_spotify}/bin/your_spotify_server
|
|
'';
|
|
serviceConfig = {
|
|
User = "your_spotify";
|
|
Group = "your_spotify";
|
|
DynamicUser = true;
|
|
EnvironmentFile = [configFile];
|
|
StateDirectory = "your_spotify";
|
|
LimitNOFILE = "1048576";
|
|
PrivateTmp = true;
|
|
PrivateDevices = true;
|
|
StateDirectoryMode = "0700";
|
|
Restart = "always";
|
|
|
|
LoadCredential = ["SPOTIFY_PUBLIC:${cfg.spotifyPublicFile}" "SPOTIFY_SECRET:${cfg.spotifySecretFile}"];
|
|
|
|
# Hardening
|
|
CapabilityBoundingSet = "";
|
|
LockPersonality = true;
|
|
#MemoryDenyWriteExecute = true;
|
|
#NoNewPrivileges = true; # Implied by DynamicUser
|
|
PrivateUsers = true;
|
|
#PrivateTmp = true; # Implied by DynamicUser
|
|
ProtectClock = true;
|
|
ProtectControlGroups = true;
|
|
ProtectHome = true;
|
|
ProtectHostname = false; # breaks bwrap
|
|
ProtectKernelLogs = false; # breaks bwrap
|
|
ProtectKernelModules = true;
|
|
ProtectKernelTunables = false; # breaks bwrap
|
|
ProtectProc = "invisible";
|
|
ProcSubset = "all"; # Using "pid" breaks bwrap
|
|
ProtectSystem = "strict";
|
|
#RemoveIPC = true; # Implied by DynamicUser
|
|
RestrictAddressFamilies = [
|
|
"AF_INET"
|
|
"AF_INET6"
|
|
"AF_NETLINK"
|
|
];
|
|
RestrictNamespaces = true;
|
|
RestrictRealtime = true;
|
|
#RestrictSUIDSGID = true; # Implied by DynamicUser
|
|
SystemCallArchitectures = "native";
|
|
SystemCallFilter = [
|
|
"@system-service"
|
|
"@pkey"
|
|
];
|
|
UMask = "0077";
|
|
};
|
|
wantedBy = ["multi-user.target"];
|
|
};
|
|
services.nginx = mkIf (cfg.nginxVirtualHost != null) {
|
|
enable = true;
|
|
virtualHosts.${cfg.nginxVirtualHost} = {
|
|
root = cfg.clientPackage;
|
|
locations."/".extraConfig = ''
|
|
try_files = $uri $uri/ /index.html ;
|
|
'';
|
|
};
|
|
};
|
|
services.mongodb = mkIf cfg.enableLocalDB {
|
|
enable = true;
|
|
};
|
|
};
|
|
}
|