Merge pull request #293817 from PatrickDaG/your_spotify

nixos/your_spotify: init at 1.10.1
This commit is contained in:
Pol Dellaiera 2024-05-15 16:45:23 +02:00 committed by GitHub
commit 4275fc290a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 390 additions and 0 deletions

View File

@ -15488,6 +15488,15 @@
githubId = 69802930;
name = "patka";
};
patrickdag = {
email = "patrick-nixos@failmail.dev";
github = "PatrickDaG";
githubId = 58092422;
name = "Patrick";
keys = [{
fingerprint = "5E4C 3D74 80C2 35FE 2F0B D23F 7DD6 A72E C899 617D";
}];
};
patricksjackson = {
email = "patrick@jackson.dev";
github = "patricksjackson";

View File

@ -187,6 +187,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
- [xdg-terminal-exec](https://github.com/Vladimir-csp/xdg-terminal-exec), the proposed Default Terminal Execution Specification.
- [your_spotify](https://github.com/Yooooomi/your_spotify), a self hosted Spotify tracking dashboard. Available as [services.your_spotify](#opt-services.your_spotify.enable)
- [RustDesk](https://rustdesk.com), a full-featured open source remote control alternative for self-hosting and security with minimal configuration. Alternative to TeamViewer. Available as [services.rustdesk-server](#opt-services.rustdesk-server.enable).
- [Scrutiny](https://github.com/AnalogJ/scrutiny), a S.M.A.R.T monitoring tool for hard disks with a web frontend. Available as [services.scrutiny](#opt-services.scrutiny.enable).

View File

@ -1431,6 +1431,7 @@
./services/web-apps/windmill.nix
./services/web-apps/wordpress.nix
./services/web-apps/writefreely.nix
./services/web-apps/your_spotify.nix
./services/web-apps/youtrack.nix
./services/web-apps/zabbix.nix
./services/web-apps/zitadel.nix

View File

@ -0,0 +1,191 @@
{
pkgs,
config,
lib,
...
}: let
inherit
(lib)
boolToString
concatMapAttrs
concatStrings
isBool
mapAttrsToList
mkEnableOption
mkIf
mkOption
mkPackageOption
optionalAttrs
types
mkDefault
;
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 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;
description = "Client package to use.";
};
spotifySecretFile = mkOption {
type = path;
description = ''
A file containing the secret key of your Spotify application.
Refer to: [Creating the Spotify Application](https://github.com/Yooooomi/your_spotify#creating-the-spotify-application).
'';
};
settings = mkOption {
description = ''
Your Spotify Configuration. Refer to [Your Spotify](https://github.com/Yooooomi/your_spotify) for definitions and values.
'';
example = lib.literalExpression ''
{
CLIENT_ENDPOINT = "https://example.com";
API_ENDPOINT = "https://api.example.com";
SPOTIFY_PUBLIC = "spotify_client_id";
}
'';
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://`)
'';
example = "https://localhost:3000";
};
SPOTIFY_PUBLIC = mkOption {
type = str;
description = ''
The public client ID of your Spotify application.
Refer to: [Creating the Spotify Application](https://github.com/Yooooomi/your_spotify#creating-the-spotify-application)
'';
};
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;
};
};
};
};
};
config = mkIf cfg.enable {
services.your_spotify.clientPackage = mkDefault (cfg.package.client.override {apiEndpoint = cfg.settings.API_ENDPOINT;});
systemd.services.your_spotify = {
after = ["network.target"];
script = ''
export SPOTIFY_SECRET=$(< "$CREDENTIALS_DIRECTORY/SPOTIFY_SECRET")
${lib.getExe' cfg.package "your_spotify_migrate"}
exec ${lib.getExe cfg.package}
'';
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_SECRET:${cfg.spotifySecretFile}"];
# Hardening
CapabilityBoundingSet = "";
LockPersonality = true;
#MemoryDenyWriteExecute = true; # Leads to coredump because V8 does JIT
PrivateUsers = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProcSubset = "pid";
ProtectSystem = "strict";
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_NETLINK"
];
RestrictNamespaces = true;
RestrictRealtime = true;
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 = ''
add_header Content-Security-Policy "frame-ancestors 'none';" ;
add_header X-Content-Type-Options "nosniff" ;
try_files = $uri $uri/ /index.html ;
'';
};
};
services.mongodb = mkIf cfg.enableLocalDB {
enable = true;
};
};
meta.maintainers = with lib.maintainers; [patrickdag];
}

View File

@ -1042,6 +1042,7 @@ in {
yabar = handleTest ./yabar.nix {};
ydotool = handleTest ./ydotool.nix {};
yggdrasil = handleTest ./yggdrasil.nix {};
your_spotify = handleTest ./your_spotify.nix {};
zammad = handleTest ./zammad.nix {};
zeronet-conservancy = handleTest ./zeronet-conservancy.nix {};
zfs = handleTest ./zfs.nix {};

View File

@ -0,0 +1,33 @@
import ./make-test-python.nix ({pkgs, ...}: {
name = "your_spotify";
meta = with pkgs.lib.maintainers; {
maintainers = [patrickdag];
};
nodes.machine = {
services.your_spotify = {
enable = true;
spotifySecretFile = pkgs.writeText "spotifySecretFile" "deadbeef";
settings = {
CLIENT_ENDPOINT = "http://localhost";
API_ENDPOINT = "http://localhost:3000";
SPOTIFY_PUBLIC = "beefdead";
};
enableLocalDB = true;
nginxVirtualHost = "localhost";
};
};
testScript = ''
machine.wait_for_unit("your_spotify.service")
machine.wait_for_open_port(3000)
machine.wait_for_open_port(80)
out = machine.succeed("curl --fail -X GET 'http://localhost:3000/'")
assert "Hello !" in out
out = machine.succeed("curl --fail -X GET 'http://localhost:80/'")
assert "<title>Your Spotify</title>" in out
'';
})

View File

@ -0,0 +1,58 @@
{
apiEndpoint ? "http://localhost:3000",
fetchYarnDeps,
your_spotify,
mkYarnPackage,
fixup-yarn-lock,
src,
version,
yarn,
}:
mkYarnPackage rec {
inherit version src;
pname = "your_spotify_client";
name = "your_spotify_client-${version}";
packageJSON = ./package.json;
offlineCache = fetchYarnDeps {
yarnLock = src + "/yarn.lock";
hash = "sha256-5SgknaRVzgO2Dzc8MhAaM8UERWMv+PrItzevoWHbWnA=";
};
configurePhase = ''
runHook preConfigure
export HOME=$(mktemp -d)
yarn config --offline set yarn-offline-mirror $offlineCache
fixup-yarn-lock yarn.lock
yarn install --offline --frozen-lockfile --ignore-platform --ignore-scripts --no-progress --non-interactive
patchShebangs node_modules/
runHook postConfigure
'';
buildPhase = ''
runHook preBuild
pushd ./apps/client/
yarn --offline run build
export API_ENDPOINT="${apiEndpoint}"
substituteInPlace scripts/run/variables.sh --replace-quiet '/app/apps/client/' "./"
chmod +x ./scripts/run/variables.sh
patchShebangs --build ./scripts/run/variables.sh
./scripts/run/variables.sh
popd
runHook postBuild
'';
nativeBuildInputs = [yarn fixup-yarn-lock];
installPhase = ''
runHook preInstall
mkdir -p $out
cp -r ./apps/client/build/* $out
runHook postInstall
'';
doDist = false;
meta = {
inherit (your_spotify.meta) homepage changelog description license maintainers;
};
}

View File

@ -0,0 +1,10 @@
{
"name": "@your_spotify/root",
"version": "1.10.1",
"repository": "git@github.com:Yooooomi/your_spotify.git",
"author": "Timothee <timothee.boussus@gmail.com>",
"private": true,
"workspaces": [
"apps/*"
]
}

View File

@ -0,0 +1,85 @@
{
callPackage,
fetchFromGitHub,
fetchYarnDeps,
lib,
makeWrapper,
mkYarnPackage,
nodejs,
fixup-yarn-lock,
yarn,
}: let
version = "1.10.1";
src = fetchFromGitHub {
owner = "Yooooomi";
repo = "your_spotify";
rev = "refs/tags/${version}";
hash = "sha256-e82j2blGxQLWAlBNuAnFvlD9vwMk4/mRI0Vf7vuaPA0=";
};
client = callPackage ./client.nix {inherit src version;};
in
mkYarnPackage rec {
inherit version src;
pname = "your_spotify_server";
name = "your_spotify_server-${version}";
packageJSON = ./package.json;
offlineCache = fetchYarnDeps {
yarnLock = src + "/yarn.lock";
hash = "sha256-5SgknaRVzgO2Dzc8MhAaM8UERWMv+PrItzevoWHbWnA=";
};
configurePhase = ''
runHook preConfigure
export HOME=$(mktemp -d)
yarn config --offline set yarn-offline-mirror $offlineCache
fixup-yarn-lock yarn.lock
runHook postConfigure
'';
buildPhase = ''
runHook preBuild
yarn install --offline --frozen-lockfile --ignore-platform --ignore-scripts --no-progress --non-interactive
patchShebangs node_modules/
pushd ./apps/server/
yarn --offline run build
popd
rm -r node_modules
export NODE_ENV="production"
yarn install --offline --frozen-lockfile --ignore-platform --ignore-scripts --no-progress --non-interactive
patchShebangs node_modules/
runHook postBuild
'';
nativeBuildInputs = [makeWrapper yarn fixup-yarn-lock];
installPhase = ''
runHook preInstall
mkdir -p $out/share/your_spotify
cp -r node_modules $out/share/your_spotify/node_modules
cp -r ./apps/server/{lib,package.json} $out
mkdir -p $out/bin
makeWrapper ${lib.escapeShellArg (lib.getExe nodejs)} "$out/bin/your_spotify_migrate" \
--add-flags "$out/lib/migrations.js" --set NODE_PATH "$out/share/your_spotify/node_modules"
makeWrapper ${lib.escapeShellArg (lib.getExe nodejs)} "$out/bin/your_spotify_server" \
--add-flags "$out/lib/index.js" --set NODE_PATH "$out/share/your_spotify/node_modules"
runHook postInstall
'';
doDist = false;
passthru = {
inherit client;
};
meta = with lib; {
homepage = "https://github.com/Yooooomi/your_spotify";
changelog = "https://github.com/Yooooomi/your_spotify/releases/tag/${version}";
description = "Self-hosted application that tracks what you listen and offers you a dashboard to explore statistics about it";
license = licenses.gpl3Only;
maintainers = with maintainers; [patrickdag];
mainProgram = "your_spotify_server";
};
}