Compare commits
No commits in common. "da3157c8188b08a6b4d4147ea2e3138d4a71608c" and "74e4e8dcd6e76cbbcc77dd542319d2882e5f9661" have entirely different histories.
da3157c818
...
74e4e8dcd6
7 changed files with 24 additions and 247 deletions
|
|
@ -3,10 +3,7 @@
|
||||||
{
|
{
|
||||||
config = {
|
config = {
|
||||||
homelab = {
|
homelab = {
|
||||||
apps = {
|
apps.changedetection.enable = true;
|
||||||
calibre.enable = true;
|
|
||||||
traefik.enable = true;
|
|
||||||
};
|
|
||||||
virtualisation.guest.enable = true;
|
virtualisation.guest.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -243,7 +243,7 @@ in {
|
||||||
port = 6767;
|
port = 6767;
|
||||||
in lib.mkIf cfg.bazarr.enable {
|
in lib.mkIf cfg.bazarr.enable {
|
||||||
hostname = "bazarr";
|
hostname = "bazarr";
|
||||||
image = "ghcr.io/hotio/bazarr:release-1.5.2";
|
image = "ghcr.io/hotio/bazarr:release-1.4.4";
|
||||||
autoStart = true;
|
autoStart = true;
|
||||||
ports = lib.mkIf cfg.bazarr.exposePorts [
|
ports = lib.mkIf cfg.bazarr.exposePorts [
|
||||||
"${toString port}:${toString port}/tcp"
|
"${toString port}:${toString port}/tcp"
|
||||||
|
|
@ -279,7 +279,7 @@ in {
|
||||||
port = 9696;
|
port = 9696;
|
||||||
in lib.mkIf cfg.prowlarr.enable {
|
in lib.mkIf cfg.prowlarr.enable {
|
||||||
hostname = "prowlarr";
|
hostname = "prowlarr";
|
||||||
image = "ghcr.io/hotio/prowlarr:release-2.0.5.5160";
|
image = "ghcr.io/hotio/prowlarr:release-1.23.1.4708";
|
||||||
autoStart = true;
|
autoStart = true;
|
||||||
ports = lib.mkIf cfg.prowlarr.exposePorts [
|
ports = lib.mkIf cfg.prowlarr.exposePorts [
|
||||||
"${toString port}:${toString port}/tcp"
|
"${toString port}:${toString port}/tcp"
|
||||||
|
|
@ -310,7 +310,7 @@ in {
|
||||||
port = 10095;
|
port = 10095;
|
||||||
in lib.mkIf cfg.qbittorrent.enable {
|
in lib.mkIf cfg.qbittorrent.enable {
|
||||||
hostname = "qbittorrent";
|
hostname = "qbittorrent";
|
||||||
image = "ghcr.io/hotio/qbittorrent:release-5.1.2";
|
image = "ghcr.io/hotio/qbittorrent:release-4.6.7";
|
||||||
autoStart = true;
|
autoStart = true;
|
||||||
ports = lib.mkIf cfg.qbittorrent.exposePorts [
|
ports = lib.mkIf cfg.qbittorrent.exposePorts [
|
||||||
"${toString port}:${toString port}/tcp"
|
"${toString port}:${toString port}/tcp"
|
||||||
|
|
@ -343,7 +343,7 @@ in {
|
||||||
port = 7878;
|
port = 7878;
|
||||||
in lib.mkIf cfg.radarr.enable {
|
in lib.mkIf cfg.radarr.enable {
|
||||||
hostname = "radarr";
|
hostname = "radarr";
|
||||||
image = "ghcr.io/hotio/radarr:testing-5.28.0.10205";
|
image = "ghcr.io/hotio/radarr:release-5.9.1.9070";
|
||||||
autoStart = true;
|
autoStart = true;
|
||||||
ports = lib.mkIf cfg.radarr.exposePorts [
|
ports = lib.mkIf cfg.radarr.exposePorts [
|
||||||
"${toString port}:${toString port}/tcp"
|
"${toString port}:${toString port}/tcp"
|
||||||
|
|
@ -377,7 +377,7 @@ in {
|
||||||
port = 8989;
|
port = 8989;
|
||||||
in lib.mkIf cfg.sonarr.enable {
|
in lib.mkIf cfg.sonarr.enable {
|
||||||
hostname = "sonarr";
|
hostname = "sonarr";
|
||||||
image = "ghcr.io/hotio/sonarr:release-4.0.15.2941";
|
image = "ghcr.io/hotio/sonarr:release-4.0.9.2244";
|
||||||
autoStart = true;
|
autoStart = true;
|
||||||
ports = lib.mkIf cfg.sonarr.exposePorts [
|
ports = lib.mkIf cfg.sonarr.exposePorts [
|
||||||
"${toString port}:${toString port}/tcp"
|
"${toString port}:${toString port}/tcp"
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ let
|
||||||
calibre-web-config = "/srv/calibre-web-config";
|
calibre-web-config = "/srv/calibre-web-config";
|
||||||
|
|
||||||
networkName = "calibre";
|
networkName = "calibre";
|
||||||
proxyNet = config.homelab.apps.traefik.sharedNetworkName;
|
|
||||||
in {
|
in {
|
||||||
options.homelab.apps.calibre = {
|
options.homelab.apps.calibre = {
|
||||||
enable = lib.mkEnableOption "Calibre (Desktop + Web)";
|
enable = lib.mkEnableOption "Calibre (Desktop + Web)";
|
||||||
|
|
@ -93,7 +92,7 @@ in {
|
||||||
innerPort = 8080;
|
innerPort = 8080;
|
||||||
in {
|
in {
|
||||||
hostname = "calibre";
|
hostname = "calibre";
|
||||||
image = "lscr.io/linuxserver/calibre:v8.10.0-ls354";
|
image = "lscr.io/linuxserver/calibre:8.5.0";
|
||||||
autoStart = true;
|
autoStart = true;
|
||||||
ports = [
|
ports = [
|
||||||
# Open ports if you don't use Traefik
|
# Open ports if you don't use Traefik
|
||||||
|
|
@ -103,7 +102,6 @@ in {
|
||||||
];
|
];
|
||||||
extraOptions = [
|
extraOptions = [
|
||||||
"--network=${networkName}"
|
"--network=${networkName}"
|
||||||
"--network=${proxyNet}"
|
|
||||||
|
|
||||||
# syscalls are unkown to Docker
|
# syscalls are unkown to Docker
|
||||||
#"--security-opt" "seccomp=unconfined"
|
#"--security-opt" "seccomp=unconfined"
|
||||||
|
|
@ -124,7 +122,6 @@ in {
|
||||||
];
|
];
|
||||||
labels = {
|
labels = {
|
||||||
"traefik.enable" = "true";
|
"traefik.enable" = "true";
|
||||||
"traefik.docker.network" = proxyNet;
|
|
||||||
"traefik.http.routers.calibre.rule" = "Host(`calibre.depeuter.dev`)";
|
"traefik.http.routers.calibre.rule" = "Host(`calibre.depeuter.dev`)";
|
||||||
"traefik.http.services.calibre.loadbalancer.server.port" = toString innerPort;
|
"traefik.http.services.calibre.loadbalancer.server.port" = toString innerPort;
|
||||||
};
|
};
|
||||||
|
|
@ -151,7 +148,7 @@ in {
|
||||||
innerPort = 8083;
|
innerPort = 8083;
|
||||||
in {
|
in {
|
||||||
hostname = "calibre-web";
|
hostname = "calibre-web";
|
||||||
image = "lscr.io/linuxserver/calibre-web:0.6.25-ls346";
|
image = "lscr.io/linuxserver/calibre-web:0.6.24";
|
||||||
autoStart = true;
|
autoStart = true;
|
||||||
ports = [
|
ports = [
|
||||||
# Open ports if you don't use Traefik
|
# Open ports if you don't use Traefik
|
||||||
|
|
@ -159,7 +156,6 @@ in {
|
||||||
];
|
];
|
||||||
extraOptions = [
|
extraOptions = [
|
||||||
"--network=${networkName}"
|
"--network=${networkName}"
|
||||||
"--network=${proxyNet}"
|
|
||||||
];
|
];
|
||||||
environment = {
|
environment = {
|
||||||
inherit PUID PGID;
|
inherit PUID PGID;
|
||||||
|
|
@ -179,7 +175,6 @@ in {
|
||||||
];
|
];
|
||||||
labels = {
|
labels = {
|
||||||
"traefik.enable" = "true";
|
"traefik.enable" = "true";
|
||||||
"traefik.docker.network" = proxyNet;
|
|
||||||
"traefik.http.routers.calibre-web.rule" = "Host(`books.depeuter.dev`)";
|
"traefik.http.routers.calibre-web.rule" = "Host(`books.depeuter.dev`)";
|
||||||
"traefik.http.services.calibre-web.loadbalancer.server.port" = toString innerPort;
|
"traefik.http.services.calibre-web.loadbalancer.server.port" = toString innerPort;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@
|
||||||
./plex
|
./plex
|
||||||
./speedtest
|
./speedtest
|
||||||
./technitium-dns
|
./technitium-dns
|
||||||
./traefik
|
|
||||||
./vaultwarden
|
./vaultwarden
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ let
|
||||||
cfg = config.homelab.apps.jellyfin;
|
cfg = config.homelab.apps.jellyfin;
|
||||||
|
|
||||||
networkName = "jellyfin";
|
networkName = "jellyfin";
|
||||||
inherit (config.homelab.fileSystems) media;
|
|
||||||
|
|
||||||
UID = 3008;
|
UID = 3008;
|
||||||
GID = config.users.groups.media.gid;
|
GID = config.users.groups.media.gid;
|
||||||
|
|
@ -13,11 +12,6 @@ in {
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
homelab = {
|
homelab = {
|
||||||
fileSystems.media.video = {
|
|
||||||
enable = true;
|
|
||||||
permissions = [ "read" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
users = {
|
users = {
|
||||||
apps.enable = true;
|
apps.enable = true;
|
||||||
media.enable = true;
|
media.enable = true;
|
||||||
|
|
@ -38,6 +32,18 @@ in {
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
"/srv/video" = {
|
||||||
|
device = "192.168.0.11:/mnt/SMALL/MEDIA/VIDEO";
|
||||||
|
fsType = "nfs";
|
||||||
|
options = [
|
||||||
|
"ro"
|
||||||
|
"nfsvers=4.2"
|
||||||
|
"async" "soft"
|
||||||
|
"timeo=100" "retry=50" "actimeo=1800" "lookupcache=all"
|
||||||
|
"nosuid" "tcp"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
"/srv/homevideo" = {
|
"/srv/homevideo" = {
|
||||||
device = "192.168.0.11:/mnt/BIG/MEDIA/HOMEVIDEO/ARCHIVE";
|
device = "192.168.0.11:/mnt/BIG/MEDIA/HOMEVIDEO/ARCHIVE";
|
||||||
fsType = "nfs";
|
fsType = "nfs";
|
||||||
|
|
@ -95,7 +101,7 @@ in {
|
||||||
virtualisation.oci-containers.containers = {
|
virtualisation.oci-containers.containers = {
|
||||||
jellyfin = {
|
jellyfin = {
|
||||||
hostname = "jellyfin";
|
hostname = "jellyfin";
|
||||||
image = "jellyfin/jellyfin:10.10.7";
|
image = "jellyfin/jellyfin:10.10.0";
|
||||||
user = "${toString UID}:${toString GID}";
|
user = "${toString UID}:${toString GID}";
|
||||||
autoStart = true;
|
autoStart = true;
|
||||||
ports = [
|
ports = [
|
||||||
|
|
@ -111,7 +117,7 @@ in {
|
||||||
"cache:/cache"
|
"cache:/cache"
|
||||||
|
|
||||||
"/srv/audio:/media/audio"
|
"/srv/audio:/media/audio"
|
||||||
"${media.video.hostPath}:/media/video"
|
"/srv/video:/media/video"
|
||||||
"/srv/homevideo:/media/homevideo"
|
"/srv/homevideo:/media/homevideo"
|
||||||
"/srv/photo:/media/photo"
|
"/srv/photo:/media/photo"
|
||||||
];
|
];
|
||||||
|
|
@ -138,7 +144,7 @@ in {
|
||||||
feishinPort = "9180";
|
feishinPort = "9180";
|
||||||
in {
|
in {
|
||||||
hostname = "feishin";
|
hostname = "feishin";
|
||||||
image = "ghcr.io/jeffvli/feishin:0.19.0";
|
image = "ghcr.io/jeffvli/feishin:0.7.1";
|
||||||
autoStart = true;
|
autoStart = true;
|
||||||
ports = [
|
ports = [
|
||||||
"${feishinPort}:9180/tcp" # Web player (HTTP)
|
"${feishinPort}:9180/tcp" # Web player (HTTP)
|
||||||
|
|
|
||||||
|
|
@ -1,220 +0,0 @@
|
||||||
{ config, lib, pkgs, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.homelab.apps.penpot;
|
|
||||||
|
|
||||||
networkName = "penpot";
|
|
||||||
UID = config.users.users.apps.uid;
|
|
||||||
GID = config.users.groups.apps.gid;
|
|
||||||
|
|
||||||
version = "2.5.4";
|
|
||||||
|
|
||||||
srvPath = "/srv/penpot";
|
|
||||||
assetsPath = "/opt/data/assets";
|
|
||||||
|
|
||||||
PENPOT_FLAGS = "enable-smtp enable-prepl-server disable-secure-session-cookies disable-onboarding disable-registration";
|
|
||||||
# Max body size (30MiB); Used for plain requests, should never be
|
|
||||||
# greater than multi-part size
|
|
||||||
PENPOT_HTTP_SERVER_MAX_BODY_SIZE = "31457280";
|
|
||||||
# Max multipart body size (350MiB)
|
|
||||||
PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE = "367001600";
|
|
||||||
|
|
||||||
dbName = "penpot";
|
|
||||||
dbUser = "penpot";
|
|
||||||
dbPass = "penpot";
|
|
||||||
|
|
||||||
docker = config.virtualisation.oci-containers.containers;
|
|
||||||
in {
|
|
||||||
options.homelab.apps.penpot.enable = lib.mkEnableOption "Penpot using Docker";
|
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
|
||||||
homelab = {
|
|
||||||
users.apps.enable = true;
|
|
||||||
virtualisation.containers.enable = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
fileSystems."${srvPath}" = {
|
|
||||||
device = "192.168.0.11:/mnt/SMALL/CONFIG/PENPOT";
|
|
||||||
fsType = "nfs";
|
|
||||||
options = [
|
|
||||||
"rw"
|
|
||||||
"nfsvers=4.2"
|
|
||||||
"sync" "hard" "timeo=600"
|
|
||||||
"retrans=2"
|
|
||||||
"_netdev"
|
|
||||||
"nosuid" "tcp"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# Make sure the Docker network exists.
|
|
||||||
systemd.services."docker-${networkName}-create-network" = lib.mkIf cfg.enable {
|
|
||||||
description = "Create Docker network for ${networkName}";
|
|
||||||
requiredBy = [
|
|
||||||
"docker-penpot-frontend.service"
|
|
||||||
"docker-penpot-backend.service"
|
|
||||||
"docker-penpot-exporter.service"
|
|
||||||
"docker-penpot-postgres.service"
|
|
||||||
"docker-penpot-redis.service"
|
|
||||||
];
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
RemainAfterExit = true;
|
|
||||||
};
|
|
||||||
script = ''
|
|
||||||
if ! ${pkgs.docker}/bin/docker network ls | grep -q ${networkName}; then
|
|
||||||
${pkgs.docker}/bin/docker network create ${networkName}
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
virtualisation.oci-containers.containers = let
|
|
||||||
frontendPort = 8080;
|
|
||||||
redisUri = "redis://${docker.penpot-redis.hostname}/0";
|
|
||||||
in {
|
|
||||||
penpot-frontend = {
|
|
||||||
hostname = "penpot-frontend";
|
|
||||||
image = "penpotapp/frontend:${version}";
|
|
||||||
# user = "${toString UID}:${toString GID}";
|
|
||||||
user = "0:${toString GID}";
|
|
||||||
ports = [
|
|
||||||
# "9001:${toString frontendPort}/tcp"
|
|
||||||
];
|
|
||||||
extraOptions = [
|
|
||||||
"--network=${networkName}"
|
|
||||||
];
|
|
||||||
environment = {
|
|
||||||
inherit PENPOT_FLAGS;
|
|
||||||
inherit PENPOT_HTTP_SERVER_MAX_BODY_SIZE;
|
|
||||||
inherit PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE;
|
|
||||||
};
|
|
||||||
volumes = [
|
|
||||||
"${srvPath}:${assetsPath}"
|
|
||||||
];
|
|
||||||
dependsOn = [
|
|
||||||
docker.penpot-backend.hostname
|
|
||||||
docker.penpot-exporter.hostname
|
|
||||||
];
|
|
||||||
labels = {
|
|
||||||
"traefik.enable" = "true";
|
|
||||||
"traefik.http.routers.penpot.rule" = "Host(`penpot.depeuter.dev`)";
|
|
||||||
"traefik.http.services.penpot.loadbalancer.server.port" = toString frontendPort;
|
|
||||||
"traefik.tls.options.default.minVersion" = "VersionTLS13";
|
|
||||||
};
|
|
||||||
autoStart = true;
|
|
||||||
};
|
|
||||||
penpot-backend = {
|
|
||||||
hostname = "penpot-backend";
|
|
||||||
image = "penpotapp/backend:latest";
|
|
||||||
# user = "${toString UID}:${toString GID}";
|
|
||||||
user = "0:${toString GID}";
|
|
||||||
extraOptions = [
|
|
||||||
"--network=${networkName}"
|
|
||||||
];
|
|
||||||
environmentFiles = [
|
|
||||||
/home/admin/.penpot.secret
|
|
||||||
];
|
|
||||||
environment = {
|
|
||||||
inherit PENPOT_FLAGS;
|
|
||||||
inherit PENPOT_HTTP_SERVER_MAX_BODY_SIZE;
|
|
||||||
inherit PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE;
|
|
||||||
|
|
||||||
PENPOT_PUBLIC_URI = "https://penpot.depeuter.dev";
|
|
||||||
|
|
||||||
## Database connection parameters. Don't touch them unless you are using custom
|
|
||||||
## postgresql connection parameters.
|
|
||||||
|
|
||||||
PENPOT_DATABASE_URI = "postgresql://${docker.penpot-postgres.hostname}/${dbName}";
|
|
||||||
PENPOT_DATABASE_USERNAME = dbUser;
|
|
||||||
PENPOT_DATABASE_PASSWORD = dbPass;
|
|
||||||
|
|
||||||
## Redis is used for the websockets notifications. Don't touch unless the redis
|
|
||||||
## container has different parameters or different name.
|
|
||||||
|
|
||||||
PENPOT_REDIS_URI = redisUri;
|
|
||||||
|
|
||||||
## Default configuration for assets storage: using filesystem based with all files
|
|
||||||
## stored in a docker volume.
|
|
||||||
|
|
||||||
PENPOT_ASSETS_STORAGE_BACKEND = "assets-fs";
|
|
||||||
PENPOT_STORAGE_ASSETS_FS_DIRECTORY = assetsPath;
|
|
||||||
|
|
||||||
## Telemetry. When enabled, a periodical process will send anonymous data about this
|
|
||||||
## instance. Telemetry data will enable us to learn how the application is used,
|
|
||||||
## based on real scenarios. If you want to help us, please leave it enabled. You can
|
|
||||||
## audit what data we send with the code available on github.
|
|
||||||
|
|
||||||
PENPOT_TELEMETRY_ENABLED = "false";
|
|
||||||
PENPOT_TELEMETRY_REFERER = "compose";
|
|
||||||
|
|
||||||
# PENPOT_SMTP_HOST = "smtp.gmail.com";
|
|
||||||
# PENPOT_SMTP_PORT = "465";
|
|
||||||
# PENPOT_SMTP_USERNAME: "kmtl.hugo@gmail.com";
|
|
||||||
# PENPOT_SMTP_PASSWORD: <password>
|
|
||||||
# PENPOT_SMTP_TLS: true
|
|
||||||
};
|
|
||||||
volumes = [
|
|
||||||
"${srvPath}:${assetsPath}"
|
|
||||||
];
|
|
||||||
dependsOn = [
|
|
||||||
docker.penpot-postgres.hostname
|
|
||||||
docker.penpot-redis.hostname
|
|
||||||
];
|
|
||||||
autoStart = true;
|
|
||||||
};
|
|
||||||
penpot-exporter = {
|
|
||||||
hostname = "penpot-exporter";
|
|
||||||
image = "penpotapp/exporter:latest";
|
|
||||||
extraOptions = [
|
|
||||||
"--network=${networkName}"
|
|
||||||
];
|
|
||||||
environment = {
|
|
||||||
# Don't touch it; this uses an internal docker network to
|
|
||||||
# communicate with the frontend.
|
|
||||||
PENPOT_PUBLIC_URI = "http://${docker.penpot-frontend.hostname}:${toString frontendPort}";
|
|
||||||
|
|
||||||
## Redis is used for the websockets notifications.
|
|
||||||
PENPOT_REDIS_URI = redisUri;
|
|
||||||
};
|
|
||||||
dependsOn = [
|
|
||||||
docker.penpot-redis.hostname
|
|
||||||
];
|
|
||||||
autoStart = true;
|
|
||||||
};
|
|
||||||
penpot-postgres = {
|
|
||||||
hostname = "penpot-postgres";
|
|
||||||
image = "postgres:15";
|
|
||||||
extraOptions = [
|
|
||||||
"--network=${networkName}"
|
|
||||||
"--health-cmd='pg_isready -U ${dbUser}'"
|
|
||||||
"--health-interval=2s"
|
|
||||||
"--health-retries=5"
|
|
||||||
"--health-timeout=10s"
|
|
||||||
"--health-start-period=2s"
|
|
||||||
];
|
|
||||||
environment = {
|
|
||||||
POSTGRES_INITDB_ARGS = "--data-checksums";
|
|
||||||
POSTGRES_DB = dbName;
|
|
||||||
POSTGRES_USER = dbUser;
|
|
||||||
POSTGRES_PASSWORD = dbPass;
|
|
||||||
};
|
|
||||||
volumes = [
|
|
||||||
"penpot_postgres_v15:/var/lib/postgresql/data"
|
|
||||||
];
|
|
||||||
autoStart = true;
|
|
||||||
};
|
|
||||||
penpot-redis = {
|
|
||||||
hostname = "penpot-redis";
|
|
||||||
image = "redis:7.2";
|
|
||||||
extraOptions = [
|
|
||||||
"--network=${networkName}"
|
|
||||||
"--health-cmd='redis-cli ping | grep PONG'"
|
|
||||||
"--health-interval=1s"
|
|
||||||
"--health-retries=5"
|
|
||||||
"--health-timeout=3s"
|
|
||||||
"--health-start-period=3s"
|
|
||||||
];
|
|
||||||
autoStart = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -44,6 +44,6 @@ in {
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue