aboutsummaryrefslogblamecommitdiff
path: root/flakes/peertube/flake.nix
blob: 2a594c0ea8be099325532059b96d5de56aba91c8 (plain) (tree)



















































































































































































































































































                                                                                                                                                                   
                                                                                           




























                                                                                          
{
  description = "A free software to take back control of your videos";
  inputs.myuids = {
    url = "https://git.immae.eu/perso/Immae/Config/Nix.git";
    type = "git";
    dir = "flakes/myuids";
  };
  inputs.flake-utils.url = "github:numtide/flake-utils";
  inputs.nixpkgs.url = "github:NixOS/nixpkgs";
  inputs.peertube = {
    url = "github:Chocobozzz/PeerTube/v3.0.1";
    flake = false;
  };

  outputs = { self, myuids, nixpkgs, peertube, flake-utils }: flake-utils.lib.eachSystem ["x86_64-linux"] (system:
    let
      version = (builtins.fromJSON (builtins.readFile ./flake.lock)).nodes.peertube.locked.rev;
      pkgs = import nixpkgs { inherit system; overlays = [
        (self: super: { nodejs = self.nodejs-12_x; })
      ]; };
      inherit (pkgs) callPackage stdenv jq youtube-dl fetchurl nodePackages yarn2nix-moretea;

      patchedSource = stdenv.mkDerivation {
        pname = "peertube";
        inherit version;
        src = peertube;
        phases = [ "unpackPhase" "patchPhase" "installPhase" ];
        patches = [ ./fix_yarn_lock.patch ];
        installPhase = ''
          mkdir $out
          cp -a . $out/
          '';
      };
      yarnModulesConfig = {
        bcrypt = {
          buildInputs = [ nodePackages.node-pre-gyp ];
          postInstall = let
            bcrypt_version = "5.0.0";
            bcrypt_lib = fetchurl {
              url = "https://github.com/kelektiv/node.bcrypt.js/releases/download/v${bcrypt_version}/bcrypt_lib-v${bcrypt_version}-napi-v3-linux-x64-glibc.tar.gz";
              sha256 = "0j3p2px1xb17sw3gpm8l4apljajxxfflal1yy552mhpzhi21wccn";
            };
          in
            ''
              if [ "${bcrypt_version}" != "$(cat package.json | ${jq}/bin/jq -r .version)" ]; then
                echo "Mismatching version please update bcrypt in derivation"
                false
              fi
              mkdir -p lib/binding && tar -C lib/binding -xf ${bcrypt_lib}
              patchShebangs ../node-pre-gyp
              npm run install
            '';
        };
        utf-8-validate = {
          buildInputs = [ nodePackages.node-gyp-build ];
        };
        youtube-dl = {
          postInstall = ''
            mkdir bin
            ln -s ${youtube-dl}/bin/youtube-dl bin/youtube-dl
            cat > bin/details <<EOF
            {"version":"${youtube-dl.version}","path":null,"exec":"youtube-dl"}
            EOF
            '';
        };
      };
      mkYarnModules' = args: (yarn2nix-moretea.mkYarnModules args).overrideAttrs(old: {
        # This hack permits to workaround the fact that the yarn.lock
        # file doesn't respect the semver requirements
        buildPhase = builtins.replaceStrings [" ./package.json"] [" /dev/null; cp deps/*/package.json ."] old.buildPhase;
      });

      server = callPackage ./server.nix {
        inherit version yarnModulesConfig mkYarnModules';
        sources = patchedSource;
      };
      client = callPackage ./client.nix {
        inherit server version yarnModulesConfig mkYarnModules';
        sources = patchedSource;
      };

    in rec {
      packages.peertube = stdenv.mkDerivation rec {
        inherit version;
        pname = "peertube";
        src = patchedSource;
        buildPhase = ''
          ln -s ${server.modules}/node_modules .
          rm -rf dist && cp -a ${server.dist}/dist dist
          rm -rf client/dist && cp -a ${client.dist}/dist client/
          '';
        installPhase = ''
          mkdir $out
          cp -a * $out
          ln -s /tmp $out/.cache
          '';

        meta = {
          description = "A free software to take back control of your videos";

          longDescription = ''
            PeerTube aspires to be a decentralized and free/libre alternative to video
            broadcasting services.
            PeerTube is not meant to become a huge platform that would centralize
            videos from all around the world. Rather, it is a network of
            inter-connected small videos hosters.
            Anyone with a modicum of technical skills can host a PeerTube server, aka
            an instance. Each instance hosts its users and their videos. In this way,
            every instance is created, moderated and maintained independently by
            various administrators.
            You can still watch from your account videos hosted by other instances
            though if the administrator of your instance had previously connected it
            with other instances.
          '';

          license = stdenv.lib.licenses.agpl3Plus;

          homepage = "https://joinpeertube.org/";

          platforms = stdenv.lib.platforms.unix;
        };
      };
      defaultPackage = packages.peertube;
      legacyPackages.peertube = packages.peertube;
      checks = {
        build = defaultPackage;
      } // pkgs.lib.optionalAttrs (builtins.elem system pkgs.lib.systems.doubles.linux) {
        test =
          let testing = import (nixpkgs + "/nixos/lib/testing-python.nix") { inherit system; };
          in testing.makeTest {
            nodes = {
              client = { pkgs, ... }: {};

              server = { pkgs, ... }: {
                imports = [ self.nixosModule ];
                config.networking.firewall.allowedTCPPorts = [ 8888 ];
                config.services.redis.enable = true;
                config.services.postgresql = {
                  enable = true;
                  initialScript = pkgs.writeText "peertube.sql" ''
                    CREATE ROLE "peertube" WITH LOGIN PASSWORD 'peertube';
                    CREATE DATABASE "peertube_prod" WITH OWNER "peertube"
                      TEMPLATE template0
                      LC_COLLATE = "C"
                      LC_CTYPE = "C";
                    \c peertube_prod
                    CREATE EXTENSION unaccent;
                    CREATE EXTENSION pg_trgm;
                  '';
                };
                config.services.peertube.enable = true;
                config.services.peertube.configFile = pkgs.writeText "peertube.conf" ''
                  listen:
                    hostname: '0.0.0.0'
                    port: 8888
                  webserver:
                    https: false
                    hostname: 'localhost.tld'
                    port: 8888
                  database:
                    hostname: 'localhost'
                    port: 5432
                    suffix: '_prod'
                    username: 'peertube'
                    password: 'peertube'
                    pool:
                      max: 5
                  redis:
                    socket: 'localhost'
                    auth: null
                    db: 0
                  storage:
                    tmp: '/var/lib/peertube/storage/tmp/'
                    avatars: '/var/lib/peertube/storage/avatars/'
                    videos: '/var/lib/peertube/storage/videos/'
                    streaming_playlists: '/var/lib/peertube/storage/streaming-playlists/'
                    redundancy: '/var/lib/peertube/storage/videos/'
                    logs: '/var/lib/peertube/storage/logs/'
                    previews: '/var/lib/peertube/storage/previews/'
                    thumbnails: '/var/lib/peertube/storage/thumbnails/'
                    torrents: '/var/lib/peertube/storage/torrents/'
                    captions: '/var/lib/peertube/storage/captions/'
                    cache: '/var/lib/peertube/storage/cache/'
                    plugins: '/var/lib/peertube/storage/plugins/'
                    client_overrides: '/var/lib/peertube/storage/client-overrides/'
                  '';
              };
            };
            testScript = ''
              start_all()
              server.wait_for_unit("peertube.service")
              server.wait_for_open_port(8888)
              client.succeed("curl http://server:8888")
              client.succeed("curl http://server:8888/client/fr-FR/index.html")
            '';
          };
      };
    }
  ) // {
    nixosModule = { lib, pkgs, config, ... }:
      let
        name = "peertube";
        cfg = config.services.peertube;
      in
      {
        options.services.peertube = {
          enable = lib.mkEnableOption "Enable Peertube’s service";
          user = lib.mkOption {
            type = lib.types.str;
            default = name;
            description = "User account under which Peertube runs";
          };
          group = lib.mkOption {
            type = lib.types.str;
            default = name;
            description = "Group under which Peertube runs";
          };
          dataDir = lib.mkOption {
            type = lib.types.path;
            default = "/var/lib/${name}";
            description = ''
              The directory where Peertube stores its data.
            '';
          };
          configFile = lib.mkOption {
            type = lib.types.path;
            description = ''
              The configuration file path for Peertube.
            '';
          };
          package = lib.mkOption {
            type = lib.types.package;
            default = self.defaultPackage."${pkgs.system}";
            description = ''
              Peertube package to use.
            '';
          };
          # Output variables
          systemdStateDirectory = lib.mkOption {
            type = lib.types.str;
            # Use ReadWritePaths= instead if varDir is outside of /var/lib
            default = assert lib.strings.hasPrefix "/var/lib/" cfg.dataDir;
                          lib.strings.removePrefix "/var/lib/" cfg.dataDir;
            description = ''
              Adjusted Peertube data directory for systemd
            '';
            readOnly = true;
          };
        };

        config = lib.mkIf cfg.enable {
          users.users = lib.optionalAttrs (cfg.user == name) {
            "${name}" = {
              uid = myuids.lib.uids.peertube;
              group = cfg.group;
              description = "Peertube user";
              home = cfg.dataDir;
              useDefaultShell = true;
            };
          };
          users.groups = lib.optionalAttrs (cfg.group == name) {
            "${name}" = {
              gid = myuids.lib.gids.peertube;
            };
          };

          systemd.services.peertube = {
            description = "Peertube";
            wantedBy = [ "multi-user.target" ];
            after = [ "network.target" "postgresql.service" ];
            wants = [ "postgresql.service" ];

            environment.NODE_CONFIG_DIR = "${cfg.dataDir}/config";
            environment.NODE_ENV = "production";
            environment.HOME = cfg.package;

            path = [ pkgs.nodejs pkgs.yarn pkgs.bashInteractive pkgs.ffmpeg pkgs.openssl ];

            script = ''
              install -m 0750 -d ${cfg.dataDir}/config
              ln -sf ${cfg.configFile} ${cfg.dataDir}/config/production.yaml
              ln -sf ${cfg.package}/config/default.yaml ${cfg.dataDir}/config/default.yaml
              exec npm run start
            '';

            serviceConfig = {
              User = cfg.user;
              Group = cfg.group;
              WorkingDirectory = cfg.package;
              StateDirectory = cfg.systemdStateDirectory;
              StateDirectoryMode = 0750;
              PrivateTmp = true;
              ProtectHome = true;
              ProtectControlGroups = true;
              Restart = "always";
              Type = "simple";
              TimeoutSec = 60;
            };

            unitConfig.RequiresMountsFor = cfg.dataDir;
          };
        };
      };

  };
}