Host a writefreely instance for dtth
This commit is contained in:
parent
2f0e0b0939
commit
2528ccd33f
|
@ -16,6 +16,7 @@
|
||||||
./headscale.nix
|
./headscale.nix
|
||||||
./gitea.nix
|
./gitea.nix
|
||||||
./miniflux.nix
|
./miniflux.nix
|
||||||
|
./writefreely.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
common.linux.enable = false; # Don't enable the "common linux" module, this is a special machine.
|
common.linux.enable = false; # Don't enable the "common linux" module, this is a special machine.
|
||||||
|
@ -156,9 +157,9 @@
|
||||||
envFile = config.sops.secrets.youmubot-env.path;
|
envFile = config.sops.secrets.youmubot-env.path;
|
||||||
};
|
};
|
||||||
|
|
||||||
# Writefreely
|
# Writefreely on nki
|
||||||
cloud.writefreely.enable = true;
|
# cloud.writefreely.enable = true;
|
||||||
cloud.writefreely.package = pkgs.unstable.writefreely;
|
# cloud.writefreely.package = pkgs.unstable.writefreely;
|
||||||
|
|
||||||
# Authentik
|
# Authentik
|
||||||
sops.secrets.authentik-env = { };
|
sops.secrets.authentik-env = { };
|
||||||
|
|
|
@ -30,6 +30,7 @@ miniflux:
|
||||||
oidc-client-secret: ENC[AES256_GCM,data:lvPFzc1ZXOTArs6VfFzJmKjW7pFpWU1XMCzHfUWC8WRq3CTvd6iQSwnGgBmUtHtDJCF1un/32fOaqBtSeMkgt5nkd/j8fLhTrTZyo8hJV2l1p16RBC2C0+igQh8PMah1Yp+V4zTMd5UKYMPkK9Iwb/tJ5zXeqQ20LvmGk+E7lCCVrnyyJrJNwr310mzYaUmpRz9/1AEM,iv:+9yhwYxnB5zPSRjX9o3PRvBpq4hM45jNF3acqT6lSJc=,tag:yMTWbxHlTZgg4XxNjfc8wQ==,type:str]
|
oidc-client-secret: ENC[AES256_GCM,data:lvPFzc1ZXOTArs6VfFzJmKjW7pFpWU1XMCzHfUWC8WRq3CTvd6iQSwnGgBmUtHtDJCF1un/32fOaqBtSeMkgt5nkd/j8fLhTrTZyo8hJV2l1p16RBC2C0+igQh8PMah1Yp+V4zTMd5UKYMPkK9Iwb/tJ5zXeqQ20LvmGk+E7lCCVrnyyJrJNwr310mzYaUmpRz9/1AEM,iv:+9yhwYxnB5zPSRjX9o3PRvBpq4hM45jNF3acqT6lSJc=,tag:yMTWbxHlTZgg4XxNjfc8wQ==,type:str]
|
||||||
pocket-consumer-key: ENC[AES256_GCM,data:NXY9Y8rFlzCVVG3ATUL/u7Sj6Im1RU/D16toUOLcIfKvddBjlu+QddKXWfLKppV1BQZ0,iv:nf3gkm098UhpZOgMbOdyG1FYVcl5G0gxoI6RTsZ1r14=,tag:bMOYwtFwUJ4SFornsWo8ig==,type:str]
|
pocket-consumer-key: ENC[AES256_GCM,data:NXY9Y8rFlzCVVG3ATUL/u7Sj6Im1RU/D16toUOLcIfKvddBjlu+QddKXWfLKppV1BQZ0,iv:nf3gkm098UhpZOgMbOdyG1FYVcl5G0gxoI6RTsZ1r14=,tag:bMOYwtFwUJ4SFornsWo8ig==,type:str]
|
||||||
admin-creds: ENC[AES256_GCM,data:cBCwwRZR0B8nH7XLxHVZCThqmnUI6ZHFp3wH9TjdRbBTmySjPqU526ltn3lRQtopgqQ0IOuneTztXJ+wfqmLUABV6xlLBkXD7VX6Mf43RtIDyHL+UC56eIdn3xeawGsIjnta,iv:DOwHUL64ufLS7FbvnJCPxPYwMJF1pMPqjx78vltm9IY=,tag:A2Fpk4rI0/WK0jFtTlGhaA==,type:str]
|
admin-creds: ENC[AES256_GCM,data:cBCwwRZR0B8nH7XLxHVZCThqmnUI6ZHFp3wH9TjdRbBTmySjPqU526ltn3lRQtopgqQ0IOuneTztXJ+wfqmLUABV6xlLBkXD7VX6Mf43RtIDyHL+UC56eIdn3xeawGsIjnta,iv:DOwHUL64ufLS7FbvnJCPxPYwMJF1pMPqjx78vltm9IY=,tag:A2Fpk4rI0/WK0jFtTlGhaA==,type:str]
|
||||||
|
writefreely-dtth: ENC[AES256_GCM,data:Q2b3eCr5GLLyBMrGlTUSIuMN/vZXmMZV8T56+t7RjcoHQmEVDKGwPGgka4jf/yO9Nf6TdGB7iiXft+XK3t74XdnzTCTYYVFzFsv49eZDKpTeaR6pKcbesfJYyqOcHIuatQz/orQ1X6Ext9Xf9aBStY4GV6ticLpvdW3GtHzchMPuMm8vY8A8DYNH/kLGb96aHpQ53paKkckeDWcbDyCulUU=,iv:G4TNJ4vY6qo4iOrEBmsf6hHJWAqbl3t8JAyDIZ1lUUg=,tag:HEknuS+MjBBFbkpDEIRUfw==,type:str]
|
||||||
sops:
|
sops:
|
||||||
kms: []
|
kms: []
|
||||||
gcp_kms: []
|
gcp_kms: []
|
||||||
|
@ -63,8 +64,8 @@ sops:
|
||||||
by9kZFlTRVdCZFkxYTVVb0RIRk8zUlkKCqMw9oL9RaYBV5Hhy3o8Nm5xmGrPH8Sd
|
by9kZFlTRVdCZFkxYTVVb0RIRk8zUlkKCqMw9oL9RaYBV5Hhy3o8Nm5xmGrPH8Sd
|
||||||
hv36sxRFFNZT/DCKaHaSRbT3mfpBZSTXJt1dgl4nZe6whH54t/1KmA==
|
hv36sxRFFNZT/DCKaHaSRbT3mfpBZSTXJt1dgl4nZe6whH54t/1KmA==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2023-05-18T18:34:18Z"
|
lastmodified: "2023-05-18T19:31:18Z"
|
||||||
mac: ENC[AES256_GCM,data:9+5M/rTOhL/DXtSYLRjjvF8smhvWQgVE/XLSjwBxv/fCR89ArFNPsf6TC1PCP9eUZb0eThoxr1zkDSBOJD57wRpIbhmDe3/UyNpPTQLnu7Hnj/nvRJYpX49TYhfdT4CH3IzP+m0sopnCvo8DYgNIOKpz7Lhi4x/eCNP8gDzfv4k=,iv:lbXxenPw8EghEvDh8OwMlVfN3gFEa+wT2/Fai/xrzuo=,tag:b+HzElhvF1Rr3BAyLiPQsQ==,type:str]
|
mac: ENC[AES256_GCM,data:yhPCOXDC8MzyKbzWOv/7XTNgeNQEprI8j6Q2u4BFoAmTmWDC8QdGs5dEesqmIaOkTGv5qEUJTfUwsHSZ2tVGW7RuwnCd9LVBkOjP89V0XGmEfb+HBjBWql94utGgRS0qTcDxF/nSUUuyhyL/TIqsvmy6wt75JdDTqSWPcAtVmwY=,iv:xOby+a3Zf+meI8LhomLJkrg5p3SeA+mCRUqCtNLl7ig=,tag:2PwUgrSLVH5JEHdrVs7BFg==,type:str]
|
||||||
pgp: []
|
pgp: []
|
||||||
unencrypted_suffix: _unencrypted
|
unencrypted_suffix: _unencrypted
|
||||||
version: 3.7.3
|
version: 3.7.3
|
||||||
|
|
79
nki-personal-do/writefreely.nix
Normal file
79
nki-personal-do/writefreely.nix
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
{ config, pkgs, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
host = "blog.dtth.ch";
|
||||||
|
port = 18074;
|
||||||
|
|
||||||
|
user = "writefreely-dtth";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./writefreely/module.nix ];
|
||||||
|
# traefik
|
||||||
|
cloud.traefik.hosts.writefreely-dtth = { inherit host port; };
|
||||||
|
|
||||||
|
sops.secrets."writefreely-dtth" = { owner = user; };
|
||||||
|
|
||||||
|
users.users.${user} = {
|
||||||
|
isSystemUser = true;
|
||||||
|
home = "${config.fileSystems.data.mountPoint}/writefreely-dtth";
|
||||||
|
createHome = true;
|
||||||
|
group = user;
|
||||||
|
};
|
||||||
|
users.groups.${user} = { };
|
||||||
|
|
||||||
|
nki.services.writefreely = {
|
||||||
|
inherit host user;
|
||||||
|
enable = true;
|
||||||
|
package = pkgs.unstable.writefreely;
|
||||||
|
|
||||||
|
group = user;
|
||||||
|
|
||||||
|
stateDir = "${config.fileSystems.data.mountPoint}/writefreely-dtth";
|
||||||
|
settings = {
|
||||||
|
server.port = port;
|
||||||
|
app = {
|
||||||
|
host = "https://${host}";
|
||||||
|
|
||||||
|
site_name = "DTTH Blog";
|
||||||
|
site_description = "Blogs from members of DTTH";
|
||||||
|
editor = "pad";
|
||||||
|
|
||||||
|
landing = "/read";
|
||||||
|
local_timeline = true;
|
||||||
|
default_visibility = "public";
|
||||||
|
|
||||||
|
open_registration = true;
|
||||||
|
disable_password_auth = true;
|
||||||
|
max_blogs = 5;
|
||||||
|
user_invites = "admin";
|
||||||
|
min_username_len = 3;
|
||||||
|
|
||||||
|
federation = true;
|
||||||
|
wf_modesty = true;
|
||||||
|
public_stats = true;
|
||||||
|
monetization = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
"oauth.generic" = {
|
||||||
|
client_id = "rpoTTr2Wz0h4EgOSCHe0G85O8DCQDMup7JW9U9fV";
|
||||||
|
host = "https://auth.dtth.ch";
|
||||||
|
display_name = "DTTH";
|
||||||
|
token_endpoint = "/application/o/token/";
|
||||||
|
inspect_endpoint = "/application/o/userinfo/";
|
||||||
|
auth_endpoint = "/application/o/authorize/";
|
||||||
|
scope = "email openid profile";
|
||||||
|
map_user_id = "nickname";
|
||||||
|
map_username = "preferred_username";
|
||||||
|
map_display_name = "name";
|
||||||
|
allow_registration = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
extraSettingsFile = config.sops.secrets."writefreely-dtth".path;
|
||||||
|
|
||||||
|
database.type = "sqlite3";
|
||||||
|
|
||||||
|
admin.name = "nki";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
500
nki-personal-do/writefreely/module.nix
Normal file
500
nki-personal-do/writefreely/module.nix
Normal file
|
@ -0,0 +1,500 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
inherit (builtins) toString;
|
||||||
|
inherit (lib) types mkIf mkOption mkDefault;
|
||||||
|
inherit (lib) optional optionals optionalAttrs optionalString;
|
||||||
|
|
||||||
|
inherit (pkgs) sqlite;
|
||||||
|
|
||||||
|
format = pkgs.formats.ini {
|
||||||
|
mkKeyValue = key: value:
|
||||||
|
let
|
||||||
|
value' = lib.optionalString (value != null)
|
||||||
|
(if builtins.isBool value then
|
||||||
|
if value == true then "true" else "false"
|
||||||
|
else
|
||||||
|
toString value);
|
||||||
|
in
|
||||||
|
"${key} = ${value'}";
|
||||||
|
};
|
||||||
|
|
||||||
|
cfg = config.nki.services.writefreely;
|
||||||
|
|
||||||
|
isSqlite = cfg.database.type == "sqlite3";
|
||||||
|
isMysql = cfg.database.type == "mysql";
|
||||||
|
isMysqlLocal = isMysql && cfg.database.createLocally == true;
|
||||||
|
|
||||||
|
hostProtocol = if cfg.acme.enable then "https" else "http";
|
||||||
|
|
||||||
|
settings = cfg.settings // {
|
||||||
|
app = cfg.settings.app or { } // {
|
||||||
|
host = cfg.settings.app.host or "${hostProtocol}://${cfg.host}";
|
||||||
|
};
|
||||||
|
|
||||||
|
database =
|
||||||
|
if cfg.database.type == "sqlite3" then {
|
||||||
|
type = "sqlite3";
|
||||||
|
filename = cfg.settings.database.filename or "writefreely.db";
|
||||||
|
database = cfg.database.name;
|
||||||
|
} else {
|
||||||
|
type = "mysql";
|
||||||
|
username = cfg.database.user;
|
||||||
|
password = "#dbpass#";
|
||||||
|
database = cfg.database.name;
|
||||||
|
host = cfg.database.host;
|
||||||
|
port = cfg.database.port;
|
||||||
|
tls = cfg.database.tls;
|
||||||
|
};
|
||||||
|
|
||||||
|
server = cfg.settings.server or { } // {
|
||||||
|
bind = cfg.settings.server.bind or "localhost";
|
||||||
|
gopher_port = cfg.settings.server.gopher_port or 0;
|
||||||
|
autocert = !cfg.nginx.enable && cfg.acme.enable;
|
||||||
|
templates_parent_dir =
|
||||||
|
cfg.settings.server.templates_parent_dir or cfg.package.src;
|
||||||
|
static_parent_dir = cfg.settings.server.static_parent_dir or assets;
|
||||||
|
pages_parent_dir =
|
||||||
|
cfg.settings.server.pages_parent_dir or cfg.package.src;
|
||||||
|
keys_parent_dir = cfg.settings.server.keys_parent_dir or cfg.stateDir;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
configFile = format.generate "config.ini" settings;
|
||||||
|
|
||||||
|
assets = pkgs.stdenvNoCC.mkDerivation {
|
||||||
|
pname = "writefreely-assets";
|
||||||
|
|
||||||
|
inherit (cfg.package) version src;
|
||||||
|
|
||||||
|
nativeBuildInputs = with pkgs.nodePackages; [ less ];
|
||||||
|
|
||||||
|
buildPhase = ''
|
||||||
|
mkdir -p $out
|
||||||
|
|
||||||
|
cp -r static $out/
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
less_dir=$src/less
|
||||||
|
css_dir=$out/static/css
|
||||||
|
|
||||||
|
lessc $less_dir/app.less $css_dir/write.css
|
||||||
|
lessc $less_dir/fonts.less $css_dir/fonts.css
|
||||||
|
lessc $less_dir/icons.less $css_dir/icons.css
|
||||||
|
lessc $less_dir/prose.less $css_dir/prose.css
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
withConfigFile = text: ''
|
||||||
|
db_pass=${
|
||||||
|
optionalString (cfg.database.passwordFile != null)
|
||||||
|
"$(head -n1 ${cfg.database.passwordFile})"
|
||||||
|
}
|
||||||
|
|
||||||
|
install -m 0660 ${configFile} '${cfg.stateDir}/config.ini'
|
||||||
|
sed -e "s,#dbpass#,$db_pass,g" -i '${cfg.stateDir}/config.ini'
|
||||||
|
${if cfg.extraSettingsFile != null then "cat ${cfg.extraSettingsFile} >> ${cfg.stateDir}/config.ini" else ""}
|
||||||
|
chmod 440 '${cfg.stateDir}/config.ini'
|
||||||
|
|
||||||
|
${text}
|
||||||
|
'';
|
||||||
|
|
||||||
|
withMysql = text:
|
||||||
|
withConfigFile ''
|
||||||
|
query () {
|
||||||
|
local result=$(${config.services.mysql.package}/bin/mysql \
|
||||||
|
--user=${cfg.database.user} \
|
||||||
|
--password=$db_pass \
|
||||||
|
--database=${cfg.database.name} \
|
||||||
|
--silent \
|
||||||
|
--raw \
|
||||||
|
--skip-column-names \
|
||||||
|
--execute "$1" \
|
||||||
|
)
|
||||||
|
|
||||||
|
echo $result
|
||||||
|
}
|
||||||
|
|
||||||
|
${text}
|
||||||
|
'';
|
||||||
|
|
||||||
|
withSqlite = text:
|
||||||
|
withConfigFile ''
|
||||||
|
query () {
|
||||||
|
local result=$(${sqlite}/bin/sqlite3 \
|
||||||
|
'${cfg.stateDir}/${settings.database.filename}'
|
||||||
|
"$1" \
|
||||||
|
)
|
||||||
|
|
||||||
|
echo $result
|
||||||
|
}
|
||||||
|
|
||||||
|
${text}
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.nki.services.writefreely = {
|
||||||
|
enable =
|
||||||
|
lib.mkEnableOption (lib.mdDoc "Writefreely, build a digital writing community");
|
||||||
|
|
||||||
|
package = lib.mkOption {
|
||||||
|
type = lib.types.package;
|
||||||
|
default = pkgs.writefreely;
|
||||||
|
defaultText = lib.literalExpression "pkgs.writefreely";
|
||||||
|
description = lib.mdDoc "Writefreely package to use.";
|
||||||
|
};
|
||||||
|
|
||||||
|
stateDir = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = "/var/lib/writefreely";
|
||||||
|
description = lib.mdDoc "The state directory where keys and data are stored.";
|
||||||
|
};
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "writefreely";
|
||||||
|
description = lib.mdDoc "User under which Writefreely is ran.";
|
||||||
|
};
|
||||||
|
|
||||||
|
group = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "writefreely";
|
||||||
|
description = lib.mdDoc "Group under which Writefreely is ran.";
|
||||||
|
};
|
||||||
|
|
||||||
|
host = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "";
|
||||||
|
description = lib.mdDoc "The public host name to serve.";
|
||||||
|
example = "example.com";
|
||||||
|
};
|
||||||
|
|
||||||
|
extraSettingsFile = mkOption {
|
||||||
|
type = types.nullOr types.path;
|
||||||
|
default = null;
|
||||||
|
description = lib.mdDoc "Additional configs to be appended to the config file";
|
||||||
|
};
|
||||||
|
|
||||||
|
settings = mkOption {
|
||||||
|
default = { };
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
Writefreely configuration ({file}`config.ini`). Refer to
|
||||||
|
<https://writefreely.org/docs/latest/admin/config>
|
||||||
|
for details.
|
||||||
|
'';
|
||||||
|
|
||||||
|
type = types.submodule {
|
||||||
|
freeformType = format.type;
|
||||||
|
|
||||||
|
options = {
|
||||||
|
app = {
|
||||||
|
theme = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "write";
|
||||||
|
description = lib.mdDoc "The theme to apply.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
server = {
|
||||||
|
port = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
default = if cfg.nginx.enable then 18080 else 80;
|
||||||
|
defaultText = "80";
|
||||||
|
description = lib.mdDoc "The port WriteFreely should listen on.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
database = {
|
||||||
|
type = mkOption {
|
||||||
|
type = types.enum [ "sqlite3" "mysql" ];
|
||||||
|
default = "sqlite3";
|
||||||
|
description = lib.mdDoc "The database provider to use.";
|
||||||
|
};
|
||||||
|
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "writefreely";
|
||||||
|
description = lib.mdDoc "The name of the database to store data in.";
|
||||||
|
};
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = if cfg.database.type == "mysql" then "writefreely" else null;
|
||||||
|
defaultText = "writefreely";
|
||||||
|
description = lib.mdDoc "The database user to connect as.";
|
||||||
|
};
|
||||||
|
|
||||||
|
passwordFile = mkOption {
|
||||||
|
type = types.nullOr types.path;
|
||||||
|
default = null;
|
||||||
|
description = lib.mdDoc "The file to load the database password from.";
|
||||||
|
};
|
||||||
|
|
||||||
|
host = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "localhost";
|
||||||
|
description = lib.mdDoc "The database host to connect to.";
|
||||||
|
};
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
default = 3306;
|
||||||
|
description = lib.mdDoc "The port used when connecting to the database host.";
|
||||||
|
};
|
||||||
|
|
||||||
|
tls = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description =
|
||||||
|
lib.mdDoc "Whether or not TLS should be used for the database connection.";
|
||||||
|
};
|
||||||
|
|
||||||
|
migrate = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description =
|
||||||
|
lib.mdDoc "Whether or not to automatically run migrations on startup.";
|
||||||
|
};
|
||||||
|
|
||||||
|
createLocally = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
When {option}`services.writefreely.database.type` is set to
|
||||||
|
`"mysql"`, this option will enable the MySQL service locally.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
admin = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
description = lib.mdDoc "The name of the first admin user.";
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
initialPasswordFile = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
Path to a file containing the initial password for the admin user.
|
||||||
|
If not provided, the default password will be set to `nixos`.
|
||||||
|
'';
|
||||||
|
default = pkgs.writeText "default-admin-pass" "nixos";
|
||||||
|
defaultText = "/nix/store/xxx-default-admin-pass";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nginx = {
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description =
|
||||||
|
lib.mdDoc "Whether or not to enable and configure nginx as a proxy for WriteFreely.";
|
||||||
|
};
|
||||||
|
|
||||||
|
forceSSL = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = lib.mdDoc "Whether or not to force the use of SSL.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
acme = {
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description =
|
||||||
|
lib.mdDoc "Whether or not to automatically fetch and configure SSL certs.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = cfg.host != "";
|
||||||
|
message = "services.writefreely.host must be set";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = isMysqlLocal -> cfg.database.passwordFile != null;
|
||||||
|
message =
|
||||||
|
"services.writefreely.database.passwordFile must be set if services.writefreely.database.createLocally is set to true";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = isSqlite -> !cfg.database.createLocally;
|
||||||
|
message =
|
||||||
|
"services.writefreely.database.createLocally has no use when services.writefreely.database.type is set to sqlite3";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
users = {
|
||||||
|
users = optionalAttrs (cfg.user == "writefreely") {
|
||||||
|
writefreely = {
|
||||||
|
group = cfg.group;
|
||||||
|
home = cfg.stateDir;
|
||||||
|
isSystemUser = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
groups =
|
||||||
|
optionalAttrs (cfg.group == "writefreely") { writefreely = { }; };
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.tmpfiles.rules =
|
||||||
|
[ "d '${cfg.stateDir}' 0750 ${cfg.user} ${cfg.group} - -" ];
|
||||||
|
|
||||||
|
systemd.services.writefreely = {
|
||||||
|
path = with pkgs; [ openssl ];
|
||||||
|
after = [ "network.target" ]
|
||||||
|
++ optional isSqlite "writefreely-sqlite-init.service"
|
||||||
|
++ optional isMysql "writefreely-mysql-init.service"
|
||||||
|
++ optional isMysqlLocal "mysql.service";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "simple";
|
||||||
|
User = cfg.user;
|
||||||
|
Group = cfg.group;
|
||||||
|
WorkingDirectory = cfg.stateDir;
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = 20;
|
||||||
|
ExecStart =
|
||||||
|
"${cfg.package}/bin/writefreely -c '${cfg.stateDir}/config.ini' serve";
|
||||||
|
AmbientCapabilities =
|
||||||
|
optionalString (settings.server.port < 1024) "cap_net_bind_service";
|
||||||
|
};
|
||||||
|
|
||||||
|
preStart = ''
|
||||||
|
if ! test -d "${cfg.stateDir}/keys"; then
|
||||||
|
mkdir -p ${cfg.stateDir}/keys
|
||||||
|
|
||||||
|
# Key files end up with the wrong permissions by default.
|
||||||
|
# We need to correct them so that Writefreely can read them.
|
||||||
|
chmod -R 750 "${cfg.stateDir}/keys"
|
||||||
|
|
||||||
|
${cfg.package}/bin/writefreely -c '${cfg.stateDir}/config.ini' keys generate
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.writefreely-sqlite-init = mkIf isSqlite {
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
User = cfg.user;
|
||||||
|
Group = cfg.group;
|
||||||
|
WorkingDirectory = cfg.stateDir;
|
||||||
|
ReadOnlyPaths = optional (cfg.admin.initialPasswordFile != null)
|
||||||
|
cfg.admin.initialPasswordFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
script =
|
||||||
|
let
|
||||||
|
migrateDatabase = optionalString cfg.database.migrate ''
|
||||||
|
${cfg.package}/bin/writefreely -c '${cfg.stateDir}/config.ini' db migrate
|
||||||
|
'';
|
||||||
|
|
||||||
|
createAdmin = optionalString (cfg.admin.name != null) ''
|
||||||
|
if [[ $(query "SELECT COUNT(*) FROM users") == 0 ]]; then
|
||||||
|
admin_pass=$(head -n1 ${cfg.admin.initialPasswordFile})
|
||||||
|
|
||||||
|
${cfg.package}/bin/writefreely -c '${cfg.stateDir}/config.ini' --create-admin ${cfg.admin.name}:$admin_pass
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
withSqlite ''
|
||||||
|
if ! test -f '${settings.database.filename}'; then
|
||||||
|
${cfg.package}/bin/writefreely -c '${cfg.stateDir}/config.ini' db init
|
||||||
|
fi
|
||||||
|
|
||||||
|
${migrateDatabase}
|
||||||
|
|
||||||
|
${createAdmin}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.writefreely-mysql-init = mkIf isMysql {
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
after = optional isMysqlLocal "mysql.service";
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
User = cfg.user;
|
||||||
|
Group = cfg.group;
|
||||||
|
WorkingDirectory = cfg.stateDir;
|
||||||
|
ReadOnlyPaths = optional isMysqlLocal cfg.database.passwordFile
|
||||||
|
++ optional (cfg.admin.initialPasswordFile != null)
|
||||||
|
cfg.admin.initialPasswordFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
script =
|
||||||
|
let
|
||||||
|
updateUser = optionalString isMysqlLocal ''
|
||||||
|
# WriteFreely currently *requires* a password for authentication, so we
|
||||||
|
# need to update the user in MySQL accordingly. By default MySQL users
|
||||||
|
# authenticate with auth_socket or unix_socket.
|
||||||
|
# See: https://github.com/writefreely/writefreely/issues/568
|
||||||
|
${config.services.mysql.package}/bin/mysql --skip-column-names --execute "ALTER USER '${cfg.database.user}'@'localhost' IDENTIFIED VIA unix_socket OR mysql_native_password USING PASSWORD('$db_pass'); FLUSH PRIVILEGES;"
|
||||||
|
'';
|
||||||
|
|
||||||
|
migrateDatabase = optionalString cfg.database.migrate ''
|
||||||
|
${cfg.package}/bin/writefreely -c '${cfg.stateDir}/config.ini' db migrate
|
||||||
|
'';
|
||||||
|
|
||||||
|
createAdmin = optionalString (cfg.admin.name != null) ''
|
||||||
|
if [[ $(query 'SELECT COUNT(*) FROM users') == 0 ]]; then
|
||||||
|
admin_pass=$(head -n1 ${cfg.admin.initialPasswordFile})
|
||||||
|
${cfg.package}/bin/writefreely -c '${cfg.stateDir}/config.ini' --create-admin ${cfg.admin.name}:$admin_pass
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
withMysql ''
|
||||||
|
${updateUser}
|
||||||
|
|
||||||
|
if [[ $(query "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '${cfg.database.name}'") == 0 ]]; then
|
||||||
|
${cfg.package}/bin/writefreely -c '${cfg.stateDir}/config.ini' db init
|
||||||
|
fi
|
||||||
|
|
||||||
|
${migrateDatabase}
|
||||||
|
|
||||||
|
${createAdmin}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
services.mysql = mkIf isMysqlLocal {
|
||||||
|
enable = true;
|
||||||
|
package = mkDefault pkgs.mariadb;
|
||||||
|
ensureDatabases = [ cfg.database.name ];
|
||||||
|
ensureUsers = [{
|
||||||
|
name = cfg.database.user;
|
||||||
|
ensurePermissions = {
|
||||||
|
"${cfg.database.name}.*" = "ALL PRIVILEGES";
|
||||||
|
# WriteFreely requires the use of passwords, so we need permissions
|
||||||
|
# to `ALTER` the user to add password support and also to reload
|
||||||
|
# permissions so they can be used.
|
||||||
|
"*.*" = "CREATE USER, RELOAD";
|
||||||
|
};
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.nginx = lib.mkIf cfg.nginx.enable {
|
||||||
|
enable = true;
|
||||||
|
recommendedProxySettings = true;
|
||||||
|
|
||||||
|
virtualHosts."${cfg.host}" = {
|
||||||
|
enableACME = cfg.acme.enable;
|
||||||
|
forceSSL = cfg.nginx.forceSSL;
|
||||||
|
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://127.0.0.1:${toString settings.server.port}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue