fixxxxxxxxxxxxxxxx
This commit is contained in:
@@ -1,7 +1,5 @@
|
|||||||
# wafrn-nix
|
# wafrn-nix
|
||||||
|
|
||||||
By default this module uses a flake-pinned Wafrn source, so you do not need to clone Wafrn manually. You can still override `services.wafrn.source` if you want a local checkout.
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
@@ -18,6 +16,8 @@ Example:
|
|||||||
|
|
||||||
services.wafrn = {
|
services.wafrn = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
# package = my-wafrn-src-package;
|
||||||
|
|
||||||
# optional: override source checkout path
|
# optional: override source checkout path
|
||||||
# source = "/srv/wafrn";
|
# source = "/srv/wafrn";
|
||||||
stateDir = "/var/lib/wafrn";
|
stateDir = "/var/lib/wafrn";
|
||||||
|
|||||||
@@ -12,6 +12,23 @@ let
|
|||||||
|
|
||||||
cfg = config.services.wafrn;
|
cfg = config.services.wafrn;
|
||||||
|
|
||||||
|
defaultWafrnPackage =
|
||||||
|
if wafrnSrc != null then
|
||||||
|
pkgs.stdenvNoCC.mkDerivation {
|
||||||
|
pname = "wafrn-source";
|
||||||
|
version = "unstable";
|
||||||
|
src = wafrnSrc;
|
||||||
|
dontConfigure = true;
|
||||||
|
dontBuild = true;
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p "$out"
|
||||||
|
cp -a ./. "$out/"
|
||||||
|
chmod -R u+w "$out"
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
null;
|
||||||
|
|
||||||
toEnvString = value:
|
toEnvString = value:
|
||||||
if builtins.isBool value then
|
if builtins.isBool value then
|
||||||
(if value then "true" else "false")
|
(if value then "true" else "false")
|
||||||
@@ -105,14 +122,17 @@ let
|
|||||||
|
|
||||||
serviceEnvFile = "${cfg.stateDir}/.env";
|
serviceEnvFile = "${cfg.stateDir}/.env";
|
||||||
composeFile = "${cfg.stateDir}/docker-compose.yml";
|
composeFile = "${cfg.stateDir}/docker-compose.yml";
|
||||||
sourcePath =
|
packageSourcePath = toString cfg.package;
|
||||||
if cfg.source != null then
|
sourcePath = if cfg.source != null then cfg.source else "";
|
||||||
cfg.source
|
|
||||||
else if wafrnSrc != null then
|
|
||||||
toString wafrnSrc
|
|
||||||
else
|
|
||||||
"";
|
|
||||||
preparedSourcePath = "${cfg.stateDir}/source";
|
preparedSourcePath = "${cfg.stateDir}/source";
|
||||||
|
usingPrebuiltImages = cfg.images.backend != null && cfg.images.frontend != null;
|
||||||
|
effectiveCaddyConfigDir =
|
||||||
|
if cfg.caddyConfigDir != null then
|
||||||
|
cfg.caddyConfigDir
|
||||||
|
else if usingPrebuiltImages then
|
||||||
|
null
|
||||||
|
else
|
||||||
|
"${preparedSourcePath}/packages/caddy";
|
||||||
|
|
||||||
publishedPorts =
|
publishedPorts =
|
||||||
lib.optionals (cfg.httpPort != null) [ "${toString cfg.httpPort}:80" ]
|
lib.optionals (cfg.httpPort != null) [ "${toString cfg.httpPort}:80" ]
|
||||||
@@ -132,8 +152,24 @@ let
|
|||||||
dockerfile = "packages/backend/Dockerfile";
|
dockerfile = "packages/backend/Dockerfile";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
backendContainerSpec =
|
||||||
|
if usingPrebuiltImages then
|
||||||
|
{ image = cfg.images.backend; }
|
||||||
|
else
|
||||||
|
{ build = backendBuild; };
|
||||||
|
|
||||||
|
frontendContainerSpec =
|
||||||
|
if usingPrebuiltImages then
|
||||||
|
{ image = cfg.images.frontend; }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
build = {
|
||||||
|
context = preparedSourcePath;
|
||||||
|
dockerfile = "packages/frontend/Dockerfile";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
backendService = {
|
backendService = {
|
||||||
build = backendBuild;
|
|
||||||
depends_on = {
|
depends_on = {
|
||||||
db = { condition = "service_started"; };
|
db = { condition = "service_started"; };
|
||||||
redis = { condition = "service_started"; };
|
redis = { condition = "service_started"; };
|
||||||
@@ -153,9 +189,9 @@ let
|
|||||||
|
|
||||||
composeConfig = {
|
composeConfig = {
|
||||||
services = {
|
services = {
|
||||||
backend = serviceCommon // backendService;
|
backend = serviceCommon // backendService // backendContainerSpec;
|
||||||
|
|
||||||
migration = serviceCommon // backendService // {
|
migration = serviceCommon // backendService // backendContainerSpec // {
|
||||||
restart = "no";
|
restart = "no";
|
||||||
depends_on = {
|
depends_on = {
|
||||||
db = { condition = "service_started"; };
|
db = { condition = "service_started"; };
|
||||||
@@ -165,11 +201,7 @@ let
|
|||||||
command = "npm exec tsx migrate.ts init-container";
|
command = "npm exec tsx migrate.ts init-container";
|
||||||
};
|
};
|
||||||
|
|
||||||
frontend = serviceCommon // {
|
frontend = serviceCommon // frontendContainerSpec // {
|
||||||
build = {
|
|
||||||
context = preparedSourcePath;
|
|
||||||
dockerfile = "packages/frontend/Dockerfile";
|
|
||||||
};
|
|
||||||
restart = "unless-stopped";
|
restart = "unless-stopped";
|
||||||
ports = publishedPorts;
|
ports = publishedPorts;
|
||||||
extra_hosts = [ "host.docker.internal:host-gateway" ];
|
extra_hosts = [ "host.docker.internal:host-gateway" ];
|
||||||
@@ -177,7 +209,8 @@ let
|
|||||||
"${cfg.stateDir}/caddy:/data"
|
"${cfg.stateDir}/caddy:/data"
|
||||||
"${cfg.stateDir}/frontend:/var/www/html/frontend"
|
"${cfg.stateDir}/frontend:/var/www/html/frontend"
|
||||||
"${cfg.stateDir}/uploads:/var/www/html/uploads"
|
"${cfg.stateDir}/uploads:/var/www/html/uploads"
|
||||||
"${preparedSourcePath}/packages/caddy:/etc/caddy/config"
|
] ++ lib.optionals (effectiveCaddyConfigDir != null) [
|
||||||
|
"${effectiveCaddyConfigDir}:/etc/caddy/config"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -205,8 +238,7 @@ let
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
// optionalAttrs cfg.bluesky.enable {
|
// optionalAttrs cfg.bluesky.enable {
|
||||||
pds_worker = serviceCommon // {
|
pds_worker = serviceCommon // backendContainerSpec // {
|
||||||
build = backendBuild;
|
|
||||||
command = "npm exec tsx atproto.ts";
|
command = "npm exec tsx atproto.ts";
|
||||||
depends_on = {
|
depends_on = {
|
||||||
db = { condition = "service_started"; };
|
db = { condition = "service_started"; };
|
||||||
@@ -266,7 +298,41 @@ in
|
|||||||
type = types.nullOr types.str;
|
type = types.nullOr types.str;
|
||||||
default = null;
|
default = null;
|
||||||
example = "/srv/wafrn";
|
example = "/srv/wafrn";
|
||||||
description = "Optional path to a Wafrn source checkout. If null, the module uses the pinned source from this flake input.";
|
description = "Optional path to a Wafrn source checkout. If null, the service uses services.wafrn.package as the source.";
|
||||||
|
};
|
||||||
|
|
||||||
|
package = mkOption {
|
||||||
|
type = types.package;
|
||||||
|
default =
|
||||||
|
if defaultWafrnPackage != null then
|
||||||
|
defaultWafrnPackage
|
||||||
|
else
|
||||||
|
throw "services.wafrn.package must be set when no flake-pinned wafrn source is available";
|
||||||
|
defaultText = lib.literalExpression "<wafrn source package>";
|
||||||
|
description = "Package containing Wafrn source tree used to build backend/frontend Docker images.";
|
||||||
|
};
|
||||||
|
|
||||||
|
caddyConfigDir = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
example = "/srv/wafrn/packages/caddy";
|
||||||
|
description = "Optional host path mounted into frontend container as /etc/caddy/config for Caddy hooks/overrides.";
|
||||||
|
};
|
||||||
|
|
||||||
|
images = {
|
||||||
|
backend = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
example = "ghcr.io/your-org/wafrn-backend:2026.02.01";
|
||||||
|
description = "Prebuilt backend image. Set together with images.frontend to skip local source/Docker builds.";
|
||||||
|
};
|
||||||
|
|
||||||
|
frontend = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
example = "ghcr.io/your-org/wafrn-frontend:2026.02.01";
|
||||||
|
description = "Prebuilt frontend image. Set together with images.backend to skip local source/Docker builds.";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
stateDir = mkOption {
|
stateDir = mkOption {
|
||||||
@@ -352,8 +418,12 @@ in
|
|||||||
message = "services.wafrn requires virtualisation.docker.enable = true;";
|
message = "services.wafrn requires virtualisation.docker.enable = true;";
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
assertion = cfg.source != null || wafrnSrc != null;
|
assertion = usingPrebuiltImages || cfg.source != null || cfg.package != null;
|
||||||
message = "services.wafrn.source is null and no flake-pinned wafrn source is available.";
|
message = "Provide services.wafrn.images.backend+frontend, set services.wafrn.source, or set services.wafrn.package.";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = (cfg.images.backend == null) == (cfg.images.frontend == null);
|
||||||
|
message = "Set both services.wafrn.images.backend and services.wafrn.images.frontend, or neither.";
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
assertion = cfg.httpPort != null || cfg.httpsPort != null;
|
assertion = cfg.httpPort != null || cfg.httpsPort != null;
|
||||||
@@ -369,6 +439,7 @@ in
|
|||||||
"d ${cfg.stateDir}/cache 0750 root root -"
|
"d ${cfg.stateDir}/cache 0750 root root -"
|
||||||
"d ${cfg.stateDir}/caddy 0750 root root -"
|
"d ${cfg.stateDir}/caddy 0750 root root -"
|
||||||
"d ${cfg.stateDir}/frontend 0750 root root -"
|
"d ${cfg.stateDir}/frontend 0750 root root -"
|
||||||
|
] ++ lib.optionals (!usingPrebuiltImages) [
|
||||||
"d ${cfg.stateDir}/source 0750 root root -"
|
"d ${cfg.stateDir}/source 0750 root root -"
|
||||||
] ++ lib.optionals (cfg.bluesky.enable && cfg.bluesky.useBundledPds) [
|
] ++ lib.optionals (cfg.bluesky.enable && cfg.bluesky.useBundledPds) [
|
||||||
"d ${cfg.stateDir}/pds 0750 root root -"
|
"d ${cfg.stateDir}/pds 0750 root root -"
|
||||||
@@ -389,20 +460,38 @@ in
|
|||||||
script = ''
|
script = ''
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
if [ ! -d "${sourcePath}" ]; then
|
${optionalString (!usingPrebuiltImages) ''
|
||||||
echo "wafrn-nix: source directory does not exist: ${sourcePath}" >&2
|
selected_source=""
|
||||||
|
|
||||||
|
if [ -n "${sourcePath}" ] && [ -d "${sourcePath}" ]; then
|
||||||
|
selected_source="${sourcePath}"
|
||||||
|
elif [ -n "${sourcePath}" ]; then
|
||||||
|
echo "wafrn-nix: configured source does not exist (${sourcePath}), falling back to services.wafrn.package" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$selected_source" ] && [ -d "${packageSourcePath}" ]; then
|
||||||
|
selected_source="${packageSourcePath}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$selected_source" ]; then
|
||||||
|
echo "wafrn-nix: no usable source directory found" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm -rf "${preparedSourcePath}"
|
rm -rf "${preparedSourcePath}"
|
||||||
mkdir -p "${preparedSourcePath}"
|
mkdir -p "${preparedSourcePath}"
|
||||||
cp -a "${sourcePath}/." "${preparedSourcePath}/"
|
cp -a "$selected_source/." "${preparedSourcePath}/"
|
||||||
chmod -R u+w "${preparedSourcePath}"
|
chmod -R u+w "${preparedSourcePath}"
|
||||||
|
|
||||||
|
if [ ! -e "${preparedSourcePath}/.git" ]; then
|
||||||
|
mkdir -p "${preparedSourcePath}/.git"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ ! -f "${preparedSourcePath}/package-lock.json" ]; then
|
if [ ! -f "${preparedSourcePath}/package-lock.json" ]; then
|
||||||
echo "wafrn-nix: package-lock.json missing, generating with npm" >&2
|
echo "wafrn-nix: package-lock.json missing, generating with npm" >&2
|
||||||
(cd "${preparedSourcePath}" && npm install --package-lock-only --ignore-scripts)
|
(cd "${preparedSourcePath}" && npm install --package-lock-only --ignore-scripts)
|
||||||
fi
|
fi
|
||||||
|
''}
|
||||||
|
|
||||||
install -m 0600 ${envTemplate} ${serviceEnvFile}
|
install -m 0600 ${envTemplate} ${serviceEnvFile}
|
||||||
${optionalString (cfg.secretsFile != null) ''
|
${optionalString (cfg.secretsFile != null) ''
|
||||||
|
|||||||
Reference in New Issue
Block a user