momo: NixOS 23.11, fix build (treefmt), move to flake-parts #262
13
flake.nix
13
flake.nix
|
@ -36,8 +36,8 @@
|
||||||
erpnext.inputs.agenix.follows = "agenix";
|
erpnext.inputs.agenix.follows = "agenix";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = inputs@{ self, ...}:
|
outputs = inputs @ {self, ...}:
|
||||||
inputs.flake-parts.lib.mkFlake { inherit inputs; } {
|
inputs.flake-parts.lib.mkFlake {inherit inputs;} {
|
||||||
debug = true;
|
debug = true;
|
||||||
systems = [
|
systems = [
|
||||||
"x86_64-linux"
|
"x86_64-linux"
|
||||||
|
@ -52,7 +52,12 @@
|
||||||
./overlays
|
./overlays
|
||||||
];
|
];
|
||||||
|
|
||||||
perSystem = args@{ system, pkgs, config, ... }: {
|
perSystem = args @ {
|
||||||
|
system,
|
||||||
|
pkgs,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
_module.args = {
|
_module.args = {
|
||||||
inherit inputs;
|
inherit inputs;
|
||||||
pkgs = import inputs.nixpkgs {
|
pkgs = import inputs.nixpkgs {
|
||||||
|
@ -61,7 +66,7 @@
|
||||||
inputs.agenix.overlays.default
|
inputs.agenix.overlays.default
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
unstable = import inputs.unstable { inherit system; };
|
unstable = import inputs.unstable {inherit system;};
|
||||||
};
|
};
|
||||||
|
|
||||||
devShells.default = pkgs.mkShell {
|
devShells.default = pkgs.mkShell {
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
{ self, inputs, ...}:
|
|
||||||
{
|
{
|
||||||
|
self,
|
||||||
|
inputs,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
flake = {
|
flake = {
|
||||||
nixosConfigurations = {
|
nixosConfigurations = {
|
||||||
pioneer-momo-koeln = self.nixos-flake.lib.mkLinuxSystem {
|
pioneer-momo-koeln = self.nixos-flake.lib.mkLinuxSystem {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{ ... }: {
|
{...}: {
|
||||||
imports = [
|
imports = [
|
||||||
./configuration.nix
|
./configuration.nix
|
||||||
./hardware-configuration.nix
|
./hardware-configuration.nix
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{ lib }:
|
{lib}: hostnames: {
|
||||||
hostnames: {
|
|
||||||
"127.0.0.1" = hostnames;
|
"127.0.0.1" = hostnames;
|
||||||
"::1" = hostnames;
|
"::1" = hostnames;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
{ lib, inputs, ... }: {
|
{
|
||||||
|
lib,
|
||||||
|
inputs,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
# Configuration common to all Linux systems
|
# Configuration common to all Linux systems
|
||||||
flake = {
|
flake = {
|
||||||
lib = let
|
lib = let
|
||||||
|
@ -10,9 +14,8 @@
|
||||||
#foo = callLibs ./foo.nix;
|
#foo = callLibs ./foo.nix;
|
||||||
## In configs, they can be used under "lib.our"
|
## In configs, they can be used under "lib.our"
|
||||||
|
|
||||||
deploy = import ./deploy.nix { inherit inputs lib; };
|
deploy = import ./deploy.nix {inherit inputs lib;};
|
||||||
addLocalHostname = callLibs ./add-local-hostname.nix;
|
addLocalHostname = callLibs ./add-local-hostname.nix;
|
||||||
recursiveMerge = callLibs ./recursive-merge.nix;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
/*
|
/*
|
||||||
* The contents of this file are adapted from digga
|
* The contents of this file are adapted from digga
|
||||||
* https://github.com/divnix/digga
|
* https://github.com/divnix/digga
|
||||||
*
|
*
|
||||||
* Licensed under the MIT license
|
* Licensed under the MIT license
|
||||||
*/
|
*/
|
||||||
|
{
|
||||||
{ lib, inputs }: let
|
lib,
|
||||||
|
inputs,
|
||||||
|
}: let
|
||||||
getFqdn = c: let
|
getFqdn = c: let
|
||||||
net = c.config.networking;
|
net = c.config.networking;
|
||||||
fqdn =
|
fqdn =
|
||||||
|
@ -17,35 +19,35 @@
|
||||||
in {
|
in {
|
||||||
mkDeployNodes = systemConfigurations: extraConfig:
|
mkDeployNodes = systemConfigurations: extraConfig:
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
Synopsis: mkNodes _systemConfigurations_ _extraConfig_
|
Synopsis: mkNodes _systemConfigurations_ _extraConfig_
|
||||||
|
|
||||||
Generate the `nodes` attribute expected by deploy-rs
|
Generate the `nodes` attribute expected by deploy-rs
|
||||||
where _systemConfigurations_ are `nodes`.
|
where _systemConfigurations_ are `nodes`.
|
||||||
|
|
||||||
_systemConfigurations_ should take the form of a flake's
|
_systemConfigurations_ should take the form of a flake's
|
||||||
_nixosConfigurations_. Note that deploy-rs does not currently support
|
_nixosConfigurations_. Note that deploy-rs does not currently support
|
||||||
deploying to darwin hosts.
|
deploying to darwin hosts.
|
||||||
|
|
||||||
_extraConfig_, if specified, will be merged into each of the
|
_extraConfig_, if specified, will be merged into each of the
|
||||||
nodes' configurations.
|
nodes' configurations.
|
||||||
|
|
||||||
Example _systemConfigurations_ input:
|
Example _systemConfigurations_ input:
|
||||||
|
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
hostname-1 = {
|
hostname-1 = {
|
||||||
fastConnection = true;
|
fastConnection = true;
|
||||||
sshOpts = [ "-p" "25" ];
|
sshOpts = [ "-p" "25" ];
|
||||||
};
|
};
|
||||||
hostname-2 = {
|
hostname-2 = {
|
||||||
sshOpts = [ "-p" "19999" ];
|
sshOpts = [ "-p" "19999" ];
|
||||||
sshUser = "root";
|
sshUser = "root";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
lib.recursiveUpdate
|
lib.recursiveUpdate
|
||||||
(lib.mapAttrs
|
(lib.mapAttrs
|
||||||
(
|
(
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
{ lib }:
|
|
||||||
attrList:
|
|
||||||
let
|
|
||||||
f = attrPath:
|
|
||||||
zipAttrsWith (
|
|
||||||
n: values:
|
|
||||||
if tail values == []
|
|
||||||
then head values
|
|
||||||
else if all isList values
|
|
||||||
then unique (concatLists values)
|
|
||||||
else if all isAttrs values
|
|
||||||
then f (attrPath ++ [n]) values
|
|
||||||
else last values
|
|
||||||
);
|
|
||||||
in
|
|
||||||
f [] attrList;
|
|
|
@ -3,16 +3,24 @@
|
||||||
pkgs,
|
pkgs,
|
||||||
lib,
|
lib,
|
||||||
...
|
...
|
||||||
}:
|
}: let
|
||||||
let
|
|
||||||
cfg = config.services.ddclient;
|
cfg = config.services.ddclient;
|
||||||
boolToStr = bool: if bool then "yes" else "no";
|
boolToStr = bool:
|
||||||
|
if bool
|
||||||
|
then "yes"
|
||||||
|
else "no";
|
||||||
dataDir = "/var/lib/ddclient";
|
dataDir = "/var/lib/ddclient";
|
||||||
StateDirectory = builtins.baseNameOf dataDir;
|
StateDirectory = builtins.baseNameOf dataDir;
|
||||||
RuntimeDirectory = StateDirectory;
|
RuntimeDirectory = StateDirectory;
|
||||||
|
|
||||||
usev4 = if cfg.usev4 != "" then "usev4=${cfg.usev4}" else "";
|
usev4 =
|
||||||
usev6 = if cfg.usev6 != "" then "usev6=${cfg.usev6}" else "";
|
if cfg.usev4 != ""
|
||||||
|
then "usev4=${cfg.usev4}"
|
||||||
|
else "";
|
||||||
|
usev6 =
|
||||||
|
if cfg.usev6 != ""
|
||||||
|
then "usev6=${cfg.usev6}"
|
||||||
|
else "";
|
||||||
|
|
||||||
configFile' = pkgs.writeText "ddclient.conf" ''
|
configFile' = pkgs.writeText "ddclient.conf" ''
|
||||||
# This file can be used as a template for configFile or is automatically generated by Nix options.
|
# This file can be used as a template for configFile or is automatically generated by Nix options.
|
||||||
|
@ -22,11 +30,15 @@ let
|
||||||
cache=${dataDir}/ddclient.cache
|
cache=${dataDir}/ddclient.cache
|
||||||
foreground=yes
|
foreground=yes
|
||||||
login=${cfg.username}
|
login=${cfg.username}
|
||||||
password=${if cfg.protocol == "nsupdate" then "/run/${RuntimeDirectory}/ddclient.key" else "@password_placeholder@"}
|
password=${
|
||||||
|
if cfg.protocol == "nsupdate"
|
||||||
|
then "/run/${RuntimeDirectory}/ddclient.key"
|
||||||
|
else "@password_placeholder@"
|
||||||
|
}
|
||||||
protocol=${cfg.protocol}
|
protocol=${cfg.protocol}
|
||||||
${lib.optionalString (cfg.script != "") "script=${cfg.script}"}
|
${lib.optionalString (cfg.script != "") "script=${cfg.script}"}
|
||||||
${lib.optionalString (cfg.server != "") "server=${cfg.server}"}
|
${lib.optionalString (cfg.server != "") "server=${cfg.server}"}
|
||||||
${lib.optionalString (cfg.zone != "") "zone=${cfg.zone}"}
|
${lib.optionalString (cfg.zone != "") "zone=${cfg.zone}"}
|
||||||
ssl=${boolToStr cfg.ssl}
|
ssl=${boolToStr cfg.ssl}
|
||||||
wildcard=yes
|
wildcard=yes
|
||||||
quiet=${boolToStr cfg.quiet}
|
quiet=${boolToStr cfg.quiet}
|
||||||
|
@ -34,212 +46,224 @@ let
|
||||||
${cfg.extraConfig}
|
${cfg.extraConfig}
|
||||||
${lib.concatStringsSep "," cfg.domains}
|
${lib.concatStringsSep "," cfg.domains}
|
||||||
'';
|
'';
|
||||||
configFile = if (cfg.configFile != null) then cfg.configFile else configFile';
|
configFile =
|
||||||
|
if (cfg.configFile != null)
|
||||||
|
then cfg.configFile
|
||||||
|
else configFile';
|
||||||
|
|
||||||
preStart = ''
|
preStart = ''
|
||||||
install --mode=600 --owner=$USER ${configFile} /run/${RuntimeDirectory}/ddclient.conf
|
install --mode=600 --owner=$USER ${configFile} /run/${RuntimeDirectory}/ddclient.conf
|
||||||
${lib.optionalString (cfg.configFile == null) (if (cfg.protocol == "nsupdate") then ''
|
${lib.optionalString (cfg.configFile == null) (
|
||||||
install --mode=600 --owner=$USER ${cfg.passwordFile} /run/${RuntimeDirectory}/ddclient.key
|
if (cfg.protocol == "nsupdate")
|
||||||
'' else if (cfg.passwordFile != null) then ''
|
then ''
|
||||||
"${pkgs.replace-secret}/bin/replace-secret" "@password_placeholder@" "${cfg.passwordFile}" "/run/${RuntimeDirectory}/ddclient.conf"
|
install --mode=600 --owner=$USER ${cfg.passwordFile} /run/${RuntimeDirectory}/ddclient.key
|
||||||
'' else ''
|
''
|
||||||
sed -i '/^password=@password_placeholder@$/d' /run/${RuntimeDirectory}/ddclient.conf
|
else if (cfg.passwordFile != null)
|
||||||
'')}
|
then ''
|
||||||
|
"${pkgs.replace-secret}/bin/replace-secret" "@password_placeholder@" "${cfg.passwordFile}" "/run/${RuntimeDirectory}/ddclient.conf"
|
||||||
|
''
|
||||||
|
else ''
|
||||||
|
sed -i '/^password=@password_placeholder@$/d' /run/${RuntimeDirectory}/ddclient.conf
|
||||||
|
''
|
||||||
|
)}
|
||||||
'';
|
'';
|
||||||
in with lib; {
|
in
|
||||||
disabledModules = [
|
with lib; {
|
||||||
"services/networking/ddclient.nix"
|
disabledModules = [
|
||||||
];
|
"services/networking/ddclient.nix"
|
||||||
|
];
|
||||||
|
|
||||||
imports = [
|
imports = [
|
||||||
(mkChangedOptionModule [ "services" "ddclient" "domain" ] [ "services" "ddclient" "domains" ]
|
(mkChangedOptionModule ["services" "ddclient" "domain"] ["services" "ddclient" "domains"]
|
||||||
(config:
|
(config: let
|
||||||
let value = getAttrFromPath [ "services" "ddclient" "domain" ] config;
|
value = getAttrFromPath ["services" "ddclient" "domain"] config;
|
||||||
in if value != "" then [ value ] else []))
|
in
|
||||||
(mkRemovedOptionModule [ "services" "ddclient" "homeDir" ] "")
|
if value != ""
|
||||||
(mkRemovedOptionModule [ "services" "ddclient" "password" ] "Use services.ddclient.passwordFile instead.")
|
then [value]
|
||||||
];
|
else []))
|
||||||
|
(mkRemovedOptionModule ["services" "ddclient" "homeDir"] "")
|
||||||
|
(mkRemovedOptionModule ["services" "ddclient" "password"] "Use services.ddclient.passwordFile instead.")
|
||||||
|
];
|
||||||
|
|
||||||
###### interface
|
###### interface
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
services.ddclient = with lib.types; {
|
services.ddclient = with lib.types; {
|
||||||
enable = mkOption {
|
enable = mkOption {
|
||||||
default = false;
|
default = false;
|
||||||
type = bool;
|
type = bool;
|
||||||
description = lib.mdDoc ''
|
description = lib.mdDoc ''
|
||||||
Whether to synchronise your machine's IP address with a dynamic DNS provider (e.g. dyndns.org).
|
Whether to synchronise your machine's IP address with a dynamic DNS provider (e.g. dyndns.org).
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
package = mkOption {
|
package = mkOption {
|
||||||
type = package;
|
type = package;
|
||||||
default = pkgs.ddclient;
|
default = pkgs.ddclient;
|
||||||
defaultText = lib.literalExpression "pkgs.ddclient";
|
defaultText = lib.literalExpression "pkgs.ddclient";
|
||||||
description = lib.mdDoc ''
|
description = lib.mdDoc ''
|
||||||
The ddclient executable package run by the service.
|
The ddclient executable package run by the service.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
domains = mkOption {
|
domains = mkOption {
|
||||||
default = [ "" ];
|
default = [""];
|
||||||
type = listOf str;
|
type = listOf str;
|
||||||
description = lib.mdDoc ''
|
description = lib.mdDoc ''
|
||||||
Domain name(s) to synchronize.
|
Domain name(s) to synchronize.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
username = mkOption {
|
username = mkOption {
|
||||||
# For `nsupdate` username contains the path to the nsupdate executable
|
# For `nsupdate` username contains the path to the nsupdate executable
|
||||||
default = lib.optionalString (config.services.ddclient.protocol == "nsupdate") "${pkgs.bind.dnsutils}/bin/nsupdate";
|
default = lib.optionalString (config.services.ddclient.protocol == "nsupdate") "${pkgs.bind.dnsutils}/bin/nsupdate";
|
||||||
defaultText = "";
|
defaultText = "";
|
||||||
type = str;
|
type = str;
|
||||||
description = lib.mdDoc ''
|
description = lib.mdDoc ''
|
||||||
User name.
|
User name.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
passwordFile = mkOption {
|
passwordFile = mkOption {
|
||||||
default = null;
|
default = null;
|
||||||
type = nullOr str;
|
type = nullOr str;
|
||||||
description = lib.mdDoc ''
|
description = lib.mdDoc ''
|
||||||
A file containing the password or a TSIG key in named format when using the nsupdate protocol.
|
A file containing the password or a TSIG key in named format when using the nsupdate protocol.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
interval = mkOption {
|
interval = mkOption {
|
||||||
default = "10min";
|
default = "10min";
|
||||||
type = str;
|
type = str;
|
||||||
description = lib.mdDoc ''
|
description = lib.mdDoc ''
|
||||||
The interval at which to run the check and update.
|
The interval at which to run the check and update.
|
||||||
See {command}`man 7 systemd.time` for the format.
|
See {command}`man 7 systemd.time` for the format.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
configFile = mkOption {
|
configFile = mkOption {
|
||||||
default = null;
|
default = null;
|
||||||
type = nullOr path;
|
type = nullOr path;
|
||||||
description = lib.mdDoc ''
|
description = lib.mdDoc ''
|
||||||
Path to configuration file.
|
Path to configuration file.
|
||||||
When set this overrides the generated configuration from module options.
|
When set this overrides the generated configuration from module options.
|
||||||
'';
|
'';
|
||||||
example = "/root/nixos/secrets/ddclient.conf";
|
example = "/root/nixos/secrets/ddclient.conf";
|
||||||
};
|
};
|
||||||
|
|
||||||
protocol = mkOption {
|
protocol = mkOption {
|
||||||
default = "dyndns2";
|
default = "dyndns2";
|
||||||
type = str;
|
type = str;
|
||||||
description = lib.mdDoc ''
|
description = lib.mdDoc ''
|
||||||
Protocol to use with dynamic DNS provider (see https://sourceforge.net/p/ddclient/wiki/protocols).
|
Protocol to use with dynamic DNS provider (see https://sourceforge.net/p/ddclient/wiki/protocols).
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
server = mkOption {
|
server = mkOption {
|
||||||
default = "";
|
default = "";
|
||||||
type = str;
|
type = str;
|
||||||
description = lib.mdDoc ''
|
description = lib.mdDoc ''
|
||||||
Server address.
|
Server address.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
ssl = mkOption {
|
ssl = mkOption {
|
||||||
default = true;
|
default = true;
|
||||||
type = bool;
|
type = bool;
|
||||||
description = lib.mdDoc ''
|
description = lib.mdDoc ''
|
||||||
Whether to use SSL/TLS to connect to dynamic DNS provider.
|
Whether to use SSL/TLS to connect to dynamic DNS provider.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
quiet = mkOption {
|
quiet = mkOption {
|
||||||
default = false;
|
default = false;
|
||||||
type = bool;
|
type = bool;
|
||||||
description = lib.mdDoc ''
|
description = lib.mdDoc ''
|
||||||
Print no messages for unnecessary updates.
|
Print no messages for unnecessary updates.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
script = mkOption {
|
script = mkOption {
|
||||||
default = "";
|
default = "";
|
||||||
type = str;
|
type = str;
|
||||||
description = lib.mdDoc ''
|
description = lib.mdDoc ''
|
||||||
script as required by some providers.
|
script as required by some providers.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
usev4 = mkOption {
|
usev4 = mkOption {
|
||||||
default = "webv4, webv4=checkip.dyndns.com/, webv4-skip='Current IP Address: '";
|
default = "webv4, webv4=checkip.dyndns.com/, webv4-skip='Current IP Address: '";
|
||||||
type = str;
|
type = str;
|
||||||
description = lib.mdDoc ''
|
description = lib.mdDoc ''
|
||||||
Method to determine the IP address to send to the dynamic DNS provider.
|
Method to determine the IP address to send to the dynamic DNS provider.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
usev6 = mkOption {
|
usev6 = mkOption {
|
||||||
default = "";
|
default = "";
|
||||||
type = str;
|
type = str;
|
||||||
description = lib.mdDoc ''
|
description = lib.mdDoc ''
|
||||||
Method to determine the IP address to send to the dynamic DNS provider.
|
Method to determine the IP address to send to the dynamic DNS provider.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
verbose = mkOption {
|
verbose = mkOption {
|
||||||
default = false;
|
default = false;
|
||||||
type = bool;
|
type = bool;
|
||||||
description = lib.mdDoc ''
|
description = lib.mdDoc ''
|
||||||
Print verbose information.
|
Print verbose information.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
zone = mkOption {
|
zone = mkOption {
|
||||||
default = "";
|
default = "";
|
||||||
type = str;
|
type = str;
|
||||||
description = lib.mdDoc ''
|
description = lib.mdDoc ''
|
||||||
zone as required by some providers.
|
zone as required by some providers.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
extraConfig = mkOption {
|
extraConfig = mkOption {
|
||||||
default = "";
|
default = "";
|
||||||
type = lines;
|
type = lines;
|
||||||
description = lib.mdDoc ''
|
description = lib.mdDoc ''
|
||||||
Extra configuration. Contents will be added verbatim to the configuration file.
|
Extra configuration. Contents will be added verbatim to the configuration file.
|
||||||
|
|
||||||
::: {.note}
|
::: {.note}
|
||||||
`daemon` should not be added here because it does not work great with the systemd-timer approach the service uses.
|
`daemon` should not be added here because it does not work great with the systemd-timer approach the service uses.
|
||||||
:::
|
:::
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
###### implementation
|
|
||||||
|
|
||||||
config = mkIf config.services.ddclient.enable {
|
|
||||||
systemd.services.ddclient = {
|
|
||||||
description = "Dynamic DNS Client";
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
after = [ "network.target" ];
|
|
||||||
restartTriggers = optional (cfg.configFile != null) cfg.configFile;
|
|
||||||
|
|
||||||
serviceConfig = {
|
|
||||||
DynamicUser = true;
|
|
||||||
RuntimeDirectoryMode = "0700";
|
|
||||||
inherit RuntimeDirectory;
|
|
||||||
inherit StateDirectory;
|
|
||||||
Type = "oneshot";
|
|
||||||
ExecStartPre = "!${pkgs.writeShellScript "ddclient-prestart" preStart}";
|
|
||||||
ExecStart = "${lib.getBin cfg.package}/bin/ddclient -file /run/${RuntimeDirectory}/ddclient.conf";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.timers.ddclient = {
|
###### implementation
|
||||||
description = "Run ddclient";
|
|
||||||
wantedBy = [ "timers.target" ];
|
config = mkIf config.services.ddclient.enable {
|
||||||
timerConfig = {
|
systemd.services.ddclient = {
|
||||||
OnBootSec = cfg.interval;
|
description = "Dynamic DNS Client";
|
||||||
OnUnitInactiveSec = cfg.interval;
|
wantedBy = ["multi-user.target"];
|
||||||
|
after = ["network.target"];
|
||||||
|
restartTriggers = optional (cfg.configFile != null) cfg.configFile;
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
DynamicUser = true;
|
||||||
|
RuntimeDirectoryMode = "0700";
|
||||||
|
inherit RuntimeDirectory;
|
||||||
|
inherit StateDirectory;
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStartPre = "!${pkgs.writeShellScript "ddclient-prestart" preStart}";
|
||||||
|
ExecStart = "${lib.getBin cfg.package}/bin/ddclient -file /run/${RuntimeDirectory}/ddclient.conf";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.timers.ddclient = {
|
||||||
|
description = "Run ddclient";
|
||||||
|
wantedBy = ["timers.target"];
|
||||||
|
timerConfig = {
|
||||||
|
OnBootSec = cfg.interval;
|
||||||
|
OnUnitInactiveSec = cfg.interval;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -29,13 +29,13 @@
|
||||||
else ""
|
else ""
|
||||||
)
|
)
|
||||||
+ ''
|
+ ''
|
||||||
bindsym l exec ${pkgs.swaylock-bg}/bin/swaylock-bg, mode "default"
|
bindsym l exec ${pkgs.swaylock-bg}/bin/swaylock-bg, mode "default"
|
||||||
bindsym s exec systemctl suspend, mode "default"
|
bindsym s exec systemctl suspend, mode "default"
|
||||||
bindsym r exec systemctl reboot, mode "default"
|
bindsym r exec systemctl reboot, mode "default"
|
||||||
bindsym Shift+s exec systemctl poweroff, mode "default"
|
bindsym Shift+s exec systemctl poweroff, mode "default"
|
||||||
|
|
||||||
# exit system mode: "Enter" or "Escape"
|
# exit system mode: "Enter" or "Escape"
|
||||||
bindsym Return mode "default"
|
bindsym Return mode "default"
|
||||||
bindsym Escape mode "default"
|
bindsym Escape mode "default"
|
||||||
}
|
}
|
||||||
''
|
''
|
||||||
|
|
|
@ -24,9 +24,10 @@ in {
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; mkIf psCfg.graphical.v4l2loopback.enable [
|
environment.systemPackages = with pkgs;
|
||||||
linuxPackages.v4l2loopback
|
mkIf psCfg.graphical.v4l2loopback.enable [
|
||||||
];
|
linuxPackages.v4l2loopback
|
||||||
|
];
|
||||||
|
|
||||||
programs.sway.enable = true;
|
programs.sway.enable = true;
|
||||||
|
|
||||||
|
|
|
@ -1,215 +1,221 @@
|
||||||
{ config, pkgs, lib, ... }:
|
{
|
||||||
|
config,
|
||||||
with lib;
|
pkgs,
|
||||||
|
lib,
|
||||||
let
|
...
|
||||||
|
}:
|
||||||
|
with lib; let
|
||||||
cfg = config.services.invoiceplane;
|
cfg = config.services.invoiceplane;
|
||||||
eachSite = cfg.sites;
|
eachSite = cfg.sites;
|
||||||
user = "invoiceplane";
|
user = "invoiceplane";
|
||||||
webserver = config.services.${cfg.webserver};
|
webserver = config.services.${cfg.webserver};
|
||||||
|
|
||||||
invoiceplane-config = hostName: cfg: pkgs.writeText "ipconfig.php" ''
|
invoiceplane-config = hostName: cfg:
|
||||||
IP_URL=http://${hostName}
|
pkgs.writeText "ipconfig.php" ''
|
||||||
ENABLE_DEBUG=false
|
IP_URL=http://${hostName}
|
||||||
DISABLE_SETUP=false
|
ENABLE_DEBUG=false
|
||||||
REMOVE_INDEXPHP=false
|
DISABLE_SETUP=false
|
||||||
DB_HOSTNAME=${cfg.database.host}
|
REMOVE_INDEXPHP=false
|
||||||
DB_USERNAME=${cfg.database.user}
|
DB_HOSTNAME=${cfg.database.host}
|
||||||
# NOTE: file_get_contents adds newline at the end of returned string
|
DB_USERNAME=${cfg.database.user}
|
||||||
DB_PASSWORD=${if cfg.database.passwordFile == null then "" else "trim(file_get_contents('${cfg.database.passwordFile}'),\"\\r\\n\")"}
|
# NOTE: file_get_contents adds newline at the end of returned string
|
||||||
DB_DATABASE=${cfg.database.name}
|
DB_PASSWORD=${
|
||||||
DB_PORT=${toString cfg.database.port}
|
if cfg.database.passwordFile == null
|
||||||
SESS_EXPIRATION=864000
|
then ""
|
||||||
ENABLE_INVOICE_DELETION=false
|
else "trim(file_get_contents('${cfg.database.passwordFile}'),\"\\r\\n\")"
|
||||||
DISABLE_READ_ONLY=false
|
}
|
||||||
ENCRYPTION_KEY=
|
DB_DATABASE=${cfg.database.name}
|
||||||
ENCRYPTION_CIPHER=AES-256
|
DB_PORT=${toString cfg.database.port}
|
||||||
SETUP_COMPLETED=false
|
SESS_EXPIRATION=864000
|
||||||
REMOVE_INDEXPHP=true
|
ENABLE_INVOICE_DELETION=false
|
||||||
'';
|
DISABLE_READ_ONLY=false
|
||||||
|
ENCRYPTION_KEY=
|
||||||
extraConfig = hostName: cfg: pkgs.writeText "extraConfig.php" ''
|
ENCRYPTION_CIPHER=AES-256
|
||||||
${toString cfg.extraConfig}
|
SETUP_COMPLETED=false
|
||||||
'';
|
REMOVE_INDEXPHP=true
|
||||||
|
|
||||||
pkg = hostName: cfg: pkgs.stdenv.mkDerivation rec {
|
|
||||||
pname = "invoiceplane-${hostName}";
|
|
||||||
version = src.version;
|
|
||||||
src = pkgs.invoiceplane;
|
|
||||||
|
|
||||||
postPhase = ''
|
|
||||||
# Patch index.php file to load additional config file
|
|
||||||
substituteInPlace index.php \
|
|
||||||
--replace "require('vendor/autoload.php');" "require('vendor/autoload.php'); \$dotenv = Dotenv\Dotenv::createImmutable(__DIR__, 'extraConfig.php'); \$dotenv->load();";
|
|
||||||
'';
|
'';
|
||||||
|
|
||||||
installPhase = ''
|
extraConfig = hostName: cfg:
|
||||||
mkdir -p $out
|
pkgs.writeText "extraConfig.php" ''
|
||||||
cp -r * $out/
|
${toString cfg.extraConfig}
|
||||||
|
|
||||||
# symlink uploads and log directories
|
|
||||||
rm -r $out/uploads $out/application/logs $out/vendor/mpdf/mpdf/tmp
|
|
||||||
ln -sf ${cfg.stateDir}/uploads $out/
|
|
||||||
ln -sf ${cfg.stateDir}/logs $out/application/
|
|
||||||
ln -sf ${cfg.stateDir}/tmp $out/vendor/mpdf/mpdf/
|
|
||||||
|
|
||||||
# symlink the InvoicePlane config
|
|
||||||
ln -s ${cfg.stateDir}/ipconfig.php $out/ipconfig.php
|
|
||||||
|
|
||||||
# symlink the extraConfig file
|
|
||||||
ln -s ${extraConfig hostName cfg} $out/extraConfig.php
|
|
||||||
|
|
||||||
# symlink additional templates
|
|
||||||
${concatMapStringsSep "\n" (template: "cp -r ${template}/. $out/application/views/invoice_templates/pdf/") cfg.invoiceTemplates}
|
|
||||||
'';
|
'';
|
||||||
};
|
|
||||||
|
|
||||||
siteOpts = { lib, name, ... }:
|
pkg = hostName: cfg:
|
||||||
{
|
pkgs.stdenv.mkDerivation rec {
|
||||||
options = {
|
pname = "invoiceplane-${hostName}";
|
||||||
|
version = src.version;
|
||||||
|
src = pkgs.invoiceplane;
|
||||||
|
|
||||||
enable = mkEnableOption (lib.mdDoc "InvoicePlane web application");
|
postPhase = ''
|
||||||
|
# Patch index.php file to load additional config file
|
||||||
|
substituteInPlace index.php \
|
||||||
|
--replace "require('vendor/autoload.php');" "require('vendor/autoload.php'); \$dotenv = Dotenv\Dotenv::createImmutable(__DIR__, 'extraConfig.php'); \$dotenv->load();";
|
||||||
|
'';
|
||||||
|
|
||||||
stateDir = mkOption {
|
installPhase = ''
|
||||||
type = types.path;
|
mkdir -p $out
|
||||||
default = "/var/lib/invoiceplane/${name}";
|
cp -r * $out/
|
||||||
description = lib.mdDoc ''
|
|
||||||
This directory is used for uploads of attachments and cache.
|
|
||||||
The directory passed here is automatically created and permissions
|
|
||||||
adjusted as required.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
database = {
|
# symlink uploads and log directories
|
||||||
host = mkOption {
|
rm -r $out/uploads $out/application/logs $out/vendor/mpdf/mpdf/tmp
|
||||||
type = types.str;
|
ln -sf ${cfg.stateDir}/uploads $out/
|
||||||
default = "localhost";
|
ln -sf ${cfg.stateDir}/logs $out/application/
|
||||||
description = lib.mdDoc "Database host address.";
|
ln -sf ${cfg.stateDir}/tmp $out/vendor/mpdf/mpdf/
|
||||||
};
|
|
||||||
|
|
||||||
port = mkOption {
|
# symlink the InvoicePlane config
|
||||||
type = types.port;
|
ln -s ${cfg.stateDir}/ipconfig.php $out/ipconfig.php
|
||||||
default = 3306;
|
|
||||||
description = lib.mdDoc "Database host port.";
|
|
||||||
};
|
|
||||||
|
|
||||||
name = mkOption {
|
# symlink the extraConfig file
|
||||||
type = types.str;
|
ln -s ${extraConfig hostName cfg} $out/extraConfig.php
|
||||||
default = "invoiceplane";
|
|
||||||
description = lib.mdDoc "Database name.";
|
|
||||||
};
|
|
||||||
|
|
||||||
user = mkOption {
|
# symlink additional templates
|
||||||
type = types.str;
|
${concatMapStringsSep "\n" (template: "cp -r ${template}/. $out/application/views/invoice_templates/pdf/") cfg.invoiceTemplates}
|
||||||
default = "invoiceplane";
|
'';
|
||||||
description = lib.mdDoc "Database user.";
|
};
|
||||||
};
|
|
||||||
|
|
||||||
passwordFile = mkOption {
|
siteOpts = {
|
||||||
type = types.nullOr types.path;
|
lib,
|
||||||
default = null;
|
name,
|
||||||
example = "/run/keys/invoiceplane-dbpassword";
|
...
|
||||||
description = lib.mdDoc ''
|
}: {
|
||||||
A file containing the password corresponding to
|
options = {
|
||||||
{option}`database.user`.
|
enable = mkEnableOption (lib.mdDoc "InvoicePlane web application");
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
createLocally = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = true;
|
|
||||||
description = lib.mdDoc "Create the database and database user locally.";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
invoiceTemplates = mkOption {
|
|
||||||
type = types.listOf types.path;
|
|
||||||
default = [];
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
List of path(s) to respective template(s) which are copied from the 'invoice_templates/pdf' directory.
|
|
||||||
|
|
||||||
::: {.note}
|
|
||||||
These templates need to be packaged before use, see example.
|
|
||||||
:::
|
|
||||||
'';
|
|
||||||
example = literalExpression ''
|
|
||||||
let
|
|
||||||
# Let's package an example template
|
|
||||||
template-vtdirektmarketing = pkgs.stdenv.mkDerivation {
|
|
||||||
name = "vtdirektmarketing";
|
|
||||||
# Download the template from a public repository
|
|
||||||
src = pkgs.fetchgit {
|
|
||||||
url = "https://git.project-insanity.org/onny/invoiceplane-vtdirektmarketing.git";
|
|
||||||
sha256 = "1hh0q7wzsh8v8x03i82p6qrgbxr4v5fb05xylyrpp975l8axyg2z";
|
|
||||||
};
|
|
||||||
sourceRoot = ".";
|
|
||||||
# Installing simply means copying template php file to the output directory
|
|
||||||
installPhase = ""
|
|
||||||
mkdir -p $out
|
|
||||||
cp invoiceplane-vtdirektmarketing/vtdirektmarketing.php $out/
|
|
||||||
"";
|
|
||||||
};
|
|
||||||
# And then pass this package to the template list like this:
|
|
||||||
in [ template-vtdirektmarketing ]
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
poolConfig = mkOption {
|
|
||||||
type = with types; attrsOf (oneOf [ str int bool ]);
|
|
||||||
default = {
|
|
||||||
"pm" = "dynamic";
|
|
||||||
"pm.max_children" = 32;
|
|
||||||
"pm.start_servers" = 2;
|
|
||||||
"pm.min_spare_servers" = 2;
|
|
||||||
"pm.max_spare_servers" = 4;
|
|
||||||
"pm.max_requests" = 500;
|
|
||||||
};
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
Options for the InvoicePlane PHP pool. See the documentation on `php-fpm.conf`
|
|
||||||
for details on configuration directives.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
extraConfig = mkOption {
|
|
||||||
type = types.nullOr types.lines;
|
|
||||||
default = null;
|
|
||||||
example = ''
|
|
||||||
SETUP_COMPLETED=true
|
|
||||||
DISABLE_SETUP=true
|
|
||||||
IP_URL=https://invoice.example.com
|
|
||||||
'';
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
InvoicePlane configuration. Refer to
|
|
||||||
<https://github.com/InvoicePlane/InvoicePlane/blob/master/ipconfig.php.example>
|
|
||||||
for details on supported values.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
cron = {
|
|
||||||
|
|
||||||
enable = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
Enable cron service which periodically runs Invoiceplane tasks.
|
|
||||||
Requires key taken from the administration page. Refer to
|
|
||||||
<https://wiki.invoiceplane.com/en/1.0/modules/recurring-invoices>
|
|
||||||
on how to configure it.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
key = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
description = lib.mdDoc "Cron key taken from the administration page.";
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
stateDir = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = "/var/lib/invoiceplane/${name}";
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
This directory is used for uploads of attachments and cache.
|
||||||
|
The directory passed here is automatically created and permissions
|
||||||
|
adjusted as required.
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
database = {
|
||||||
|
host = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "localhost";
|
||||||
|
description = lib.mdDoc "Database host address.";
|
||||||
|
};
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
default = 3306;
|
||||||
|
description = lib.mdDoc "Database host port.";
|
||||||
|
};
|
||||||
|
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "invoiceplane";
|
||||||
|
description = lib.mdDoc "Database name.";
|
||||||
|
};
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "invoiceplane";
|
||||||
|
description = lib.mdDoc "Database user.";
|
||||||
|
};
|
||||||
|
|
||||||
|
passwordFile = mkOption {
|
||||||
|
type = types.nullOr types.path;
|
||||||
|
default = null;
|
||||||
|
example = "/run/keys/invoiceplane-dbpassword";
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
A file containing the password corresponding to
|
||||||
|
{option}`database.user`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
createLocally = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = lib.mdDoc "Create the database and database user locally.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
invoiceTemplates = mkOption {
|
||||||
|
type = types.listOf types.path;
|
||||||
|
default = [];
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
List of path(s) to respective template(s) which are copied from the 'invoice_templates/pdf' directory.
|
||||||
|
|
||||||
|
::: {.note}
|
||||||
|
These templates need to be packaged before use, see example.
|
||||||
|
:::
|
||||||
|
'';
|
||||||
|
example = literalExpression ''
|
||||||
|
let
|
||||||
|
# Let's package an example template
|
||||||
|
template-vtdirektmarketing = pkgs.stdenv.mkDerivation {
|
||||||
|
name = "vtdirektmarketing";
|
||||||
|
# Download the template from a public repository
|
||||||
|
src = pkgs.fetchgit {
|
||||||
|
url = "https://git.project-insanity.org/onny/invoiceplane-vtdirektmarketing.git";
|
||||||
|
sha256 = "1hh0q7wzsh8v8x03i82p6qrgbxr4v5fb05xylyrpp975l8axyg2z";
|
||||||
|
};
|
||||||
|
sourceRoot = ".";
|
||||||
|
# Installing simply means copying template php file to the output directory
|
||||||
|
installPhase = ""
|
||||||
|
mkdir -p $out
|
||||||
|
cp invoiceplane-vtdirektmarketing/vtdirektmarketing.php $out/
|
||||||
|
"";
|
||||||
|
};
|
||||||
|
# And then pass this package to the template list like this:
|
||||||
|
in [ template-vtdirektmarketing ]
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
poolConfig = mkOption {
|
||||||
|
type = with types; attrsOf (oneOf [str int bool]);
|
||||||
|
default = {
|
||||||
|
"pm" = "dynamic";
|
||||||
|
"pm.max_children" = 32;
|
||||||
|
"pm.start_servers" = 2;
|
||||||
|
"pm.min_spare_servers" = 2;
|
||||||
|
"pm.max_spare_servers" = 4;
|
||||||
|
"pm.max_requests" = 500;
|
||||||
|
};
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
Options for the InvoicePlane PHP pool. See the documentation on `php-fpm.conf`
|
||||||
|
for details on configuration directives.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
extraConfig = mkOption {
|
||||||
|
type = types.nullOr types.lines;
|
||||||
|
default = null;
|
||||||
|
example = ''
|
||||||
|
SETUP_COMPLETED=true
|
||||||
|
DISABLE_SETUP=true
|
||||||
|
IP_URL=https://invoice.example.com
|
||||||
|
'';
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
InvoicePlane configuration. Refer to
|
||||||
|
<https://github.com/InvoicePlane/InvoicePlane/blob/master/ipconfig.php.example>
|
||||||
|
for details on supported values.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
cron = {
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
Enable cron service which periodically runs Invoiceplane tasks.
|
||||||
|
Requires key taken from the administration page. Refer to
|
||||||
|
<https://wiki.invoiceplane.com/en/1.0/modules/recurring-invoices>
|
||||||
|
on how to configure it.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
key = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = lib.mdDoc "Cron key taken from the administration page.";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
in
|
};
|
||||||
{
|
in {
|
||||||
disabledModules = [
|
disabledModules = [
|
||||||
"services/web-apps/invoiceplane.nix"
|
"services/web-apps/invoiceplane.nix"
|
||||||
];
|
];
|
||||||
|
@ -218,7 +224,6 @@ in
|
||||||
options = {
|
options = {
|
||||||
services.invoiceplane = mkOption {
|
services.invoiceplane = mkOption {
|
||||||
type = types.submodule {
|
type = types.submodule {
|
||||||
|
|
||||||
options.sites = mkOption {
|
options.sites = mkOption {
|
||||||
type = types.attrsOf (types.submodule siteOpts);
|
type = types.attrsOf (types.submodule siteOpts);
|
||||||
default = {};
|
default = {};
|
||||||
|
@ -226,7 +231,7 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
options.webserver = mkOption {
|
options.webserver = mkOption {
|
||||||
type = types.enum [ "caddy" ];
|
type = types.enum ["caddy"];
|
||||||
default = "caddy";
|
default = "caddy";
|
||||||
description = lib.mdDoc ''
|
description = lib.mdDoc ''
|
||||||
Which webserver to use for virtual host management. Currently only
|
Which webserver to use for virtual host management. Currently only
|
||||||
|
@ -237,126 +242,136 @@ in
|
||||||
default = {};
|
default = {};
|
||||||
description = lib.mdDoc "InvoicePlane configuration.";
|
description = lib.mdDoc "InvoicePlane configuration.";
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# implementation
|
# implementation
|
||||||
config = mkIf (eachSite != {}) (mkMerge [{
|
config = mkIf (eachSite != {}) (mkMerge [
|
||||||
|
{
|
||||||
|
assertions = flatten (mapAttrsToList (hostName: cfg: [
|
||||||
|
{
|
||||||
|
assertion = cfg.database.createLocally -> cfg.database.user == user;
|
||||||
|
message = ''services.invoiceplane.sites."${hostName}".database.user must be ${user} if the database is to be automatically provisioned'';
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
|
||||||
|
message = ''services.invoiceplane.sites."${hostName}".database.passwordFile cannot be specified if services.invoiceplane.sites."${hostName}".database.createLocally is set to true.'';
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = cfg.cron.enable -> cfg.cron.key != null;
|
||||||
|
message = ''services.invoiceplane.sites."${hostName}".cron.key must be set in order to use cron service.'';
|
||||||
|
}
|
||||||
|
])
|
||||||
|
eachSite);
|
||||||
|
|
||||||
assertions = flatten (mapAttrsToList (hostName: cfg:
|
services.mysql = mkIf (any (v: v.database.createLocally) (attrValues eachSite)) {
|
||||||
[{ assertion = cfg.database.createLocally -> cfg.database.user == user;
|
enable = true;
|
||||||
message = ''services.invoiceplane.sites."${hostName}".database.user must be ${user} if the database is to be automatically provisioned'';
|
package = mkDefault pkgs.mariadb;
|
||||||
}
|
ensureDatabases = mapAttrsToList (hostName: cfg: cfg.database.name) eachSite;
|
||||||
{ assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
|
ensureUsers =
|
||||||
message = ''services.invoiceplane.sites."${hostName}".database.passwordFile cannot be specified if services.invoiceplane.sites."${hostName}".database.createLocally is set to true.'';
|
mapAttrsToList (
|
||||||
}
|
hostName: cfg: {
|
||||||
{ assertion = cfg.cron.enable -> cfg.cron.key != null;
|
name = cfg.database.user;
|
||||||
message = ''services.invoiceplane.sites."${hostName}".cron.key must be set in order to use cron service.'';
|
ensurePermissions = {"${cfg.database.name}.*" = "ALL PRIVILEGES";};
|
||||||
}
|
}
|
||||||
]) eachSite);
|
)
|
||||||
|
eachSite;
|
||||||
|
};
|
||||||
|
|
||||||
services.mysql = mkIf (any (v: v.database.createLocally) (attrValues eachSite)) {
|
services.phpfpm = {
|
||||||
enable = true;
|
phpPackage = pkgs.php81;
|
||||||
package = mkDefault pkgs.mariadb;
|
pools =
|
||||||
ensureDatabases = mapAttrsToList (hostName: cfg: cfg.database.name) eachSite;
|
mapAttrs' (hostName: cfg: (
|
||||||
ensureUsers = mapAttrsToList (hostName: cfg:
|
nameValuePair "invoiceplane-${hostName}" {
|
||||||
{ name = cfg.database.user;
|
inherit user;
|
||||||
ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
|
group = webserver.group;
|
||||||
}
|
settings =
|
||||||
) eachSite;
|
{
|
||||||
};
|
"listen.owner" = webserver.user;
|
||||||
|
"listen.group" = webserver.group;
|
||||||
|
}
|
||||||
|
// cfg.poolConfig;
|
||||||
|
}
|
||||||
|
))
|
||||||
|
eachSite;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
services.phpfpm = {
|
{
|
||||||
phpPackage = pkgs.php81;
|
systemd.tmpfiles.rules = flatten (mapAttrsToList (hostName: cfg: [
|
||||||
pools = mapAttrs' (hostName: cfg: (
|
"d ${cfg.stateDir} 0750 ${user} ${webserver.group} - -"
|
||||||
nameValuePair "invoiceplane-${hostName}" {
|
"f ${cfg.stateDir}/ipconfig.php 0750 ${user} ${webserver.group} - -"
|
||||||
inherit user;
|
"d ${cfg.stateDir}/logs 0750 ${user} ${webserver.group} - -"
|
||||||
group = webserver.group;
|
"d ${cfg.stateDir}/uploads 0750 ${user} ${webserver.group} - -"
|
||||||
settings = {
|
"d ${cfg.stateDir}/uploads/archive 0750 ${user} ${webserver.group} - -"
|
||||||
"listen.owner" = webserver.user;
|
"d ${cfg.stateDir}/uploads/customer_files 0750 ${user} ${webserver.group} - -"
|
||||||
"listen.group" = webserver.group;
|
"d ${cfg.stateDir}/uploads/temp 0750 ${user} ${webserver.group} - -"
|
||||||
} // cfg.poolConfig;
|
"d ${cfg.stateDir}/uploads/temp/mpdf 0750 ${user} ${webserver.group} - -"
|
||||||
}
|
"d ${cfg.stateDir}/tmp 0750 ${user} ${webserver.group} - -"
|
||||||
)) eachSite;
|
])
|
||||||
};
|
eachSite);
|
||||||
|
|
||||||
}
|
systemd.services.invoiceplane-config = {
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
script = concatStrings (mapAttrsToList (hostName: cfg: ''
|
||||||
|
mkdir -p ${cfg.stateDir}/logs \
|
||||||
|
${cfg.stateDir}/uploads
|
||||||
|
if ! grep -q IP_URL "${cfg.stateDir}/ipconfig.php"; then
|
||||||
|
cp "${invoiceplane-config hostName cfg}" "${cfg.stateDir}/ipconfig.php"
|
||||||
|
fi
|
||||||
|
'')
|
||||||
|
eachSite);
|
||||||
|
wantedBy = ["multi-user.target"];
|
||||||
|
};
|
||||||
|
|
||||||
{
|
users.users.${user} = {
|
||||||
|
group = webserver.group;
|
||||||
|
isSystemUser = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
# Cron service implementation
|
||||||
|
|
||||||
systemd.tmpfiles.rules = flatten (mapAttrsToList (hostName: cfg: [
|
systemd.timers =
|
||||||
"d ${cfg.stateDir} 0750 ${user} ${webserver.group} - -"
|
mapAttrs' (hostName: cfg: (
|
||||||
"f ${cfg.stateDir}/ipconfig.php 0750 ${user} ${webserver.group} - -"
|
nameValuePair "invoiceplane-cron-${hostName}" (mkIf cfg.cron.enable {
|
||||||
"d ${cfg.stateDir}/logs 0750 ${user} ${webserver.group} - -"
|
wantedBy = ["timers.target"];
|
||||||
"d ${cfg.stateDir}/uploads 0750 ${user} ${webserver.group} - -"
|
timerConfig = {
|
||||||
"d ${cfg.stateDir}/uploads/archive 0750 ${user} ${webserver.group} - -"
|
OnBootSec = "5m";
|
||||||
"d ${cfg.stateDir}/uploads/customer_files 0750 ${user} ${webserver.group} - -"
|
OnUnitActiveSec = "5m";
|
||||||
"d ${cfg.stateDir}/uploads/temp 0750 ${user} ${webserver.group} - -"
|
Unit = "invoiceplane-cron-${hostName}.service";
|
||||||
"d ${cfg.stateDir}/uploads/temp/mpdf 0750 ${user} ${webserver.group} - -"
|
};
|
||||||
"d ${cfg.stateDir}/tmp 0750 ${user} ${webserver.group} - -"
|
})
|
||||||
]) eachSite);
|
))
|
||||||
|
eachSite;
|
||||||
|
|
||||||
systemd.services.invoiceplane-config = {
|
systemd.services =
|
||||||
serviceConfig.Type = "oneshot";
|
mapAttrs' (hostName: cfg: (
|
||||||
script = concatStrings (mapAttrsToList (hostName: cfg:
|
nameValuePair "invoiceplane-cron-${hostName}" (mkIf cfg.cron.enable {
|
||||||
''
|
serviceConfig = {
|
||||||
mkdir -p ${cfg.stateDir}/logs \
|
Type = "oneshot";
|
||||||
${cfg.stateDir}/uploads
|
User = user;
|
||||||
if ! grep -q IP_URL "${cfg.stateDir}/ipconfig.php"; then
|
ExecStart = "${pkgs.curl}/bin/curl --header 'Host: ${hostName}' http://localhost/invoices/cron/recur/${cfg.cron.key}";
|
||||||
cp "${invoiceplane-config hostName cfg}" "${cfg.stateDir}/ipconfig.php"
|
};
|
||||||
fi
|
})
|
||||||
'') eachSite);
|
))
|
||||||
wantedBy = [ "multi-user.target" ];
|
eachSite;
|
||||||
};
|
}
|
||||||
|
|
||||||
users.users.${user} = {
|
|
||||||
group = webserver.group;
|
|
||||||
isSystemUser = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
{
|
|
||||||
|
|
||||||
# Cron service implementation
|
|
||||||
|
|
||||||
systemd.timers = mapAttrs' (hostName: cfg: (
|
|
||||||
nameValuePair "invoiceplane-cron-${hostName}" (mkIf cfg.cron.enable {
|
|
||||||
wantedBy = [ "timers.target" ];
|
|
||||||
timerConfig = {
|
|
||||||
OnBootSec = "5m";
|
|
||||||
OnUnitActiveSec = "5m";
|
|
||||||
Unit = "invoiceplane-cron-${hostName}.service";
|
|
||||||
};
|
|
||||||
})
|
|
||||||
)) eachSite;
|
|
||||||
|
|
||||||
systemd.services =
|
|
||||||
mapAttrs' (hostName: cfg: (
|
|
||||||
nameValuePair "invoiceplane-cron-${hostName}" (mkIf cfg.cron.enable {
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
User = user;
|
|
||||||
ExecStart = "${pkgs.curl}/bin/curl --header 'Host: ${hostName}' http://localhost/invoices/cron/recur/${cfg.cron.key}";
|
|
||||||
};
|
|
||||||
})
|
|
||||||
)) eachSite;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
(mkIf (cfg.webserver == "caddy") {
|
|
||||||
services.caddy = {
|
|
||||||
enable = true;
|
|
||||||
virtualHosts = mapAttrs' (hostName: cfg: (
|
|
||||||
nameValuePair "http://${hostName}" {
|
|
||||||
extraConfig = ''
|
|
||||||
root * ${pkg hostName cfg}
|
|
||||||
file_server
|
|
||||||
php_fastcgi unix/${config.services.phpfpm.pools."invoiceplane-${hostName}".socket}
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
)) eachSite;
|
|
||||||
};
|
|
||||||
})
|
|
||||||
|
|
||||||
|
(mkIf (cfg.webserver == "caddy") {
|
||||||
|
services.caddy = {
|
||||||
|
enable = true;
|
||||||
|
virtualHosts =
|
||||||
|
mapAttrs' (hostName: cfg: (
|
||||||
|
nameValuePair "http://${hostName}" {
|
||||||
|
extraConfig = ''
|
||||||
|
root * ${pkg hostName cfg}
|
||||||
|
file_server
|
||||||
|
php_fastcgi unix/${config.services.phpfpm.pools."invoiceplane-${hostName}".socket}
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
))
|
||||||
|
eachSite;
|
||||||
|
};
|
||||||
|
})
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,15 +5,16 @@
|
||||||
flake,
|
flake,
|
||||||
...
|
...
|
||||||
}: {
|
}: {
|
||||||
nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
|
nixpkgs.config.allowUnfreePredicate = pkg:
|
||||||
"1password"
|
builtins.elem (lib.getName pkg) [
|
||||||
"1password-cli"
|
"1password"
|
||||||
"cups-brother-hl3140cw"
|
"1password-cli"
|
||||||
"facetimehd-firmware"
|
"cups-brother-hl3140cw"
|
||||||
"slack"
|
"facetimehd-firmware"
|
||||||
"veracrypt"
|
"slack"
|
||||||
"zoom"
|
"veracrypt"
|
||||||
];
|
"zoom"
|
||||||
|
];
|
||||||
|
|
||||||
nix = {
|
nix = {
|
||||||
# Use default version alias for nix package
|
# Use default version alias for nix package
|
||||||
|
|
|
@ -16,11 +16,15 @@
|
||||||
services.printing.listenAddresses = ["localhost:631"];
|
services.printing.listenAddresses = ["localhost:631"];
|
||||||
services.printing.defaultShared = lib.mkDefault false;
|
services.printing.defaultShared = lib.mkDefault false;
|
||||||
|
|
||||||
services.printing.drivers = [
|
services.printing.drivers =
|
||||||
pkgs.gutenprint
|
[
|
||||||
] ++ (if (pkgs.system == "x86_64-linux")
|
pkgs.gutenprint
|
||||||
then [ pkgs.cups-brother-hl3140cw ]
|
]
|
||||||
else []);
|
++ (
|
||||||
|
if (pkgs.system == "x86_64-linux")
|
||||||
|
then [pkgs.cups-brother-hl3140cw]
|
||||||
|
else []
|
||||||
|
);
|
||||||
|
|
||||||
networking.hosts = flake.self.lib.addLocalHostname ["cups.local"];
|
networking.hosts = flake.self.lib.addLocalHostname ["cups.local"];
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{ ... }:
|
{...}: {
|
||||||
{
|
|
||||||
enable = true;
|
enable = true;
|
||||||
nix-direnv = {
|
nix-direnv = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{ ... }:
|
{...}: {
|
||||||
{
|
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
extraConfig = {
|
extraConfig = {
|
||||||
|
|
|
@ -44,135 +44,136 @@ in {
|
||||||
universal-ctags
|
universal-ctags
|
||||||
];
|
];
|
||||||
|
|
||||||
plugins = with pkgs.vimPlugins; lib.mkIf cfg.full [
|
plugins = with pkgs.vimPlugins;
|
||||||
(pkgs.vimPlugins.nvim-treesitter.withPlugins (p: [
|
lib.mkIf cfg.full [
|
||||||
p.ini
|
(pkgs.vimPlugins.nvim-treesitter.withPlugins (p: [
|
||||||
p.json
|
p.ini
|
||||||
p.json5
|
p.json
|
||||||
p.markdown
|
p.json5
|
||||||
p.nix
|
p.markdown
|
||||||
p.toml
|
p.nix
|
||||||
p.yaml
|
p.toml
|
||||||
|
p.yaml
|
||||||
|
|
||||||
p.css
|
p.css
|
||||||
p.graphql
|
p.graphql
|
||||||
p.html
|
p.html
|
||||||
p.javascript
|
p.javascript
|
||||||
p.scss
|
p.scss
|
||||||
p.tsx
|
p.tsx
|
||||||
p.typescript
|
p.typescript
|
||||||
p.vue
|
p.vue
|
||||||
|
|
||||||
p.c
|
p.c
|
||||||
p.cpp
|
p.cpp
|
||||||
p.go
|
p.go
|
||||||
p.gomod
|
p.gomod
|
||||||
p.gosum
|
p.gosum
|
||||||
p.haskell
|
p.haskell
|
||||||
p.lua
|
p.lua
|
||||||
p.php
|
p.php
|
||||||
p.python
|
p.python
|
||||||
p.ruby
|
p.ruby
|
||||||
p.rust
|
p.rust
|
||||||
|
|
||||||
p.vim
|
p.vim
|
||||||
p.vimdoc
|
p.vimdoc
|
||||||
|
|
||||||
p.passwd
|
p.passwd
|
||||||
p.sql
|
p.sql
|
||||||
|
|
||||||
p.diff
|
p.diff
|
||||||
p.gitcommit
|
p.gitcommit
|
||||||
p.gitignore
|
p.gitignore
|
||||||
p.git_config
|
p.git_config
|
||||||
p.gitattributes
|
p.gitattributes
|
||||||
p.git_rebase
|
p.git_rebase
|
||||||
|
|
||||||
p.bash
|
p.bash
|
||||||
p.dockerfile
|
p.dockerfile
|
||||||
p.make
|
p.make
|
||||||
p.ninja
|
p.ninja
|
||||||
p.terraform
|
p.terraform
|
||||||
]))
|
]))
|
||||||
|
|
||||||
# Dependencies for nvim-lspconfig
|
# Dependencies for nvim-lspconfig
|
||||||
nvim-cmp
|
nvim-cmp
|
||||||
cmp-nvim-lsp
|
cmp-nvim-lsp
|
||||||
cmp_luasnip
|
cmp_luasnip
|
||||||
luasnip
|
luasnip
|
||||||
|
|
||||||
# Quickstart configs for neovim LSP
|
# Quickstart configs for neovim LSP
|
||||||
lsp_extensions-nvim
|
lsp_extensions-nvim
|
||||||
nvim-lspconfig
|
nvim-lspconfig
|
||||||
|
|
||||||
# Collaborative editing in Neovim using built-in capabilities
|
# Collaborative editing in Neovim using built-in capabilities
|
||||||
instant-nvim-nvfetcher
|
instant-nvim-nvfetcher
|
||||||
|
|
||||||
# Search functionality behind :Ack
|
# Search functionality behind :Ack
|
||||||
ack-vim
|
ack-vim
|
||||||
|
|
||||||
# The status bar in the bottom of the screen with the mode indication and file location
|
# The status bar in the bottom of the screen with the mode indication and file location
|
||||||
vim-airline
|
vim-airline
|
||||||
|
|
||||||
# Automatically load editorconfig files in repos to configure nvim settings
|
# Automatically load editorconfig files in repos to configure nvim settings
|
||||||
editorconfig-vim
|
editorconfig-vim
|
||||||
|
|
||||||
# File browser. Use <leader>n to access
|
# File browser. Use <leader>n to access
|
||||||
nnn-vim
|
nnn-vim
|
||||||
|
|
||||||
# Highlight characters when using f, F, t, and T
|
# Highlight characters when using f, F, t, and T
|
||||||
quick-scope
|
quick-scope
|
||||||
|
|
||||||
# Get sudo in vim; :SudaWrite <optional filename>
|
# Get sudo in vim; :SudaWrite <optional filename>
|
||||||
suda-vim
|
suda-vim
|
||||||
|
|
||||||
# Undo history etc. per project
|
# Undo history etc. per project
|
||||||
vim-workspace-nvfetcher
|
vim-workspace-nvfetcher
|
||||||
|
|
||||||
# JSON schemas
|
# JSON schemas
|
||||||
SchemaStore-nvim
|
SchemaStore-nvim
|
||||||
|
|
||||||
# Work with tags files
|
# Work with tags files
|
||||||
vim-gutentags
|
vim-gutentags
|
||||||
|
|
||||||
# Neovim colorschemes / themes
|
# Neovim colorschemes / themes
|
||||||
sonokai
|
sonokai
|
||||||
vim-hybrid-material
|
vim-hybrid-material
|
||||||
vim-airline-themes
|
vim-airline-themes
|
||||||
vim-apprentice-nvfetcher
|
vim-apprentice-nvfetcher
|
||||||
|
|
||||||
# Git integrations
|
# Git integrations
|
||||||
# A Git wrapper so awesome, it should be illegal
|
# A Git wrapper so awesome, it should be illegal
|
||||||
fugitive
|
fugitive
|
||||||
# Shows git diff markers in the sign column
|
# Shows git diff markers in the sign column
|
||||||
vim-gitgutter
|
vim-gitgutter
|
||||||
# GitHub extension for fugitive
|
# GitHub extension for fugitive
|
||||||
vim-rhubarb
|
vim-rhubarb
|
||||||
# Ease your git workflow within Vim
|
# Ease your git workflow within Vim
|
||||||
vimagit-nvfetcher
|
vimagit-nvfetcher
|
||||||
|
|
||||||
# FZF fuzzy finder
|
# FZF fuzzy finder
|
||||||
fzf-vim
|
fzf-vim
|
||||||
fzfWrapper
|
fzfWrapper
|
||||||
# Make the yanked region apparent
|
# Make the yanked region apparent
|
||||||
vim-highlightedyank
|
vim-highlightedyank
|
||||||
|
|
||||||
# :Beautify Code beautifier
|
# :Beautify Code beautifier
|
||||||
vim-beautify-nvfetcher
|
vim-beautify-nvfetcher
|
||||||
|
|
||||||
# Unload, delete or wipe a buffer without closing the window
|
# Unload, delete or wipe a buffer without closing the window
|
||||||
vim-bufkill
|
vim-bufkill
|
||||||
# Defaults everyone can agree on
|
# Defaults everyone can agree on
|
||||||
vim-sensible
|
vim-sensible
|
||||||
|
|
||||||
# emmet for vim: http://emmet.io/
|
# emmet for vim: http://emmet.io/
|
||||||
emmet-vim
|
emmet-vim
|
||||||
# Caddyfile syntax support for Vim
|
# Caddyfile syntax support for Vim
|
||||||
vim-caddyfile-nvfetcher
|
vim-caddyfile-nvfetcher
|
||||||
|
|
||||||
# Fix TOFU hashes when writing nix derivations without leaving neovim
|
# Fix TOFU hashes when writing nix derivations without leaving neovim
|
||||||
vim-nixhash
|
vim-nixhash
|
||||||
];
|
];
|
||||||
|
|
||||||
extraConfig = builtins.concatStringsSep "\n" [
|
extraConfig = builtins.concatStringsSep "\n" [
|
||||||
''
|
''
|
||||||
|
|
|
@ -6,96 +6,96 @@
|
||||||
}: let
|
}: let
|
||||||
psCfg = config.pub-solar;
|
psCfg = config.pub-solar;
|
||||||
in
|
in
|
||||||
with lib; {
|
with lib; {
|
||||||
imports = [
|
imports = [
|
||||||
./home.nix
|
./home.nix
|
||||||
];
|
|
||||||
|
|
||||||
options.pub-solar = {
|
|
||||||
user = {
|
|
||||||
name = mkOption {
|
|
||||||
description = "User login name";
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
default = "nixos";
|
|
||||||
};
|
|
||||||
description = mkOption {
|
|
||||||
description = "User description";
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
default = "The main PubSolarOS user";
|
|
||||||
};
|
|
||||||
password = mkOption {
|
|
||||||
description = "User password";
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
default = null;
|
|
||||||
};
|
|
||||||
passwordlessSudo = mkOption {
|
|
||||||
description = "Whether this user can use sudo without entering a password";
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
};
|
|
||||||
publicKeys = mkOption {
|
|
||||||
description = "User SSH public keys";
|
|
||||||
type = types.listOf types.str;
|
|
||||||
default = [];
|
|
||||||
};
|
|
||||||
fullName = mkOption {
|
|
||||||
description = "User full name";
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
default = null;
|
|
||||||
};
|
|
||||||
email = mkOption {
|
|
||||||
description = "User email address";
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
default = null;
|
|
||||||
};
|
|
||||||
gpgKeyId = mkOption {
|
|
||||||
description = "GPG Key ID";
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
default = null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
users = {
|
|
||||||
mutableUsers = false;
|
|
||||||
|
|
||||||
users."${psCfg.user.name}" = {
|
|
||||||
# Indicates whether this is an account for a “real” user.
|
|
||||||
# This automatically sets group to users, createHome to true,
|
|
||||||
# home to /home/username, useDefaultShell to true, and isSystemUser to false.
|
|
||||||
isNormalUser = true;
|
|
||||||
description = psCfg.user.description;
|
|
||||||
extraGroups = [
|
|
||||||
"input"
|
|
||||||
"lp"
|
|
||||||
"networkmanager"
|
|
||||||
"scanner"
|
|
||||||
"video"
|
|
||||||
"wheel"
|
|
||||||
];
|
|
||||||
shell = pkgs.bash;
|
|
||||||
initialHashedPassword =
|
|
||||||
if psCfg.user.password != null
|
|
||||||
then psCfg.user.password
|
|
||||||
else "";
|
|
||||||
openssh.authorizedKeys.keys =
|
|
||||||
if psCfg.user.publicKeys != null
|
|
||||||
then psCfg.user.publicKeys
|
|
||||||
else [];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
security.sudo.extraRules = mkIf psCfg.user.passwordlessSudo [
|
|
||||||
{
|
|
||||||
users = ["${psCfg.user.name}"];
|
|
||||||
commands = [
|
|
||||||
{
|
|
||||||
command = "ALL";
|
|
||||||
options = ["NOPASSWD"];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
};
|
|
||||||
}
|
options.pub-solar = {
|
||||||
|
user = {
|
||||||
|
name = mkOption {
|
||||||
|
description = "User login name";
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = "nixos";
|
||||||
|
};
|
||||||
|
description = mkOption {
|
||||||
|
description = "User description";
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = "The main PubSolarOS user";
|
||||||
|
};
|
||||||
|
password = mkOption {
|
||||||
|
description = "User password";
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
passwordlessSudo = mkOption {
|
||||||
|
description = "Whether this user can use sudo without entering a password";
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
publicKeys = mkOption {
|
||||||
|
description = "User SSH public keys";
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
};
|
||||||
|
fullName = mkOption {
|
||||||
|
description = "User full name";
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
email = mkOption {
|
||||||
|
description = "User email address";
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
gpgKeyId = mkOption {
|
||||||
|
description = "GPG Key ID";
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
users = {
|
||||||
|
mutableUsers = false;
|
||||||
|
|
||||||
|
users."${psCfg.user.name}" = {
|
||||||
|
# Indicates whether this is an account for a “real” user.
|
||||||
|
# This automatically sets group to users, createHome to true,
|
||||||
|
# home to /home/username, useDefaultShell to true, and isSystemUser to false.
|
||||||
|
isNormalUser = true;
|
||||||
|
description = psCfg.user.description;
|
||||||
|
extraGroups = [
|
||||||
|
"input"
|
||||||
|
"lp"
|
||||||
|
"networkmanager"
|
||||||
|
"scanner"
|
||||||
|
"video"
|
||||||
|
"wheel"
|
||||||
|
];
|
||||||
|
shell = pkgs.bash;
|
||||||
|
initialHashedPassword =
|
||||||
|
if psCfg.user.password != null
|
||||||
|
then psCfg.user.password
|
||||||
|
else "";
|
||||||
|
openssh.authorizedKeys.keys =
|
||||||
|
if psCfg.user.publicKeys != null
|
||||||
|
then psCfg.user.publicKeys
|
||||||
|
else [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
security.sudo.extraRules = mkIf psCfg.user.passwordlessSudo [
|
||||||
|
{
|
||||||
|
users = ["${psCfg.user.name}"];
|
||||||
|
commands = [
|
||||||
|
{
|
||||||
|
command = "ALL";
|
||||||
|
options = ["NOPASSWD"];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -6,18 +6,16 @@
|
||||||
}: {
|
}: {
|
||||||
flake = {
|
flake = {
|
||||||
nixosModules = rec {
|
nixosModules = rec {
|
||||||
overlays = ({ ... }: {
|
overlays = {...}: {
|
||||||
nixpkgs.overlays = [
|
nixpkgs.overlays = [
|
||||||
(final: prev:
|
(final: prev: let
|
||||||
let
|
|
||||||
unstable = import inputs.unstable {
|
unstable = import inputs.unstable {
|
||||||
system = prev.system;
|
system = prev.system;
|
||||||
};
|
};
|
||||||
master = import inputs.master {
|
master = import inputs.master {
|
||||||
system = prev.system;
|
system = prev.system;
|
||||||
};
|
};
|
||||||
in
|
in {
|
||||||
{
|
|
||||||
direnv = unstable.direnv;
|
direnv = unstable.direnv;
|
||||||
nix-direnv = unstable.nix-direnv;
|
nix-direnv = unstable.nix-direnv;
|
||||||
#vimPlugins = prev.vimPlugins // {inherit (unstable.vimPlugins) nvim-lspconfig;};
|
#vimPlugins = prev.vimPlugins // {inherit (unstable.vimPlugins) nvim-lspconfig;};
|
||||||
|
@ -31,7 +29,7 @@
|
||||||
(import ./nix-index.nix)
|
(import ./nix-index.nix)
|
||||||
(import ./neovim-plugins.nix)
|
(import ./neovim-plugins.nix)
|
||||||
];
|
];
|
||||||
});
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{ self, ... }:
|
{self, ...}: {
|
||||||
{
|
|
||||||
flake = {
|
flake = {
|
||||||
nixosModules = rec {
|
nixosModules = rec {
|
||||||
root = import ./root;
|
root = import ./root;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
{...}:
|
{...}: {
|
||||||
{
|
|
||||||
users.users.root.hashedPassword = "";
|
users.users.root.hashedPassword = "";
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue