Migrate corp configuration to this repository and use flakes

This commit is contained in:
Eli Ribble 2025-07-16 09:32:19 -07:00
parent af5c966e5b
commit ecbb1b932c
17 changed files with 1099 additions and 0 deletions

View file

@ -0,0 +1,10 @@
{ config, lib, pkgs, configFiles, ... }:
with lib;
{
services.cloud-init = {
enable = true;
network.enable = true;
};
}

View file

@ -0,0 +1,13 @@
{
imports = [
./cloud-init.nix
./do-agent.nix
./fish.nix
./onlyoffice.nix
./seafile.nix
./sudo.nix
./synapse.nix
./timecardbot.nix
./tmux.nix
];
}

View file

@ -0,0 +1,7 @@
{ config, lib, pkgs, configFiles, ... }:
with lib;
{
services.do-agent.enable = true;
}

7
modules/system/fish.nix Normal file
View file

@ -0,0 +1,7 @@
{ config, configPath, lib, pkgs, ... }:
with lib;
{
environment.systemPackages = [ pkgs.fish ];
}

View file

@ -0,0 +1,347 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.onlyoffice;
in
{
# Override built-in onlyoffice definition to enable WOPI
options.services.onlyoffice = {
enable = lib.mkEnableOption "OnlyOffice DocumentServer";
enableExampleServer = lib.mkEnableOption "OnlyOffice example server";
hostname = lib.mkOption {
type = lib.types.str;
default = "localhost";
description = "FQDN for the OnlyOffice instance.";
};
jwtSecretFile = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = ''
Path to a file that contains the secret to sign web requests using JSON Web Tokens.
If left at the default value null signing is disabled.
'';
};
package = lib.mkPackageOption pkgs "onlyoffice-documentserver" { };
x2t = lib.mkPackageOption pkgs "x2t" { };
port = lib.mkOption {
type = lib.types.port;
default = 8000;
description = "Port the OnlyOffice document server should listen on.";
};
examplePort = lib.mkOption {
type = lib.types.port;
default = null;
description = "Port the OnlyOffice example server should listen on.";
};
postgresHost = lib.mkOption {
type = lib.types.str;
default = "/run/postgresql";
description = "The Postgresql hostname or socket path OnlyOffice should connect to.";
};
postgresName = lib.mkOption {
type = lib.types.str;
default = "onlyoffice";
description = "The name of database OnlyOffice should use.";
};
postgresPasswordFile = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = ''
Path to a file that contains the password OnlyOffice should use to connect to Postgresql.
Unused when using socket authentication.
'';
};
postgresUser = lib.mkOption {
type = lib.types.str;
default = "onlyoffice";
description = ''
The username OnlyOffice should use to connect to Postgresql.
Unused when using socket authentication.
'';
};
rabbitmqUrl = lib.mkOption {
type = lib.types.str;
default = "amqp://guest:guest@localhost:5672";
description = "The Rabbitmq in amqp URI style OnlyOffice should connect to.";
};
wopi = lib.mkEnableOption "Enable WOPI support";
};
options.myModules.onlyoffice.enable = mkEnableOption "custom onlyoffice configuration";
config = mkIf config.myModules.onlyoffice.enable {
services = {
nginx = {
enable = lib.mkDefault true;
# misses text/csv, font/ttf, application/x-font-ttf, application/rtf, application/wasm
recommendedGzipSettings = lib.mkDefault true;
recommendedProxySettings = lib.mkDefault true;
upstreams = {
# /etc/nginx/includes/http-common.conf
onlyoffice-docservice = {
servers = {
"localhost:${toString cfg.port}" = { };
};
};
onlyoffice-example = lib.mkIf cfg.enableExampleServer {
servers = {
"localhost:${toString cfg.examplePort}" = { };
};
};
};
virtualHosts.${cfg.hostname} = {
locations = {
# resources that are generated and thus cannot be taken from the cfg.package yet:
"~ ^(\\/[\\d]+\\.[\\d]+\\.[\\d]+[\\.|-][\\w]+)?\\/(sdkjs/common/AllFonts.js)$".extraConfig = ''
proxy_pass http://onlyoffice-docservice/$2$3;
'';
"~ ^(\\/[\\d]+\\.[\\d]+\\.[\\d]+[\\.|-][\\w]+)?\\/(fonts/.*)$".extraConfig = ''
proxy_pass http://onlyoffice-docservice/$2$3;
'';
# /etc/nginx/includes/ds-docservice.conf
# disable caching for api.js
"~ ^(\\/[\\d]+\\.[\\d]+\\.[\\d]+[\\.|-][\\w]+)?\\/(web-apps\\/apps\\/api\\/documents\\/api\\.js)$".extraConfig =
''
expires -1;
# gzip_static on;
alias ${cfg.package}/var/www/onlyoffice/documentserver/$2;
'';
"~ ^(\\/[\\d]+\\.[\\d]+\\.[\\d]+[\\.|-][\\w]+)?\\/(document_editor_service_worker\\.js)$".extraConfig =
''
expires 365d;
alias ${cfg.package}/var/www/onlyoffice/documentserver/sdkjs/common/serviceworker/$2;
'';
# suppress logging the unsupported locale error in web-apps
"~ ^(\\/[\\d]+\\.[\\d]+\\.[\\d]+[\\.|-][\\w]+)?\\/(web-apps)(\\/.*\\.json)$".extraConfig = ''
expires 365d;
error_log /dev/null crit;
alias ${cfg.package}/var/www/onlyoffice/documentserver/$2$3;
'';
# suppress logging the unsupported locale error in plugins
"~ ^(\\/[\\d]+\\.[\\d]+\\.[\\d]+[\\.|-][\\w]+)?\\/(sdkjs-plugins)(\\/.*\\.json)$".extraConfig = ''
expires 365d;
error_log /dev/null crit;
alias ${cfg.package}/var/www/onlyoffice/documentserver/$2$3;
'';
"~ ^(\\/[\\d]+\\.[\\d]+\\.[\\d]+[\\.|-][\\w]+)?\\/(web-apps|sdkjs|sdkjs-plugins|fonts|dictionaries)(\\/.*)$".extraConfig =
''
expires 365d;
alias ${cfg.package}/var/www/onlyoffice/documentserver/$2$3;
'';
"~* ^(\\/cache\\/files.*)(\\/.*)".extraConfig = ''
alias /var/lib/onlyoffice/documentserver/App_Data$1;
add_header Content-Disposition "attachment; filename*=UTF-8''$arg_filename";
set $secure_link_secret verysecretstring;
secure_link $arg_md5,$arg_expires;
secure_link_md5 "$secure_link_expires$uri$secure_link_secret";
if ($secure_link = "") {
return 403;
}
if ($secure_link = "0") {
return 410;
}
'';
# Allow "/internal" interface only from 127.0.0.1
# Don't comment out the section below for the security reason!
"~* ^(\\/[\\d]+\\.[\\d]+\\.[\\d]+[\\.|-][\\w]+)?\\/(internal)(\\/.*)$".extraConfig = ''
allow 127.0.0.1;
deny all;
proxy_pass http://onlyoffice-docservice/$2$3;
'';
# Allow "/info" interface only from 127.0.0.1 by default
# Comment out lines allow 127.0.0.1; and deny all;
# of below section to turn on the info page
"~* ^(\\/[\\d]+\\.[\\d]+\\.[\\d]+[\\.|-][\\w]+)?\\/(info)(\\/.*)$".extraConfig = ''
allow 127.0.0.1;
deny all;
proxy_pass http://onlyoffice-docservice/$2$3;
'';
"/".extraConfig = ''
proxy_pass http://onlyoffice-docservice;
'';
"~ ^(\\/[\\d]+\\.[\\d]+\\.[\\d]+[\\.|-][\\w]+)?(\\/(doc|downloadas)\\/.*)".extraConfig = ''
proxy_pass http://onlyoffice-docservice$2$is_args$args;
proxy_http_version 1.1;
'';
# end of /etc/nginx/includes/ds-docservice.conf
"/${cfg.package.version}/".extraConfig = ''
proxy_pass http://onlyoffice-docservice/;
'';
# /etc/nginx/includes/ds-example.conf
"~ ^(\\/welcome\\/.*)$".extraConfig = lib.mkIf cfg.enableExampleServer ''
expires 365d;
alias ${cfg.package}/var/www/onlyoffice/documentserver-example$1;
index docker.html;
'';
"/example/".extraConfig = lib.mkIf cfg.enableExampleServer ''
proxy_pass http://onlyoffice-example/;
proxy_set_header X-Forwarded-Path /example;
'';
};
extraConfig = ''
rewrite ^/$ /welcome/ redirect;
rewrite ^\/OfficeWeb(\/apps\/.*)$ /${cfg.package.version}/web-apps$1 redirect;
rewrite ^(\/web-apps\/apps\/(?!api\/).*)$ /${cfg.package.version}$1 redirect;
# based on https://github.com/ONLYOFFICE/document-server-package/blob/master/common/documentserver/nginx/includes/http-common.conf.m4#L29-L34
# without variable indirection and correct variable names
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
# required for CSP to take effect
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# required for websocket
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
'';
};
};
rabbitmq.enable = lib.mkDefault true;
postgresql = {
enable = lib.mkDefault true;
ensureDatabases = [ "onlyoffice" ];
ensureUsers = [
{
name = "onlyoffice";
ensureDBOwnership = true;
}
];
};
};
systemd.services = {
onlyoffice-converter = {
description = "onlyoffice converter";
after = [
"network.target"
"onlyoffice-docservice.service"
"postgresql.service"
];
requires = [
"network.target"
"onlyoffice-docservice.service"
"postgresql.service"
];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${cfg.package.fhs}/bin/onlyoffice-wrapper FileConverter/converter /run/onlyoffice/config";
Group = "onlyoffice";
Restart = "always";
RuntimeDirectory = "onlyoffice";
StateDirectory = "onlyoffice";
Type = "simple";
User = "onlyoffice";
};
};
onlyoffice-docservice =
let
onlyoffice-prestart = pkgs.writeShellScript "onlyoffice-prestart" ''
PATH=$PATH:${
lib.makeBinPath (
with pkgs;
[
jq
moreutils
config.services.postgresql.package
]
)
}
umask 077
mkdir -p /run/onlyoffice/config/ /var/lib/onlyoffice/documentserver/sdkjs/{slide/themes,common}/ /var/lib/onlyoffice/documentserver/{fonts,server/FileConverter/bin}/
cp -r ${cfg.package}/etc/onlyoffice/documentserver/* /run/onlyoffice/config/
chmod u+w /run/onlyoffice/config/default.json
# Allow members of the onlyoffice group to serve files under /var/lib/onlyoffice/documentserver/App_Data
chmod g+x /var/lib/onlyoffice/documentserver
cp /run/onlyoffice/config/default.json{,.orig}
# for a mapping of environment variables from the docker container to json options see
# https://github.com/ONLYOFFICE/Docker-DocumentServer/blob/master/run-document-server.sh
jq '
.services.CoAuthoring.server.port = ${toString cfg.port} |
.services.CoAuthoring.sql.dbHost = "${cfg.postgresHost}" |
.services.CoAuthoring.sql.dbName = "${cfg.postgresName}" |
${lib.optionalString (cfg.postgresPasswordFile != null) ''
.services.CoAuthoring.sql.dbPass = "'"$(cat ${cfg.postgresPasswordFile})"'" |
''}
.services.CoAuthoring.sql.dbUser = "${cfg.postgresUser}" |
${lib.optionalString (cfg.jwtSecretFile != null) ''
.services.CoAuthoring.token.enable.browser = true |
.services.CoAuthoring.token.enable.request.inbox = true |
.services.CoAuthoring.token.enable.request.outbox = true |
.services.CoAuthoring.secret.inbox.string = "'"$(cat ${cfg.jwtSecretFile})"'" |
.services.CoAuthoring.secret.outbox.string = "'"$(cat ${cfg.jwtSecretFile})"'" |
.services.CoAuthoring.secret.session.string = "'"$(cat ${cfg.jwtSecretFile})"'" |
''}
.rabbitmq.url = "${cfg.rabbitmqUrl}" |
.wopi.enable = "${toString cfg.wopi}"
' /run/onlyoffice/config/default.json | sponge /run/onlyoffice/config/default.json
chmod u+w /run/onlyoffice/config/production-linux.json
jq '.FileConverter.converter.x2tPath = "${cfg.x2t}/bin/x2t"' \
/run/onlyoffice/config/production-linux.json | sponge /run/onlyoffice/config/production-linux.json
if psql -d onlyoffice -c "SELECT 'task_result'::regclass;" >/dev/null; then
psql -f ${cfg.package}/var/www/onlyoffice/documentserver/server/schema/postgresql/removetbl.sql
psql -f ${cfg.package}/var/www/onlyoffice/documentserver/server/schema/postgresql/createdb.sql
else
psql -f ${cfg.package}/var/www/onlyoffice/documentserver/server/schema/postgresql/createdb.sql
fi
'';
in
{
description = "onlyoffice documentserver";
after = [
"network.target"
"postgresql.service"
];
requires = [ "postgresql.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${cfg.package.fhs}/bin/onlyoffice-wrapper DocService/docservice /run/onlyoffice/config";
ExecStartPre = [ onlyoffice-prestart ];
Group = "onlyoffice";
Restart = "always";
RuntimeDirectory = "onlyoffice";
StateDirectory = "onlyoffice";
Type = "simple";
User = "onlyoffice";
};
};
};
users.users = {
onlyoffice = {
description = "OnlyOffice Service";
group = "onlyoffice";
isSystemUser = true;
};
nginx.extraGroups = [ "onlyoffice" ];
};
users.groups.onlyoffice = { };
};
}

145
modules/system/seafile.nix Normal file
View file

@ -0,0 +1,145 @@
{ config, lib, pkgs, ... }:
with lib;
let
domain = "files.gleipnir.technology";
domain2 = "filez.gleipnir.technology";
stripTabs = text: let
# Whether all lines start with a tab (or is empty)
shouldStripTab = lines: builtins.all (line: (line == "") || (pkgs.lib.strings.hasPrefix " " line)) lines;
# Strip a leading tab from all lines
stripTab = lines: builtins.map (line: pkgs.lib.strings.removePrefix " " line) lines;
# Strip tabs recursively until there are none
stripTabs = lines: if (shouldStripTab lines) then (stripTabs (stripTab lines)) else lines;
in
# Split into lines. Strip leading tabs. Concat back to string.
builtins.concatStringsSep "\n" (stripTabs (pkgs.lib.strings.splitString "\n" text));
in {
options.myModules.seafile.enable = mkEnableOption "custom seafile configuration";
config = mkIf config.myModules.seafile.enable {
services.nginx = {
virtualHosts."${domain}" = {
enableACME = true;
forceSSL = true;
locations = {
"/" = {
proxyPass = "http://unix:/run/seahub/gunicorn.sock";
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto https;
proxy_read_timeout 1200s;
client_max_body_size 0;
'';
};
"/seafhttp" = {
proxyPass = "http://unix:/run/seafile/server.sock";
extraConfig = ''
rewrite ^/seafhttp(.*)$ $1 break;
client_max_body_size 0;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_connect_timeout 36000s;
proxy_read_timeout 36000s;
proxy_send_timeout 36000s;
send_timeout 36000s;
'';
};
};
};
virtualHosts."${domain2}" = {
enableACME = true;
forceSSL = true;
locations = {
"/" = {
proxyPass = "http://[::1]:10030";
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto https;
proxy_read_timeout 1200s;
client_max_body_size 0;
'';
};
};
};
};
services.seafile = {
adminEmail = "eli@gleipnir.technology";
ccnetSettings = {
General.SERVICE_URL = "https://${domain}";
};
enable = true;
gc = {
enable = true;
dates = [ "Sun 03:00:00" ];
};
initialAdminPassword = "change this later!";
seafileSettings = {
fileserver = {
host = "unix:/run/seafile/server.sock";
use_go_fileserver = "false";
};
# Enable weekly collection of freed blocks
history.keep_days = "14"; # Remove deleted files after 14 days
quota.default = "50"; # Amount of GB allotted to users
};
seahubExtraConf = stripTabs(''
ENABLE_OAUTH = True
ENABLE_ONLYOFFICE = True
# If create new user when he/she logs in Seafile for the first time, defalut `True`.
OAUTH_CREATE_UNKNOWN_USER = True
# If active new user when he/she logs in Seafile for the first time, defalut `True`.
OAUTH_ACTIVATE_USER_AFTER_CREATION = True
# Usually OAuth works through SSL layer. If your server is not parametrized to allow HTTPS, some method will raise an "oauthlib.oauth2.rfc6749.errors.InsecureTransportError". Set this to `True` to avoid this error.
#OAUTH_ENABLE_INSECURE_TRANSPORT = True
# Client id/secret generated by authorization server when you register your client application.
OAUTH_CLIENT_ID = "secret"
OAUTH_CLIENT_SECRET = "secret"
# Callback url when user authentication succeeded. Note, the redirect url you input when you register your client application MUST be exactly the same as this value.
OAUTH_REDIRECT_URL = 'https://files.gleipnir.technology/oauth/callback/'
# The following should NOT be changed if you are using Github as OAuth provider.
OAUTH_PROVIDER_DOMAIN = 'gleipnir.technology'
OAUTH_PROVIDER = 'Authentik'
OAUTH_AUTHORIZATION_URL = 'https://auth.gleipnir.technology/application/o/authorize/'
OAUTH_TOKEN_URL = 'https://auth.gleipnir.technology/application/o/token/'
OAUTH_USER_INFO_URL = 'https://auth.gleipnir.technology/application/o/userinfo/'
OAUTH_SCOPE = ["openid", "profile", "email"]
OAUTH_ATTRIBUTE_MAP = {
"id": (False, "not used"),
"name": (True, "name"),
"email": (True, "email"),
}
SEAHUB_DATA_ROOT = "/var/lib/seafile/seahub/data"
OFFICE_SERVER_TYPE = 'CollaboraOffice'
ENABLE_OFFICE_WEB_APP = True
OFFICE_WEB_APP_BASE_URL = 'https://docs.gleipnir.technology/hosting/discovery'
# Expiration of WOPI access token
# WOPI access token is a string used by Seafile to determine the file's
# identity and permissions when use LibreOffice Online view it online
# And for security reason, this token should expire after a set time period
WOPI_ACCESS_TOKEN_EXPIRATION = 30 * 60 # seconds
VERIFY_ONLYOFFICE_CERTIFICATE = False
ONLYOFFICE_APIJS_URL = 'https://docs.gleipnir.technology/web-apps/apps/api/documents/api.js'
ONLYOFFICE_FILE_EXTENSION = ('doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx', 'odt', 'fodt', 'odp', 'fodp', 'ods', 'fods')
ONLYOFFICE_EDIT_FILE_EXTENSION = ('docx', 'pptx', 'xlsx')
ONLYOFFICE_FORCE_SAVE = True
'');
};
};
}

7
modules/system/sudo.nix Normal file
View file

@ -0,0 +1,7 @@
{ config, lib, pkgs, configFiles, ... }:
with lib;
{
security.sudo.wheelNeedsPassword = false;
}

115
modules/system/synapse.nix Normal file
View file

@ -0,0 +1,115 @@
{ config, lib, pkgs, ... }:
with lib;
let
fqdn = "matrix.gleipnir.technology";
baseUrl = "https://${fqdn}";
clientConfig."m.homeserver".base_url = baseUrl;
serverConfig."m.server" = "${fqdn}:443";
mkWellKnown = data: ''
default_type application/json;
add_header Access-Control-Allow-Origin *;
return 200 '${builtins.toJSON data}';
'';
in {
options.myModules.synapse.enable = mkEnableOption "custom synapse configuration";
config = mkIf config.myModules.synapse.enable {
services.nginx = {
virtualHosts."chat.gleipnir.technology" = {
enableACME = true;
forceSSL = true;
# Host element web client at the root
root = pkgs.element-web.override {
conf = {
default_server_config = clientConfig;
};
};
};
virtualHosts."corp.gleipnir.technology" = {
enableACME = true;
forceSSL = true;
# This section is not needed if the server_name of matrix-synapse is equal to
# the domain (i.e. example.org from @foo:example.org) and the federation port
# is 8448.
# Further reference can be found in the docs about delegation under
# https://element-hq.github.io/synapse/latest/delegate.html
locations."= /.well-known/matrix/server".extraConfig = mkWellKnown serverConfig;
# This is usually needed for homeserver discovery (from e.g. other Matrix clients).
# Further reference can be found in the upstream docs at
# https://spec.matrix.org/latest/client-server-api/#getwell-knownmatrixclient
locations."= /.well-known/matrix/client".extraConfig = mkWellKnown clientConfig;
};
virtualHosts."matrix.gleipnir.technology" = {
enableACME = true;
forceSSL = true;
# It's also possible to do a redirect here or something else, this vhost is not
# needed for Matrix. It's recommended though to *not put* element
# here, see also the section about Element.
locations."/".extraConfig = ''
return 404;
'';
# Forward all Matrix API calls to the synapse Matrix homeserver. A trailing slash
# *must not* be used here.
locations."/_matrix" = {
extraConfig = ''
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
include /etc/nginx/proxy.conf;
'';
proxyPass = "http://[::1]:8008";
};
# Forward requests for e.g. SSO and password-resets.
locations."/_synapse/client" = {
extraConfig = ''
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
include /etc/nginx/proxy.conf;
'';
proxyPass = "http://[::1]:8008";
};
};
virtualHosts."matrix-bot.gleipnir.technology" = {
enableACME = true;
forceSSL = true;
locations."/" = {
extraConfig = ''
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
include /etc/nginx/proxy.conf;
'';
proxyPass = "http://[::1]:10050";
};
};
};
services.matrix-synapse = {
enable = true;
extras = ["oidc"];
extraConfigFiles = [
"/run/secrets/matrix"
];
log.root.level = "WARNING";
settings = {
listeners = [
{ port = 8008;
bind_addresses = [ "::1" ];
type = "http";
tls = false;
x_forwarded = true;
resources = [ {
names = [ "client" "federation" ];
compress = true;
} ];
}
];
# The public base URL value must match the `base_url` value set in `clientConfig` above.
# The default value here is based on `server_name`, so if your `server_name` is different
# from the value of `fqdn` above, you will likely run into some mismatched domain names
# in client applications.
public_baseurl = baseUrl;
server_name = config.networking.domain;
};
};
};
}

View file

@ -0,0 +1,21 @@
{ pkgs, lib, config, ... }:
with lib;
let
timecardBotSrc = pkgs.fetchFromGitHub {
owner = "Gleipnir-Technology";
repo = "timecard-bot";
rev = "00b2850655295513c1e99a519d1d59c3b9847122";
sha256 = "1f78jm3jgzwzc69q1h9nplmcbz5hb9l74phyhzkbfjb99n3vrf1q";
};
timecardBotFlake = (import timecardBotSrc);
timecardBotPackage = timecardBotFlake.packages.${pkgs.system}.default;
in
{
options.myModules.timecardbot.enable = mkEnableOption "custom timecardbot configuration";
config = mkIf config.myModules.timecardbot.enable {
#environment.systemPackages = with pkgs; [
#timecardBotPackage
#];
};
}

18
modules/system/tmux.nix Normal file
View file

@ -0,0 +1,18 @@
{ config, lib, pkgs, configFiles, ... }:
with lib;
{
options.myModules.tmux.enable = mkEnableOption "custom tmux configuration";
config = mkIf config.myModules.tmux.enable {
environment.systemPackages = [ pkgs.tmux ];
environment.etc."tmux.conf".source = "${configFiles}/tmux/tmux.conf";
# Alternative: if you want per-user configs
# users.users = mkMerge (map (user: {
# ${user}.home = "${configPath}/configs/tmux/tmux.conf";
# }) config.myModules.tmux.users);
};
}