Something went wrong. Try again.
What happens if I push all of nixpkgs
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506{ 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.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; [ lessc ];
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})" }
cp -f ${configFile} '${cfg.stateDir}/config.ini' sed -e "s,#dbpass#,$db_pass,g" -i '${cfg.stateDir}/config.ini' 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.services.writefreely = { enable = lib.mkEnableOption "Writefreely, build a digital writing community";
package = lib.mkOption { type = lib.types.package; default = pkgs.writefreely; defaultText = lib.literalExpression "pkgs.writefreely"; description = "Writefreely package to use."; };
stateDir = mkOption { type = types.path; default = "/var/lib/writefreely"; description = "The state directory where keys and data are stored."; };
user = mkOption { type = types.str; default = "writefreely"; description = "User under which Writefreely is ran."; };
group = mkOption { type = types.str; default = "writefreely"; description = "Group under which Writefreely is ran."; };
host = mkOption { type = types.str; default = ""; description = "The public host name to serve."; example = "example.com"; };
settings = mkOption { default = { }; description = '' 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 = "The theme to apply."; }; };
server = { port = mkOption { type = types.port; default = if cfg.nginx.enable then 18080 else 80; defaultText = "80"; description = "The port WriteFreely should listen on."; }; }; }; }; };
database = { type = mkOption { type = types.enum [ "sqlite3" "mysql" ]; default = "sqlite3"; description = "The database provider to use."; };
name = mkOption { type = types.str; default = "writefreely"; description = "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 = "The database user to connect as."; };
passwordFile = mkOption { type = types.nullOr types.path; default = null; description = "The file to load the database password from."; };
host = mkOption { type = types.str; default = "localhost"; description = "The database host to connect to."; };
port = mkOption { type = types.port; default = 3306; description = "The port used when connecting to the database host."; };
tls = mkOption { type = types.bool; default = false; description = "Whether or not TLS should be used for the database connection."; };
migrate = mkOption { type = types.bool; default = true; description = "Whether or not to automatically run migrations on startup."; };
createLocally = mkOption { type = types.bool; default = false; description = '' 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 = "The name of the first admin user."; default = null; };
initialPasswordFile = mkOption { type = types.path; description = '' 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 = "Whether or not to enable and configure nginx as a proxy for WriteFreely."; };
forceSSL = mkOption { type = types.bool; default = false; description = "Whether or not to force the use of SSL."; }; };
acme = { enable = mkOption { type = types.bool; default = false; description = "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.settings."10-writefreely".${cfg.stateDir}.d = { inherit (cfg) user group; mode = "0750"; };
systemd.services.writefreely = { 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}"; }; }; }; };}