Compare commits
1 commit
Author | SHA1 | Date | |
---|---|---|---|
|
05c4fe4823 |
6 changed files with 668 additions and 0 deletions
|
@ -15,6 +15,7 @@
|
||||||
# ./k8s.nix
|
# ./k8s.nix
|
||||||
./services/docker.nix
|
./services/docker.nix
|
||||||
./services/gitlab-runner.nix
|
./services/gitlab-runner.nix
|
||||||
|
./services/funkwhale.nix
|
||||||
];
|
];
|
||||||
boot.loader.grub.enable = true;
|
boot.loader.grub.enable = true;
|
||||||
boot.loader.grub.version = 2;
|
boot.loader.grub.version = 2;
|
||||||
|
|
55
configuration/hosts/hainich/services/funkwhale.nix
Normal file
55
configuration/hosts/hainich/services/funkwhale.nix
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
containers.funkwhale = {
|
||||||
|
inherit pkgs;
|
||||||
|
privateNetwork = true;
|
||||||
|
hostAddress = "192.168.100.1";
|
||||||
|
localAddress = "192.168.100.4";
|
||||||
|
autoStart = true;
|
||||||
|
config = { config, lib, pkgs, ... }: {
|
||||||
|
imports = [
|
||||||
|
../../../../modules
|
||||||
|
];
|
||||||
|
services.coredns = {
|
||||||
|
enable = true;
|
||||||
|
config = ''
|
||||||
|
.:53 {
|
||||||
|
forward . 1.1.1.1
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
networking.firewall.enable = false;
|
||||||
|
services.funkwhale = {
|
||||||
|
enable = true;
|
||||||
|
apiIp = "192.168.100.4";
|
||||||
|
hostname = "funkwhale.hacc.media";
|
||||||
|
protocol = "https";
|
||||||
|
defaultFromEmail = "funkwhale@hacc.media";
|
||||||
|
api.djangoSecretKey = "TwsgANNKid+HZ0HwhR/FgTcxFIW6sZ8s4n7HxV6zPdU=";
|
||||||
|
};
|
||||||
|
services.nginx.virtualHosts."funkwhale.hacc.media" = {
|
||||||
|
enableACME = lib.mkForce false;
|
||||||
|
forceSSL = lib.mkForce false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
services.nginx.virtualHosts."funkwhale.hacc.media" = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://192.168.100.4";
|
||||||
|
extraConfig = ''
|
||||||
|
proxy_pass_request_headers on;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Forwarded-Host $http_host;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection $http_connection;
|
||||||
|
proxy_buffering off;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -8,5 +8,6 @@ let
|
||||||
in {
|
in {
|
||||||
imports = [
|
imports = [
|
||||||
"${immaeNix}/modules/webapps/peertube.nix"
|
"${immaeNix}/modules/webapps/peertube.nix"
|
||||||
|
./funkwhale
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
563
modules/funkwhale/default.nix
Normal file
563
modules/funkwhale/default.nix
Normal file
|
@ -0,0 +1,563 @@
|
||||||
|
{config, lib, pkgs, ...}:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
django-cacheops = with final; with pkgs.python3.pkgs; ( buildPythonPackage rec {
|
||||||
|
pname = "django-cacheops";
|
||||||
|
version = "5.1";
|
||||||
|
|
||||||
|
src = fetchPypi {
|
||||||
|
inherit pname version;
|
||||||
|
sha256 = "sha256-1YUc178whzhKH87PqN3bj1UDDu39b98SciW3W8oPmd0=";
|
||||||
|
};
|
||||||
|
propagatedBuildInputs = [ django redis six funcy ];
|
||||||
|
doCheck = false;
|
||||||
|
});
|
||||||
|
pythonEnv = (pkgs.python3.override {
|
||||||
|
packageOverrides = self: super: rec {
|
||||||
|
django = self.django_2_2;
|
||||||
|
};
|
||||||
|
}).withPackages (ps: [
|
||||||
|
django-cacheops
|
||||||
|
ps.aioredis
|
||||||
|
ps.aiohttp
|
||||||
|
ps.arrow
|
||||||
|
ps.autobahn
|
||||||
|
ps.av
|
||||||
|
ps.bleach
|
||||||
|
ps.boto3
|
||||||
|
ps.celery
|
||||||
|
ps.channels
|
||||||
|
ps.channels-redis
|
||||||
|
ps.click
|
||||||
|
ps.django
|
||||||
|
ps.django-allauth
|
||||||
|
ps.django-auth-ldap
|
||||||
|
ps.django-oauth-toolkit
|
||||||
|
ps.django-cleanup
|
||||||
|
ps.django-cors-headers
|
||||||
|
ps.django-dynamic-preferences
|
||||||
|
ps.django_environ
|
||||||
|
ps.django-filter
|
||||||
|
ps.django_redis
|
||||||
|
ps.django-rest-auth
|
||||||
|
ps.djangorestframework
|
||||||
|
ps.djangorestframework-jwt
|
||||||
|
ps.django-storages
|
||||||
|
ps.django_taggit
|
||||||
|
ps.django-versatileimagefield
|
||||||
|
ps.feedparser
|
||||||
|
ps.gunicorn
|
||||||
|
ps.kombu
|
||||||
|
ps.ldap
|
||||||
|
ps.markdown
|
||||||
|
ps.mutagen
|
||||||
|
ps.musicbrainzngs
|
||||||
|
ps.pillow
|
||||||
|
ps.pendulum
|
||||||
|
ps.persisting-theory
|
||||||
|
ps.psycopg2
|
||||||
|
ps.pyacoustid
|
||||||
|
ps.pydub
|
||||||
|
ps.PyLD
|
||||||
|
ps.pymemoize
|
||||||
|
ps.pyopenssl
|
||||||
|
ps.python_magic
|
||||||
|
ps.pytz
|
||||||
|
ps.redis
|
||||||
|
ps.requests
|
||||||
|
ps.requests-http-signature
|
||||||
|
ps.service-identity
|
||||||
|
ps.unidecode
|
||||||
|
ps.unicode-slugify
|
||||||
|
ps.uvicorn
|
||||||
|
ps.watchdog
|
||||||
|
]);
|
||||||
|
cfg = config.services.funkwhale;
|
||||||
|
databasePassword = if (cfg.database.passwordFile != null)
|
||||||
|
then builtins.readFile cfg.database.passwordFile
|
||||||
|
else cfg.database.password;
|
||||||
|
databaseUrl = if (cfg.database.createLocally && cfg.database.socket != null)
|
||||||
|
then "postgresql:///${cfg.database.name}?host=${cfg.database.socket}"
|
||||||
|
else "postgresql://${cfg.database.user}:${databasePassword}@${cfg.database.host}:${toString cfg.database.port}/${cfg.database.name}";
|
||||||
|
|
||||||
|
funkwhaleEnvironment = [
|
||||||
|
"FUNKWHALE_URL=${cfg.hostname}"
|
||||||
|
"FUNKWHALE_HOSTNAME=${cfg.hostname}"
|
||||||
|
"FUNKWHALE_PROTOCOL=${cfg.protocol}"
|
||||||
|
"EMAIL_CONFIG=${cfg.emailConfig}"
|
||||||
|
"DEFAULT_FROM_EMAIL=${cfg.defaultFromEmail}"
|
||||||
|
"REVERSE_PROXY_TYPE=nginx"
|
||||||
|
"DATABASE_URL=${databaseUrl}"
|
||||||
|
"CACHE_URL=redis://localhost:${toString config.services.redis.port}/0"
|
||||||
|
"MEDIA_ROOT=${cfg.api.mediaRoot}"
|
||||||
|
"STATIC_ROOT=${cfg.api.staticRoot}"
|
||||||
|
"DJANGO_SECRET_KEY=${cfg.api.djangoSecretKey}"
|
||||||
|
"RAVEN_ENABLED=${boolToString cfg.enableRaven}"
|
||||||
|
"RAVEN_DSN=${cfg.ravenDsn}"
|
||||||
|
"MUSIC_DIRECTORY_PATH=${cfg.musicDirectoryPath}"
|
||||||
|
"MUSIC_DIRECTORY_SERVE_PATH=${cfg.musicDirectoryPath}"
|
||||||
|
"FUNKWHALE_FRONTEND_PATH=${cfg.dataDir}/front/dist"
|
||||||
|
];
|
||||||
|
funkwhaleEnvFileData = builtins.concatStringsSep "\n" funkwhaleEnvironment;
|
||||||
|
funkwhaleEnvScriptData = builtins.concatStringsSep " " funkwhaleEnvironment;
|
||||||
|
|
||||||
|
funkwhaleEnvFile = pkgs.writeText "funkwhale.env" funkwhaleEnvFileData;
|
||||||
|
funkwhaleEnv = {
|
||||||
|
ENV_FILE = "${funkwhaleEnvFile}";
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
|
||||||
|
options = {
|
||||||
|
services.funkwhale = {
|
||||||
|
enable = mkEnableOption "funkwhale";
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "funkwhale";
|
||||||
|
description = "User under which Funkwhale is ran.";
|
||||||
|
};
|
||||||
|
|
||||||
|
group = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "funkwhale";
|
||||||
|
description = "Group under which Funkwhale is ran.";
|
||||||
|
};
|
||||||
|
|
||||||
|
database = {
|
||||||
|
host = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "localhost";
|
||||||
|
description = "Database host address.";
|
||||||
|
};
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
type = types.int;
|
||||||
|
default = 5432;
|
||||||
|
defaultText = "5432";
|
||||||
|
description = "Database host port.";
|
||||||
|
};
|
||||||
|
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "funkwhale";
|
||||||
|
description = "Database name.";
|
||||||
|
};
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "funkwhale";
|
||||||
|
description = "Database user.";
|
||||||
|
};
|
||||||
|
|
||||||
|
password = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "";
|
||||||
|
description = ''
|
||||||
|
The password corresponding to <option>database.user</option>.
|
||||||
|
Warning: this is stored in cleartext in the Nix store!
|
||||||
|
Use <option>database.passwordFile</option> instead.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
passwordFile = mkOption {
|
||||||
|
type = types.nullOr types.path;
|
||||||
|
default = null;
|
||||||
|
example = "/run/keys/funkwhale-dbpassword";
|
||||||
|
description = ''
|
||||||
|
A file containing the password corresponding to
|
||||||
|
<option>database.user</option>.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
socket = mkOption {
|
||||||
|
type = types.nullOr types.path;
|
||||||
|
default = "/run/postgresql";
|
||||||
|
defaultText = "/run/postgresql";
|
||||||
|
example = "/run/postgresql";
|
||||||
|
description = "Path to the unix socket file to use for authentication for local connections.";
|
||||||
|
};
|
||||||
|
|
||||||
|
createLocally = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Create the database and database user locally.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
dataDir = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "/srv/funkwhale";
|
||||||
|
description = ''
|
||||||
|
Where to keep the funkwhale data.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
apiIp = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "127.0.0.1";
|
||||||
|
description = ''
|
||||||
|
Funkwhale API IP.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
webWorkers = mkOption {
|
||||||
|
type = types.int;
|
||||||
|
default = 1;
|
||||||
|
description = ''
|
||||||
|
Funkwhale number of web workers.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
apiPort = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
default = 5000;
|
||||||
|
description = ''
|
||||||
|
Funkwhale API Port.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
hostname = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
The definitive, public domain you will use for your instance.
|
||||||
|
'';
|
||||||
|
example = "funkwhale.yourdomain.net";
|
||||||
|
};
|
||||||
|
|
||||||
|
protocol = mkOption {
|
||||||
|
type = types.enum [ "http" "https" ];
|
||||||
|
default = "https";
|
||||||
|
description = ''
|
||||||
|
Web server protocol.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
emailConfig = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "consolemail://";
|
||||||
|
description = ''
|
||||||
|
Configure email sending. By default, it outputs emails to console instead of sending them. See https://docs.funkwhale.audio/configuration.html#email-config for details.
|
||||||
|
'';
|
||||||
|
example = "smtp+ssl://user@:password@youremail.host:465";
|
||||||
|
};
|
||||||
|
|
||||||
|
defaultFromEmail = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
The email address to use to send system emails.
|
||||||
|
'';
|
||||||
|
example = "funkwhale@yourdomain.net";
|
||||||
|
};
|
||||||
|
|
||||||
|
api = {
|
||||||
|
mediaRoot = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "/srv/funkwhale/media";
|
||||||
|
description = ''
|
||||||
|
Where media files (such as album covers or audio tracks) should be stored on your system ? Ensure this directory actually exists.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
staticRoot = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "/srv/funkwhale/static";
|
||||||
|
description = ''
|
||||||
|
Where static files (such as API css or icons) should be compiled on your system ? Ensure this directory actually exists.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
djangoSecretKey = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
Django secret key. Generate one using `openssl rand -base64 45` for example.
|
||||||
|
'';
|
||||||
|
example = "6VhAWVKlqu/dJSdz6TVgEJn/cbbAidwsFvg9ddOwuPRssEs0OtzAhJxLcLVC";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
musicDirectoryPath = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "/srv/funkwhale/music";
|
||||||
|
description = ''
|
||||||
|
In-place import settings.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
enableRaven = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Sentry/Raven error reporting (server side).
|
||||||
|
Enable Raven if you want to help improve funkwhale by
|
||||||
|
automatically sending error reports to the funkwhale developers Sentry instance.
|
||||||
|
This will help them detect and correct bugs.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
ravenDsn = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "https://44332e9fdd3d42879c7d35bf8562c6a4:0062dc16a22b41679cd5765e5342f716@sentry.eliotberriot.com/5";
|
||||||
|
description = ''
|
||||||
|
Sentry/Raven DSN.
|
||||||
|
The default is the Funkwhale developers instance DSN.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
assertions = [
|
||||||
|
{ assertion = cfg.database.passwordFile != null || cfg.database.password != "" || cfg.database.socket != null;
|
||||||
|
message = "one of services.funkwhale.database.socket, services.funkwhale.database.passwordFile, or services.funkwhale.database.password must be set";
|
||||||
|
}
|
||||||
|
{ assertion = cfg.database.createLocally -> cfg.database.user == cfg.user;
|
||||||
|
message = "services.funkwhale.database.user must be set to ${cfg.user} if services.funkwhale.database.createLocally is set true";
|
||||||
|
}
|
||||||
|
{ assertion = cfg.database.createLocally -> cfg.database.socket != null;
|
||||||
|
message = "services.funkwhale.database.socket must be set if services.funkwhale.database.createLocally is set to true";
|
||||||
|
}
|
||||||
|
{ assertion = cfg.database.createLocally -> cfg.database.host == "localhost";
|
||||||
|
message = "services.funkwhale.database.host must be set to localhost if services.funkwhale.database.createLocally is set to true";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
users.users.funkwhale = mkIf (cfg.user == "funkwhale")
|
||||||
|
{ name = "funkwhale";
|
||||||
|
group = cfg.group;
|
||||||
|
};
|
||||||
|
|
||||||
|
users.groups.funkwhale = mkIf (cfg.group == "funkwhale") { name = "funkwhale"; };
|
||||||
|
|
||||||
|
services.postgresql = mkIf cfg.database.createLocally {
|
||||||
|
enable = true;
|
||||||
|
ensureDatabases = [ cfg.database.name ];
|
||||||
|
ensureUsers = [
|
||||||
|
{ name = cfg.database.user;
|
||||||
|
ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.redis.enable = true;
|
||||||
|
|
||||||
|
services.nginx = {
|
||||||
|
enable = true;
|
||||||
|
appendHttpConfig = ''
|
||||||
|
upstream funkwhale-api {
|
||||||
|
server ${cfg.apiIp}:${toString cfg.apiPort};
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
virtualHosts =
|
||||||
|
let proxyConfig = ''
|
||||||
|
# global proxy conf
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Forwarded-Host $host:$server_port;
|
||||||
|
proxy_set_header X-Forwarded-Port $server_port;
|
||||||
|
proxy_redirect off;
|
||||||
|
|
||||||
|
# websocket support
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection $connection_upgrade;
|
||||||
|
'';
|
||||||
|
withSSL = cfg.protocol == "https";
|
||||||
|
in {
|
||||||
|
"${cfg.hostname}" = {
|
||||||
|
enableACME = withSSL;
|
||||||
|
forceSSL = withSSL;
|
||||||
|
root = "${pkgs.funkwhale}/front";
|
||||||
|
# gzip config is nixos nginx recommendedGzipSettings with gzip_types from funkwhale doc (https://docs.funkwhale.audio/changelog.html#id5)
|
||||||
|
extraConfig = ''
|
||||||
|
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; media-src 'self' data:";
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin";
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
gzip_disable "msie6";
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_comp_level 5;
|
||||||
|
gzip_types
|
||||||
|
application/javascript
|
||||||
|
application/vnd.geo+json
|
||||||
|
application/vnd.ms-fontobject
|
||||||
|
application/x-font-ttf
|
||||||
|
application/x-web-app-manifest+json
|
||||||
|
font/opentype
|
||||||
|
image/bmp
|
||||||
|
image/svg+xml
|
||||||
|
image/x-icon
|
||||||
|
text/cache-manifest
|
||||||
|
text/css
|
||||||
|
text/plain
|
||||||
|
text/vcard
|
||||||
|
text/vnd.rim.location.xloc
|
||||||
|
text/vtt
|
||||||
|
text/x-component
|
||||||
|
text/x-cross-domain-policy;
|
||||||
|
gzip_vary on;
|
||||||
|
'';
|
||||||
|
locations = {
|
||||||
|
"/" = {
|
||||||
|
extraConfig = proxyConfig;
|
||||||
|
proxyPass = "http://funkwhale-api/";
|
||||||
|
};
|
||||||
|
"/front/" = {
|
||||||
|
alias = "${pkgs.funkwhale}/front/";
|
||||||
|
extraConfig = ''
|
||||||
|
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; media-src 'self' data:";
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin";
|
||||||
|
expires 30d;
|
||||||
|
add_header Pragma public;
|
||||||
|
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"= /front/embed.html" = {
|
||||||
|
alias = "${pkgs.funkwhale}/front/embed.html";
|
||||||
|
extraConfig = ''
|
||||||
|
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; media-src 'self' data:";
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin";
|
||||||
|
add_header X-Frame-Options "ALLOW";
|
||||||
|
expires 30d;
|
||||||
|
add_header Pragma public;
|
||||||
|
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"/federation/" = {
|
||||||
|
extraConfig = proxyConfig;
|
||||||
|
proxyPass = "http://funkwhale-api/federation/";
|
||||||
|
};
|
||||||
|
"/rest/" = {
|
||||||
|
extraConfig = proxyConfig;
|
||||||
|
proxyPass = "http://funkwhale-api/api/subsonic/rest/";
|
||||||
|
};
|
||||||
|
"/.well-known/" = {
|
||||||
|
extraConfig = proxyConfig;
|
||||||
|
proxyPass = "http://funkwhale-api/.well-known/";
|
||||||
|
};
|
||||||
|
"/media/".alias = "${cfg.api.mediaRoot}/";
|
||||||
|
"/_protected/media/" = {
|
||||||
|
extraConfig = ''
|
||||||
|
internal;
|
||||||
|
'';
|
||||||
|
alias = "${cfg.api.mediaRoot}/";
|
||||||
|
};
|
||||||
|
"/_protected/music/" = {
|
||||||
|
extraConfig = ''
|
||||||
|
internal;
|
||||||
|
'';
|
||||||
|
alias = "${cfg.musicDirectoryPath}/";
|
||||||
|
};
|
||||||
|
"/staticfiles/".alias = "${cfg.api.staticRoot}/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d ${cfg.dataDir} 0755 ${cfg.user} ${cfg.group} - -"
|
||||||
|
"d ${cfg.api.mediaRoot} 0755 ${cfg.user} ${cfg.group} - -"
|
||||||
|
"d ${cfg.api.staticRoot} 0755 ${cfg.user} ${cfg.group} - -"
|
||||||
|
"d ${cfg.musicDirectoryPath} 0755 ${cfg.user} ${cfg.group} - -"
|
||||||
|
];
|
||||||
|
|
||||||
|
systemd.targets.funkwhale = {
|
||||||
|
description = "Funkwhale";
|
||||||
|
wants = ["funkwhale-server.service" "funkwhale-worker.service" "funkwhale-beat.service"];
|
||||||
|
};
|
||||||
|
systemd.services =
|
||||||
|
let serviceConfig = {
|
||||||
|
User = "${cfg.user}";
|
||||||
|
WorkingDirectory = "${pkgs.funkwhale}";
|
||||||
|
EnvironmentFile = "${funkwhaleEnvFile}";
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
funkwhale-psql-init = mkIf cfg.database.createLocally {
|
||||||
|
description = "Funkwhale database preparation";
|
||||||
|
after = [ "redis.service" "postgresql.service" ];
|
||||||
|
wantedBy = [ "funkwhale-init.service" ];
|
||||||
|
before = [ "funkwhale-init.service" ];
|
||||||
|
serviceConfig = {
|
||||||
|
User = "postgres";
|
||||||
|
ExecStart = '' ${config.services.postgresql.package}/bin/psql -d ${cfg.database.name} -c 'CREATE EXTENSION IF NOT EXISTS "unaccent";CREATE EXTENSION IF NOT EXISTS "citext";' '';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
funkwhale-init = {
|
||||||
|
description = "Funkwhale initialization";
|
||||||
|
wantedBy = [ "funkwhale-server.service" "funkwhale-worker.service" "funkwhale-beat.service" ];
|
||||||
|
before = [ "funkwhale-server.service" "funkwhale-worker.service" "funkwhale-beat.service" ];
|
||||||
|
environment = funkwhaleEnv;
|
||||||
|
serviceConfig = {
|
||||||
|
User = "${cfg.user}";
|
||||||
|
Group = "${cfg.group}";
|
||||||
|
};
|
||||||
|
script = ''
|
||||||
|
${pythonEnv}/bin/python ${pkgs.funkwhale}/manage.py migrate
|
||||||
|
${pythonEnv}/bin/python ${pkgs.funkwhale}/manage.py collectstatic --no-input
|
||||||
|
if ! test -e ${cfg.dataDir}/createSuperUser.sh; then
|
||||||
|
echo "#!/bin/sh
|
||||||
|
|
||||||
|
${funkwhaleEnvScriptData} ${pythonEnv}/bin/python ${pkgs.funkwhale}/manage.py createsuperuser" > ${cfg.dataDir}/createSuperUser.sh
|
||||||
|
chmod u+x ${cfg.dataDir}/createSuperUser.sh
|
||||||
|
chown -R ${cfg.user}.${cfg.group} ${cfg.dataDir}
|
||||||
|
fi
|
||||||
|
if ! test -e ${cfg.dataDir}/config; then
|
||||||
|
mkdir -p ${cfg.dataDir}/config
|
||||||
|
ln -s ${funkwhaleEnvFile} ${cfg.dataDir}/config/.env
|
||||||
|
ln -s ${funkwhaleEnvFile} ${cfg.dataDir}/.env
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
funkwhale-server = {
|
||||||
|
description = "Funkwhale application server";
|
||||||
|
partOf = [ "funkwhale.target" ];
|
||||||
|
|
||||||
|
serviceConfig = serviceConfig // {
|
||||||
|
ExecStart = "${pythonEnv}/bin/gunicorn config.asgi:application -w ${toString cfg.webWorkers} -k uvicorn.workers.UvicornWorker -b ${cfg.apiIp}:${toString cfg.apiPort}";
|
||||||
|
};
|
||||||
|
environment = funkwhaleEnv;
|
||||||
|
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
funkwhale-worker = {
|
||||||
|
description = "Funkwhale celery worker";
|
||||||
|
partOf = [ "funkwhale.target" ];
|
||||||
|
|
||||||
|
serviceConfig = serviceConfig // {
|
||||||
|
RuntimeDirectory = "funkwhaleworker";
|
||||||
|
ExecStart = "${pythonEnv}/bin/celery -A funkwhale_api.taskapp worker -l INFO";
|
||||||
|
};
|
||||||
|
environment = funkwhaleEnv;
|
||||||
|
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
funkwhale-beat = {
|
||||||
|
description = "Funkwhale celery beat process";
|
||||||
|
partOf = [ "funkwhale.target" ];
|
||||||
|
|
||||||
|
serviceConfig = serviceConfig // {
|
||||||
|
RuntimeDirectory = "funkwhalebeat";
|
||||||
|
ExecStart = '' ${pythonEnv}/bin/celery -A funkwhale_api.taskapp beat -l INFO --schedule="/run/funkwhalebeat/celerybeat-schedule.db" --pidfile="/run/funkwhalebeat/celerybeat.pid" '';
|
||||||
|
};
|
||||||
|
environment = funkwhaleEnv;
|
||||||
|
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
maintainers = with lib.maintainers; [ mmai ];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ let
|
||||||
wasi = import wasiSrc { inherit wasiSrc; pkgs = pkgs // newpkgs; };
|
wasi = import wasiSrc { inherit wasiSrc; pkgs = pkgs // newpkgs; };
|
||||||
peertube = callPackage ./peertube { mylibs = import "${immaeNix}/lib" { inherit pkgs; }; };
|
peertube = callPackage ./peertube { mylibs = import "${immaeNix}/lib" { inherit pkgs; }; };
|
||||||
alps = callPackage ./alps {};
|
alps = callPackage ./alps {};
|
||||||
|
funkwhale = callPackage ./funkwhale {};
|
||||||
};
|
};
|
||||||
|
|
||||||
in newpkgs
|
in newpkgs
|
||||||
|
|
47
pkgs/funkwhale/default.nix
Normal file
47
pkgs/funkwhale/default.nix
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
{ stdenv, fetchurl, unzip }:
|
||||||
|
|
||||||
|
# Look for the correct urls for build_front and build_api artifacts on the tags page of the project : https://dev.funkwhale.audio/funkwhale/funkwhale/pipelines?scope=tags
|
||||||
|
# Attention : do not use the url "https://dev.funkwhale.audio/funkwhale/funkwhale/-/jobs/artifacts/${release}/download?job=" : it is not guaranteed to be stable
|
||||||
|
|
||||||
|
let
|
||||||
|
release = "1.0.1";
|
||||||
|
srcs = {
|
||||||
|
api = fetchurl {
|
||||||
|
url = https://dev.funkwhale.audio/funkwhale/funkwhale/-/jobs/56793/artifacts/download;
|
||||||
|
name = "api.zip";
|
||||||
|
sha256 = "0p21r8kbn7sr33chp7404fi9pm4yz6qhfz4z7gxf3vamg9fbsbsc";
|
||||||
|
};
|
||||||
|
frontend = fetchurl {
|
||||||
|
url = https://dev.funkwhale.audio/funkwhale/funkwhale/-/jobs/56790/artifacts/download;
|
||||||
|
name = "frontend.zip";
|
||||||
|
sha256 = "0hz4d59sva6zi5q53wj3f6yaw5didcl9z148s6rsy2m6gyr8566d";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in stdenv.mkDerivation {
|
||||||
|
name = "funkwhale";
|
||||||
|
version = "${release}";
|
||||||
|
src = srcs.api;
|
||||||
|
nativeBuildInputs = [ unzip ];
|
||||||
|
postPatch = ''
|
||||||
|
substituteInPlace requirements/base.txt \
|
||||||
|
--replace "django-cleanup==3.2.0" django-cleanup
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
mkdir $out
|
||||||
|
cp -R ./* $out
|
||||||
|
unzip ${srcs.frontend} -d $out
|
||||||
|
mv $out/front/ $out/front_tmp
|
||||||
|
mv $out/front_tmp/dist $out/front
|
||||||
|
rmdir $out/front_tmp
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = with stdenv.lib; {
|
||||||
|
description = "A modern, convivial and free music server";
|
||||||
|
homepage = https://funkwhale.audio/;
|
||||||
|
license = licenses.agpl3;
|
||||||
|
platforms = platforms.linux;
|
||||||
|
maintainers = with maintainers; [ mmai ];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue