init
This commit is contained in:
commit
b704ff1bab
5 changed files with 305 additions and 0 deletions
27
flake.lock
generated
Normal file
27
flake.lock
generated
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1771848320,
|
||||
"narHash": "sha256-0MAd+0mun3K/Ns8JATeHT1sX28faLII5hVLq0L3BdZU=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2fc6539b481e1d2569f25f8799236694180c0993",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
42
flake.nix
Normal file
42
flake.nix
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
description = "Airdrome — modern web UI for Subsonic-compatible music servers";
|
||||
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
|
||||
outputs =
|
||||
{ self, nixpkgs }:
|
||||
let
|
||||
supportedSystems = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-darwin"
|
||||
];
|
||||
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||
in
|
||||
{
|
||||
packages = forAllSystems (
|
||||
system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
airdrome = pkgs.callPackage ./package.nix { };
|
||||
in
|
||||
{
|
||||
inherit airdrome;
|
||||
default = airdrome;
|
||||
}
|
||||
);
|
||||
|
||||
overlays.default = final: prev: {
|
||||
airdrome = final.callPackage ./package.nix { };
|
||||
};
|
||||
|
||||
nixosModules.default = self.nixosModules.airdrome;
|
||||
nixosModules.airdrome =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
imports = [ ./module.nix ];
|
||||
services.airdrome.package = self.packages.${pkgs.stdenv.hostPlatform.system}.airdrome;
|
||||
};
|
||||
};
|
||||
}
|
||||
119
module.nix
Normal file
119
module.nix
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.services.airdrome;
|
||||
|
||||
# Base env object built at Nix eval time (no shell quoting needed).
|
||||
baseEnv =
|
||||
lib.optionalAttrs (cfg.serverUrl != null) { SERVER_URL = cfg.serverUrl; }
|
||||
// lib.optionalAttrs (cfg.username != null) { USERNAME = cfg.username; }
|
||||
// lib.optionalAttrs (cfg.password != null) { PASSWORD = cfg.password; };
|
||||
|
||||
baseEnvJson = pkgs.writeText "airdrome-env.json" (builtins.toJSON baseEnv);
|
||||
in
|
||||
{
|
||||
options.services.airdrome = {
|
||||
enable = lib.mkEnableOption "Airdrome web root assembly";
|
||||
|
||||
package = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
defaultText = lib.literalExpression "pkgs.airdrome";
|
||||
description = "Package to use.";
|
||||
};
|
||||
|
||||
serverUrl = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "Navidrome server URL.";
|
||||
};
|
||||
|
||||
username = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "Auto-login username.";
|
||||
};
|
||||
|
||||
password = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
Plaintext password (ends up in the Nix store — use
|
||||
{option}`passwordFile` instead).
|
||||
'';
|
||||
};
|
||||
|
||||
passwordFile = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "Runtime path to a file containing the password.";
|
||||
};
|
||||
|
||||
webRoot = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/var/lib/airdrome/web";
|
||||
readOnly = true;
|
||||
description = "Assembled web root to point your web server at.";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = !(cfg.password != null && cfg.passwordFile != null);
|
||||
message = "services.airdrome.password and services.airdrome.passwordFile are mutually exclusive.";
|
||||
}
|
||||
];
|
||||
|
||||
systemd.services.airdrome-config = {
|
||||
description = "Assemble Airdrome web root";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
StateDirectory = "airdrome";
|
||||
};
|
||||
|
||||
path = lib.optionals (cfg.passwordFile != null) [ pkgs.jq ];
|
||||
|
||||
script =
|
||||
let
|
||||
webRoot = cfg.webRoot;
|
||||
pkg = cfg.package;
|
||||
in
|
||||
''
|
||||
# 1. Fresh web root
|
||||
rm -rf ${webRoot}
|
||||
mkdir -p ${webRoot}
|
||||
|
||||
# 2. Symlink all package files except env.js
|
||||
for f in ${pkg}/*; do
|
||||
name="$(basename "$f")"
|
||||
[ "$name" = "env.js" ] && continue
|
||||
ln -s "$f" ${webRoot}/"$name"
|
||||
done
|
||||
|
||||
# 3. Build env JSON
|
||||
''
|
||||
+ (
|
||||
if cfg.passwordFile != null then
|
||||
''
|
||||
env_json=$(jq --arg pw "$(cat ${cfg.passwordFile})" '. + {PASSWORD: $pw}' ${baseEnvJson})
|
||||
''
|
||||
else
|
||||
''
|
||||
env_json=$(cat ${baseEnvJson})
|
||||
''
|
||||
)
|
||||
+ ''
|
||||
# 4. Write env.js
|
||||
printf 'window.env = %s;\n' "$env_json" > ${webRoot}/env.js
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
66
package.nix
Normal file
66
package.nix
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
{
|
||||
lib,
|
||||
buildNpmPackage,
|
||||
nodejs_22,
|
||||
fetchFromGitHub,
|
||||
}:
|
||||
|
||||
buildNpmPackage rec {
|
||||
pname = "airdrome";
|
||||
version = "4.3";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "JPGuillemin";
|
||||
repo = "Airdrome";
|
||||
rev = version;
|
||||
hash = "sha256-UGJMbrrX6pBjQJFiQtb1QvECvgVQMk8gDuJJhbFW9HQ=";
|
||||
};
|
||||
|
||||
nodejs = nodejs_22;
|
||||
|
||||
npmDepsHash = "sha256-zgKmXSOdCaMbg520IpT93n3e/6KW+wMUQ94wGfyKXz0=";
|
||||
|
||||
postPatch = ''
|
||||
# Remove vite-plugin-checker — vue-tsc fails in sandbox because
|
||||
# tsconfig lacks the bootstrap-vue-3 alias. Not needed for prod builds.
|
||||
sed -i '/import checker/d' vite.config.mjs
|
||||
sed -i '/checker({/,/}),/d' vite.config.mjs
|
||||
|
||||
# Add resolve alias: bootstrap-vue-3 → bootstrap-vue-next
|
||||
# (upstream imports use the old name but only bootstrap-vue-next is installed)
|
||||
sed -i "s|'@': fileURLToPath(new URL('./src', import.meta.url))|'@': fileURLToPath(new URL('./src', import.meta.url)),\n 'bootstrap-vue-3': 'bootstrap-vue-next'|" vite.config.mjs
|
||||
|
||||
# Fix index.html — add missing <script> tag for env.js (upstream bug)
|
||||
sed -i 's|</head>| <script src="/env.js"></script>\n </head>|' index.html
|
||||
|
||||
# Extend Config interface in auth/service.ts with username + password
|
||||
sed -i 's/serverUrl: string/serverUrl: string\n username: string\n password: string/' src/auth/service.ts
|
||||
sed -i "s|serverUrl: env?.SERVER_URL.*|serverUrl: env?.SERVER_URL \|\| ${"''"},\n username: env?.USERNAME \|\| ${"''"},\n password: env?.PASSWORD \|\| ${"''"},|" src/auth/service.ts
|
||||
|
||||
# Patch Login.vue — auto-login with hardcoded credentials before autoLogin()
|
||||
sed -i '/onMounted(async() => {/a\
|
||||
if (config.serverUrl \&\& config.username \&\& config.password) {\
|
||||
try {\
|
||||
await auth.loginWithPassword(config.serverUrl, config.username, config.password)\
|
||||
store.setLoginSuccess(auth.username, auth.server)\
|
||||
await router.replace(props.returnTo)\
|
||||
return\
|
||||
} catch {}\
|
||||
}' src/auth/Login.vue
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p "$out"
|
||||
cp -r dist/. "$out/"
|
||||
echo 'window.env = {};' > "$out/env.js"
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
meta = {
|
||||
description = "Modern web UI for Subsonic-compatible music servers";
|
||||
homepage = "https://github.com/JPGuillemin/Airdrome";
|
||||
license = lib.licenses.mit;
|
||||
platforms = lib.platforms.all;
|
||||
};
|
||||
}
|
||||
51
update.sh
Executable file
51
update.sh
Executable file
|
|
@ -0,0 +1,51 @@
|
|||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p curl jq nurl nix-prefetch-npm-deps cacert
|
||||
# Update airdrome to the latest release from GitHub.
|
||||
#
|
||||
# Usage: ./update.sh # auto-detect latest tag
|
||||
# ./update.sh 4.3 # pin to a specific tag
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
REPO="JPGuillemin/Airdrome"
|
||||
PKG_FILE="$(cd "$(dirname "$0")" && pwd)/package.nix"
|
||||
|
||||
# --- resolve version --------------------------------------------------------
|
||||
if [[ $# -ge 1 ]]; then
|
||||
VERSION="$1"
|
||||
else
|
||||
VERSION=$(curl -sL "https://api.github.com/repos/$REPO/releases/latest" \
|
||||
| jq -r '.tag_name')
|
||||
if [[ -z "$VERSION" || "$VERSION" == "null" ]]; then
|
||||
echo "error: could not determine latest release" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
echo "version: $VERSION"
|
||||
|
||||
# --- source hash (via nurl) -------------------------------------------------
|
||||
SRC_HASH=$(nurl "https://github.com/$REPO" "$VERSION" --hash)
|
||||
echo "src hash: $SRC_HASH"
|
||||
|
||||
# --- npm deps hash -----------------------------------------------------------
|
||||
# Prefetch source, install deps, and hash them
|
||||
SRC_PATH=$(nix build --no-link --print-out-paths \
|
||||
--impure --expr "
|
||||
(builtins.getFlake \"nixpkgs\").legacyPackages.\${builtins.currentSystem}.fetchFromGitHub {
|
||||
owner = \"JPGuillemin\";
|
||||
repo = \"Airdrome\";
|
||||
rev = \"$VERSION\";
|
||||
hash = \"$SRC_HASH\";
|
||||
}
|
||||
")
|
||||
NPM_DEPS_HASH=$(nix-prefetch-npm-deps "$SRC_PATH/package-lock.json")
|
||||
echo "npm deps hash: $NPM_DEPS_HASH"
|
||||
|
||||
# --- patch package.nix -------------------------------------------------------
|
||||
sed -i -E "
|
||||
s|(version = \").*(\";)|\1${VERSION}\2|
|
||||
s|(hash = \").*(\";)|\1${SRC_HASH}\2|
|
||||
s|(npmDepsHash = \").*(\";)|\1${NPM_DEPS_HASH}\2|
|
||||
" "$PKG_FILE"
|
||||
|
||||
echo "updated $PKG_FILE"
|
||||
Loading…
Add table
Add a link
Reference in a new issue