5116faf0d3
- caddy: security headers (X-Content-Type-Options/X-XSS-Protection/ X-Frame-Options) on all vhosts + baseline CSP; strip SnappyMail upstream copies via header_down on mail.severijnse.eu - tlsa-updater: compute TLSA 3 1 1 from cert SPKI (SHA-256), sync _25/_465/_993, fail-safe placeholders; coredns zone updated - pre-commit: wire cachix/git-hooks.nix (alejandra, statix, actionlint, deadnix); CI pre-commit job over x86_64 + aarch64 matrix - gitea: enable Gitea Actions + self-hosted runner (native:host, aarch64 via binfmt); add .gitea/workflows/ci.yml and local hook - fix statix warnings (merge repeated systemd/database/configFile keys) + disable empty_pattern via statix.toml (nixpkgs standard) - Clean up unused lambda patterns across 38 .nix files via deadnix - Format whole repo with alejandra (27 files) - Remove .github/workflows/ci.yml (Gitea shadows .github; runner labels differ) - Fix CI nix-not-found: export /run/current-system/sw/bin in PATH - Trim aarch64 from pre-commit matrix (no QEMU binfmt deployed yet)
175 lines
4.6 KiB
Nix
175 lines
4.6 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}: let
|
|
cfg = config.xdg;
|
|
username = "someone";
|
|
fileType = lib.types.submodule {
|
|
options = {
|
|
text = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.lines;
|
|
default = null;
|
|
};
|
|
source = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.path;
|
|
default = null;
|
|
};
|
|
mutable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = "If true, copy the file instead of symlinking, allowing the application to modify it";
|
|
};
|
|
};
|
|
};
|
|
userOpts = _: {
|
|
options = {
|
|
configFiles = lib.mkOption {
|
|
type = lib.types.attrsOf fileType;
|
|
default = {};
|
|
};
|
|
cacheFiles = lib.mkOption {
|
|
type = lib.types.attrsOf fileType;
|
|
default = {};
|
|
};
|
|
dataFiles = lib.mkOption {
|
|
type = lib.types.attrsOf fileType;
|
|
default = {};
|
|
};
|
|
stateFiles = lib.mkOption {
|
|
type = lib.types.attrsOf fileType;
|
|
default = {};
|
|
};
|
|
homeFiles = lib.mkOption {
|
|
type = lib.types.attrsOf fileType;
|
|
default = {};
|
|
};
|
|
};
|
|
};
|
|
|
|
mkLinkScript = baseDir: name: file: let
|
|
target =
|
|
if file.text != null
|
|
then pkgs.writeText name file.text
|
|
else file.source;
|
|
fullPath = "${baseDir}/${name}";
|
|
parentDir = builtins.dirOf fullPath;
|
|
in
|
|
if file.mutable
|
|
then ''
|
|
mkdir -p "${parentDir}"
|
|
if [ -L "${fullPath}" ]; then
|
|
# Replace symlink with a copy
|
|
rm -f "${fullPath}"
|
|
cp "${target}" "${fullPath}"
|
|
chmod +w "${fullPath}"
|
|
elif [ -e "${fullPath}" ]; then
|
|
:
|
|
else
|
|
cp "${target}" "${fullPath}"
|
|
chmod +w "${fullPath}"
|
|
fi
|
|
''
|
|
else ''
|
|
mkdir -p "${parentDir}"
|
|
if [ -L "${fullPath}" ]; then
|
|
current_target=$(readlink "${fullPath}")
|
|
if [ "$current_target" != "${target}" ]; then
|
|
rm -f "${fullPath}"
|
|
ln -s "${target}" "${fullPath}"
|
|
fi
|
|
elif [ -e "${fullPath}" ]; then
|
|
rm -f "${fullPath}"
|
|
ln -s "${target}" "${fullPath}"
|
|
else
|
|
ln -s "${target}" "${fullPath}"
|
|
fi
|
|
'';
|
|
in {
|
|
options = {
|
|
users.users = lib.mkOption {type = lib.types.attrsOf (lib.types.submodule userOpts);};
|
|
home = {
|
|
homeDirectory = lib.mkOption {
|
|
type = lib.types.path;
|
|
default = "/home/${username}";
|
|
};
|
|
file = lib.mkOption {
|
|
type = lib.types.attrsOf fileType;
|
|
default = {};
|
|
};
|
|
};
|
|
xdg = {
|
|
configHome = lib.mkOption {
|
|
type = lib.types.path;
|
|
default = "/home/${username}/.config";
|
|
};
|
|
cacheHome = lib.mkOption {
|
|
type = lib.types.path;
|
|
default = "/home/${username}/.cache";
|
|
};
|
|
dataHome = lib.mkOption {
|
|
type = lib.types.path;
|
|
default = "/home/${username}/.local/share";
|
|
};
|
|
stateHome = lib.mkOption {
|
|
type = lib.types.path;
|
|
default = "/home/${username}/.local/state";
|
|
};
|
|
runtimeDir = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "/run/user/1000";
|
|
};
|
|
configFile = lib.mkOption {
|
|
type = lib.types.attrsOf fileType;
|
|
default = {};
|
|
};
|
|
cacheFile = lib.mkOption {
|
|
type = lib.types.attrsOf fileType;
|
|
default = {};
|
|
};
|
|
dataFile = lib.mkOption {
|
|
type = lib.types.attrsOf fileType;
|
|
default = {};
|
|
};
|
|
stateFile = lib.mkOption {
|
|
type = lib.types.attrsOf fileType;
|
|
default = {};
|
|
};
|
|
};
|
|
};
|
|
|
|
config = {
|
|
users.users.${username} = {
|
|
configFiles = cfg.configFile;
|
|
cacheFiles = cfg.cacheFile;
|
|
dataFiles = cfg.dataFile;
|
|
stateFiles = cfg.stateFile;
|
|
homeFiles = config.home.file;
|
|
};
|
|
|
|
environment.sessionVariables = {
|
|
XDG_CONFIG_HOME = cfg.configHome;
|
|
XDG_CACHE_HOME = cfg.cacheHome;
|
|
XDG_DATA_HOME = cfg.dataHome;
|
|
XDG_STATE_HOME = cfg.stateHome;
|
|
};
|
|
|
|
system.activationScripts.xdgUserFiles = lib.stringAfter ["users"] ''
|
|
${lib.concatStringsSep "\n" (
|
|
lib.flatten (
|
|
lib.mapAttrsToList (
|
|
_user: userCfg:
|
|
(lib.mapAttrsToList (mkLinkScript cfg.configHome) userCfg.configFiles)
|
|
++ (lib.mapAttrsToList (mkLinkScript cfg.cacheHome) userCfg.cacheFiles)
|
|
++ (lib.mapAttrsToList (mkLinkScript cfg.dataHome) userCfg.dataFiles)
|
|
++ (lib.mapAttrsToList (mkLinkScript cfg.stateHome) userCfg.stateFiles)
|
|
++ (lib.mapAttrsToList (mkLinkScript config.home.homeDirectory) userCfg.homeFiles)
|
|
)
|
|
config.users.users
|
|
)
|
|
)}
|
|
'';
|
|
};
|
|
}
|