Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos/zipline: init module #370878

Merged
merged 3 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions nixos/doc/manual/release-notes/rl-2505.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@

- [immich-public-proxy](https://github.com/alangrainger/immich-public-proxy), a proxy for sharing Immich albums without exposing the Immich API. Available as [services.immich-public-proxy](#opt-services.immich-public-proxy.enable).

- [Zipline](https://zipline.diced.sh/), a ShareX/file upload server that is easy to use, packed with features, and with an easy setup. Available as [services.zipline](#opt-services.zipline.enable).

- [mqtt-exporter](https://github.com/kpetremann/mqtt-exporter/), a Prometheus exporter for exposing messages from MQTT. Available as [services.prometheus.exporters.mqtt](#opt-services.prometheus.exporters.mqtt.enable).

- [nvidia-gpu](https://github.com/utkuozdemir/nvidia_gpu_exporter), a Prometheus exporter that scrapes `nvidia-smi` for GPU metrics. Available as [services.prometheus.exporters.nvidia-gpu](#opt-services.prometheus.exporters.nvidia-gpu.enable).
Expand Down
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -1576,6 +1576,7 @@
./services/web-apps/your_spotify.nix
./services/web-apps/youtrack.nix
./services/web-apps/zabbix.nix
./services/web-apps/zipline.nix
./services/web-apps/zitadel.nix
./services/web-servers/agate.nix
./services/web-servers/apache-httpd/default.nix
Expand Down
136 changes: 136 additions & 0 deletions nixos/modules/services/web-apps/zipline.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.zipline;
in
{
meta.maintainers = with lib.maintainers; [ defelo ];

options.services.zipline = {
enable = lib.mkEnableOption "Zipline";

package = lib.mkPackageOption pkgs "zipline" { };

settings = lib.mkOption {
description = ''
Configuration of Zipline. See <https://zipline.diced.sh/docs/config> for more information.
'';
default = { };
example = {
CORE_SECRET = "changethis";
CORE_DATABASE_URL = "postgres://postgres:postgres@postgres/postgres";
CORE_HOST = "0.0.0.0";
CORE_PORT = "3000";
DATASOURCE_LOCAL_DIRECTORY = "/var/lib/zipline/uploads";
};

type = lib.types.submodule {
freeformType =
with lib.types;
attrsOf (oneOf [
str
int
]);

options = {
CORE_HOST = lib.mkOption {
type = lib.types.str;
description = "The hostname to listen on.";
default = "127.0.0.1";
example = "0.0.0.0";
};

CORE_PORT = lib.mkOption {
type = lib.types.port;
description = "The port to listen on.";
default = 3000;
example = 8000;
};
};
};
};

environmentFiles = lib.mkOption {
type = lib.types.listOf lib.types.path;
default = [ ];
example = [ "/run/secrets/zipline.env" ];
description = ''
Files to load environment variables from (in addition to [](#opt-services.zipline.settings)). This is useful to avoid putting secrets into the nix store. See <https://zipline.diced.sh/docs/config> for more information.
'';
};

database.createLocally = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether to enable and configure a local PostgreSQL database server.
'';
};
};

config = lib.mkIf cfg.enable {
services.zipline.settings = {
CORE_DATABASE_URL = lib.mkIf cfg.database.createLocally "postgresql://zipline@localhost/zipline?host=/run/postgresql";
DATASOURCE_LOCAL_DIRECTORY = lib.mkDefault "/var/lib/zipline/uploads"; # created automatically by zipline
};

services.postgresql = lib.mkIf cfg.database.createLocally {
enable = true;
ensureUsers = lib.singleton {
name = "zipline";
ensureDBOwnership = true;
};
ensureDatabases = [ "zipline" ];
};

systemd.services.zipline = {
wantedBy = [ "multi-user.target" ];

wants = [ "network-online.target" ];
after = [ "network-online.target" ] ++ lib.optional cfg.database.createLocally "postgresql.service";
requires = lib.optional cfg.database.createLocally "postgresql.service";

environment = lib.mapAttrs (_: value: toString value) cfg.settings;

serviceConfig = {
User = "zipline";
Group = "zipline";
DynamicUser = true;
StateDirectory = "zipline";
EnvironmentFile = cfg.environmentFiles;
ExecStart = lib.getExe cfg.package;

# Hardening
CapabilityBoundingSet = [ "" ];
DeviceAllow = [ "" ];
LockPersonality = true;
PrivateDevices = true;
PrivateTmp = true;
PrivateUsers = true;
ProcSubset = "pid";
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProtectSystem = "strict";
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
};
};
};
}
1 change: 1 addition & 0 deletions nixos/tests/all-tests.nix
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,7 @@ in {
zeronet-conservancy = handleTest ./zeronet-conservancy.nix {};
zfs = handleTest ./zfs.nix {};
zigbee2mqtt = handleTest ./zigbee2mqtt.nix {};
zipline = handleTest ./zipline.nix {};
zoneminder = handleTest ./zoneminder.nix {};
zookeeper = handleTest ./zookeeper.nix {};
zram-generator = handleTest ./zram-generator.nix {};
Expand Down
48 changes: 48 additions & 0 deletions nixos/tests/zipline.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import ./make-test-python.nix (
{ lib, ... }:
{
name = "zipline";
meta.maintainers = with lib.maintainers; [ defelo ];

nodes.machine = {
services.zipline = {
enable = true;
settings = {
CORE_HOST = "127.0.0.1";
Defelo marked this conversation as resolved.
Show resolved Hide resolved
CORE_PORT = 8000;
};
environmentFiles = [
(builtins.toFile "zipline.env" ''
CORE_SECRET=testsecret
'')
];
};

networking.hosts."127.0.0.1" = [ "zipline.local" ];
};

testScript = ''
import json
import re

machine.wait_for_unit("zipline.service")
machine.wait_for_open_port(8000)

resp = machine.succeed("curl zipline.local:8000/api/auth/login -v -X POST -H 'Content-Type: application/json' -d '{\"username\": \"administrator\", \"password\": \"password\"}' 2>&1")
assert json.loads(resp.splitlines()[-1]) == {"success": True}

assert (cookie := re.search(r"(?m)^< Set-Cookie: ([^;]*)", resp))
resp = machine.succeed(f"curl zipline.local:8000/api/user/token -H 'Cookie: {cookie[1]}' -X PATCH")
token = json.loads(resp)["success"]

resp = machine.succeed(f"curl zipline.local:8000/api/shorten -H 'Authorization: {token}' -X POST -H 'Content-Type: application/json' -d '{{\"url\": \"https://nixos.org/\", \"vanity\": \"nixos\"}}'")
url = json.loads(resp)["url"]
assert url == "http://zipline.local:8000/go/nixos"

resp = machine.succeed(f"curl -I {url}")
assert re.search(r"(?m)^HTTP/1.1 302 Found\r?$", resp)
assert (location := re.search(r"(?mi)^location: (.+?)\r?$", resp))
assert location[1] == "https://nixos.org/"
'';
}
)
154 changes: 154 additions & 0 deletions pkgs/by-name/zi/zipline/package.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
{
lib,
stdenv,
fetchFromGitHub,
nodejs,
yarn-berry,
pkg-config,
makeWrapper,
cacert,
prisma-engines,
openssl,
ffmpeg,
python3,
vips,
nixosTests,
}:

let
environment = {
NEXT_TELEMETRY_DISABLED = "1";
FFMPEG_BIN = lib.getExe ffmpeg;
PRISMA_SCHEMA_ENGINE_BINARY = lib.getExe' prisma-engines "schema-engine";
PRISMA_QUERY_ENGINE_BINARY = lib.getExe' prisma-engines "query-engine";
PRISMA_QUERY_ENGINE_LIBRARY = "${prisma-engines}/lib/libquery_engine.node";
PRISMA_INTROSPECTION_ENGINE_BINARY = lib.getExe' prisma-engines "introspection-engine";
PRISMA_FMT_BINARY = lib.getExe' prisma-engines "prisma-fmt";
};
in

stdenv.mkDerivation (finalAttrs: {
pname = "zipline";
version = "3.7.11";

src = fetchFromGitHub {
owner = "diced";
repo = "zipline";
tag = "v${finalAttrs.version}";
hash = "sha256-sogsPx6vh+1+ew9o3/0B4yU9I/Gllo9XLJqvMvGZ89Q=";
};

patches = [
# Update prisma to match the version in nixpkgs exactly (currently 6.0.1). To create this patch, change the
# versions in `package.json`, then run `nix run nixpkgs#yarn-berry -- install --mode update-lockfile`
# to update `yarn.lock`.
./prisma6.patch
];

# we cannot use fetchYarnDeps because that doesn't support yarn 2/berry lockfiles
yarnOfflineCache = stdenv.mkDerivation {
pname = "yarn-deps";
inherit (finalAttrs) version src patches;

nativeBuildInputs = [ yarn-berry ];

dontInstall = true;

YARN_ENABLE_TELEMETRY = "0";
NODE_EXTRA_CA_CERTS = "${cacert}/etc/ssl/certs/ca-bundle.crt";

configurePhase = ''
export HOME="$NIX_BUILD_TOP"
yarn config set enableGlobalCache false
yarn config set cacheFolder $out
yarn config set --json supportedArchitectures.os '[ "linux" ]'
yarn config set --json supportedArchitectures.cpu '[ "arm64", "x64" ]'
'';

buildPhase = ''
mkdir -p $out
yarn install --immutable --mode skip-build
'';

outputHash = "sha256-kWE6YVhyH5Lk/SO0h624Zq9/6ztoUE3FNzHB0dyl5aI=";
outputHashMode = "recursive";
};

nativeBuildInputs = [
pkg-config
makeWrapper
nodejs
yarn-berry
python3
];

buildInputs = [
vips
openssl
];

YARN_ENABLE_TELEMETRY = "0";

ZIPLINE_DOCKER_BUILD = "true";

configurePhase = ''
export HOME="$NIX_BUILD_TOP"
yarn config set enableGlobalCache false
yarn config set cacheFolder $yarnOfflineCache

${lib.concatStringsSep "\n" (
lib.mapAttrsToList (name: value: "export ${name}=${lib.escapeShellArg value}") environment
)}
'';

buildPhase = ''
mkdir -p $HOME/.node-gyp/${nodejs.version}
echo 9 > $HOME/.node-gyp/${nodejs.version}/installVersion
ln -sfv ${nodejs}/include $HOME/.node-gyp/${nodejs.version}
export npm_config_nodedir=${nodejs}

yarn install --immutable --immutable-cache
yarn build
'';

installPhase = ''
mkdir -p $out/{bin,share/zipline}

cp -r dist public node_modules .next prisma next.config.js mimes.json package.json $out/share/zipline

mkBin() {
makeWrapper ${lib.getExe nodejs} "$out/bin/$1" \
--chdir "$out/share/zipline" \
--prefix PATH : ${lib.makeBinPath [ openssl ]} \
--prefix LD_LIBRARY_PATH : ${lib.makeLibraryPath [ openssl ]} \
${
lib.concatStringsSep " " (
lib.mapAttrsToList (name: value: "--set ${name} ${lib.escapeShellArg value}") environment
)
} \
--add-flags "--enable-source-maps $2"
}

mkBin zipline dist
for script in dist/scripts/*.js; do
mkBin "zipline-$(basename $script .js)" "$script"
done
'';

passthru = {
tests = { inherit (nixosTests) zipline; };
};

meta = {
description = "ShareX/file upload server that is easy to use, packed with features, and with an easy setup";
changelog = "https://github.com/diced/zipline/releases/tag/v${finalAttrs.version}";
homepage = "https://zipline.diced.sh/";
license = lib.licenses.mit;
maintainers = with lib.maintainers; [ defelo ];
mainProgram = "zipline";
platforms = [
"x86_64-linux"
"aarch64-linux"
];
};
})
Loading