forked from jan-leila/nix-config
Compare commits
31 commits
28a962d712
...
3a58722815
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a58722815 | |||
| 197031975a | |||
| e8f7331b6c | |||
| ebf7ea3cf7 | |||
| dd165d48fe | |||
| 260e37e016 | |||
| 0c88746da1 | |||
| 46890110f8 | |||
| 290db94f42 | |||
| b05bfc31fe | |||
| 85a6f4a006 | |||
| 69ec14ef79 | |||
| 5ccfe1a337 | |||
| 62bb650878 | |||
| 488ef1e94a | |||
| 59dc4a7ee1 | |||
| 6afdcce951 | |||
| e895fa5edd | |||
| f02cb08570 | |||
| 352ca6fccf | |||
| c953571f2f | |||
| d87462981e | |||
| 75dcac8d17 | |||
| 80ad498f94 | |||
| 6d5a07e08f | |||
| 337f03b4e7 | |||
| a51a364ce9 | |||
| ee6d48fe49 | |||
| c81fa77a29 | |||
| 32c7086394 | |||
| f80ae02e47 |
83 changed files with 1139 additions and 311 deletions
18
.hooks/post-merge
Executable file
18
.hooks/post-merge
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env nix-shell
|
||||
#! nix-shell -i bash ../shell.nix
|
||||
|
||||
# Get current branch name
|
||||
current_branch=$(git branch --show-current)
|
||||
|
||||
# Only restore stash if we're on main branch and a merge just completed
|
||||
if [ "$current_branch" = "main" ]; then
|
||||
# Check if there are any stashes to restore
|
||||
if git stash list | grep -q "stash@"; then
|
||||
echo "Post-merge: restoring stashed changes on main branch"
|
||||
git stash pop -q
|
||||
else
|
||||
echo "Post-merge: no stash to restore on main branch"
|
||||
fi
|
||||
else
|
||||
echo "Post-merge: no action needed on branch '$current_branch'"
|
||||
fi
|
||||
37
.hooks/pre-merge-commit
Executable file
37
.hooks/pre-merge-commit
Executable file
|
|
@ -0,0 +1,37 @@
|
|||
#!/usr/bin/env nix-shell
|
||||
#! nix-shell -i bash ../shell.nix
|
||||
|
||||
# Get the target branch (the branch being merged into)
|
||||
target_branch=""
|
||||
|
||||
# Check if we're in the middle of a merge
|
||||
if [ -f .git/MERGE_HEAD ]; then
|
||||
# We're in a merge, check if the current branch is main
|
||||
current_branch=$(git branch --show-current)
|
||||
if [ "$current_branch" = "main" ]; then
|
||||
target_branch="main"
|
||||
fi
|
||||
fi
|
||||
|
||||
# If we're merging into main, run nix flake check
|
||||
if [ "$target_branch" = "main" ]; then
|
||||
echo "Merging into main branch - running nix flake check..."
|
||||
|
||||
echo "stashing all uncommitted changes"
|
||||
git stash -q --keep-index
|
||||
|
||||
echo "checking flakes all compile"
|
||||
nix flake check
|
||||
|
||||
if [ ! $? -eq 0 ]; then
|
||||
echo "Error: nix flake check failed. Merge aborted."
|
||||
echo "Please fix the issues and try merging again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "nix flake check passed. Merge can proceed."
|
||||
else
|
||||
echo "Not merging into main branch, skipping nix flake check."
|
||||
fi
|
||||
|
||||
exit 0
|
||||
84
README.md
84
README.md
|
|
@ -46,36 +46,56 @@ nix multi user, multi system, configuration with `sops` secret management, `home
|
|||
|
||||
# Tasks:
|
||||
|
||||
## Chores:
|
||||
- [ ] test out crab hole service
|
||||
- [ ] learn how to use actual
|
||||
|
||||
## Tech Debt
|
||||
- monitor configuration in `~/.config/monitors.xml` should be sym linked to `/run/gdm/.config/monitors.xml` (https://www.reddit.com/r/NixOS/comments/u09cz9/home_manager_create_my_own_symlinks_automatically/)
|
||||
- nfs export should be backed by the same values for server and client
|
||||
## New Features
|
||||
- crab-hole
|
||||
- figure out why syncthing and jellyfins permissions don't propagate downwards
|
||||
- figure out steam vr things?
|
||||
- auto turn off on power loss - nut
|
||||
- zfs email after scrubbing # TODO: test this
|
||||
- SMART test with email results
|
||||
- samba mounts
|
||||
- offline access for nfs mounts (overlay with rsync might be a good option here? https://www.spinics.net/lists/linux-unionfs/msg07105.html note about nfs4 and overlay fs)
|
||||
- Create Tor guard/relay server
|
||||
- migrate away from flakes and move to npins
|
||||
- whisper
|
||||
- zfs encryption FIDO2 2fa (look into shavee)
|
||||
- Secure Boot - https://github.com/nix-community/lanzaboote
|
||||
- rotate sops encryption keys periodically (and somehow sync between devices?)
|
||||
- wake on LAN for updates
|
||||
- remote distributed builds - https://nix.dev/tutorials/nixos/distributed-builds-setup.html
|
||||
- ISO target that contains authorized keys for nixos-anywhere https://github.com/diegofariasm/yggdrasil/blob/4acc43ebc7bcbf2e41376d14268e382007e94d78/hosts/bootstrap/default.nix
|
||||
- panoramax instance
|
||||
- mastodon instance
|
||||
- rework the reverse_proxy.nix file so that it is a normally named service. Then also change it so that we can hook into it with both a base domain and a subdomain to make migrating to vpn accessible services easier
|
||||
- move searx, home-assistant, actual, jellyfin, paperless, and immich to only be accessible via vpn
|
||||
- make radarr, sonarr, and bazarr accessible over vpn
|
||||
- create some sort of service that allows uploading files to jellyfin
|
||||
- auto sort files into where they should go with some combination of filebot cli and picard cli
|
||||
- graphana accessible though tailscale
|
||||
- fix panoramax package
|
||||
- actual instance
|
||||
- intergrade radarr, sonarr, and bazarr
|
||||
- claude code MCP servers should bundle node with them so they work in all environments
|
||||
- [ ] monitor configuration in `~/.config/monitors.xml` should be sym linked to `/run/gdm/.config/monitors.xml` (https://www.reddit.com/r/NixOS/comments/u09cz9/home_manager_create_my_own_symlinks_automatically/)
|
||||
- [ ] migrate away from flakes and move to npins
|
||||
- [ ] rework the reverse_proxy.nix file so that it is a normally named service. Then also change it so that we can hook into it with both a base domain and a subdomain to make migrating to vpn accessible services easier
|
||||
|
||||
## Broken things
|
||||
- [ ] figure out steam vr things?
|
||||
- [ ] whisper was having issues
|
||||
|
||||
## Data Integrity
|
||||
- [ ] zfs email after scrubbing # TODO: test this
|
||||
- [ ] SMART test with email results
|
||||
- [ ] zfs encryption FIDO2 2fa (look into shavee)
|
||||
- [ ] rotate sops encryption keys periodically (and somehow sync between devices?)
|
||||
- [ ] Secure Boot - https://github.com/nix-community/lanzaboote
|
||||
- [ ] auto turn off on power loss - nut
|
||||
|
||||
## Data Access
|
||||
- [ ] nfs export should be backed by the same values for server and client
|
||||
- [ ] samba mounts
|
||||
- [ ] offline access for nfs mounts (overlay with rsync might be a good option here? https://www.spinics.net/lists/linux-unionfs/msg07105.html note about nfs4 and overlay fs)
|
||||
- [ ] figure out why syncthing and jellyfins permissions don't propagate downwards
|
||||
- [ ] make radarr, sonarr, and bazarr accessible over vpn
|
||||
- [ ] move searx, home-assistant, actual, jellyfin, paperless, and immich to only be accessible via vpn
|
||||
|
||||
## Services
|
||||
- [ ] vikunja service for project management
|
||||
- [ ] Create Tor guard/relay server
|
||||
- [ ] mastodon instance
|
||||
|
||||
## DevOps
|
||||
- [ ] wake on LAN for updates
|
||||
- [ ] remote distributed builds - https://nix.dev/tutorials/nixos/distributed-builds-setup.html
|
||||
- [ ] ISO target that contains authorized keys for nixos-anywhere https://github.com/diegofariasm/yggdrasil/blob/4acc43ebc7bcbf2e41376d14268e382007e94d78/hosts/bootstrap/default.nix
|
||||
- [ ] fix panoramax package
|
||||
- [ ] claude code MCP servers should bundle node with them so they work in all environments
|
||||
|
||||
## Observability
|
||||
- [ ] graphana for dashboards
|
||||
- [ ] prometheus and loki for metric and log collection
|
||||
- [ ] zfs storage usage
|
||||
- [ ] zfs drive health status
|
||||
- [ ] service version lag
|
||||
- [ ] network/cpu/ram utilization
|
||||
- [ ] http latency
|
||||
- [ ] postgres db load
|
||||
- [ ] nginx queries
|
||||
- [ ] ntfy.sh for push notifications
|
||||
- [ ] kuma for uptime visualization
|
||||
|
|
@ -1,12 +1,39 @@
|
|||
{pkgs, ...}: {
|
||||
{
|
||||
osConfig,
|
||||
lib,
|
||||
...
|
||||
}: {
|
||||
config = {
|
||||
gnome = lib.mkMerge [
|
||||
{
|
||||
colorScheme = "prefer-dark";
|
||||
accentColor = "slate";
|
||||
clockFormat = "24h";
|
||||
nightLight = {
|
||||
enable = true;
|
||||
automatic = false;
|
||||
fromTime = 12.0;
|
||||
toTime = 11.999999999999;
|
||||
temperature = 2700;
|
||||
};
|
||||
extraWindowControls = true;
|
||||
extensions = {
|
||||
dash-to-panel = {
|
||||
enable = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
(lib.mkIf (osConfig.networking.hostName == "horizon") {
|
||||
displayScaling = 125;
|
||||
experimentalFeatures = {
|
||||
scaleMonitorFramebuffer = true;
|
||||
};
|
||||
})
|
||||
];
|
||||
|
||||
dconf = {
|
||||
enable = true;
|
||||
settings = {
|
||||
"org/gnome/shell".enabled-extensions = [
|
||||
pkgs.gnomeExtensions.dash-to-panel.extensionUuid
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ in {
|
|||
home.packages = lib.lists.optionals userConfig.isDesktopUser (
|
||||
with pkgs; [
|
||||
gnomeExtensions.dash-to-panel
|
||||
claude-code
|
||||
]
|
||||
);
|
||||
|
||||
|
|
@ -61,6 +62,22 @@ in {
|
|||
piper.enable = hardware.piperMouse.enable;
|
||||
krita.enable = true;
|
||||
ungoogled-chromium.enable = true;
|
||||
|
||||
inkscape.enable = true;
|
||||
obsidian.enable = true;
|
||||
obs-studio.enable = true;
|
||||
kdenlive.enable = true;
|
||||
tor-browser.enable = true;
|
||||
olympus.enable = true;
|
||||
libreoffice.enable = true;
|
||||
|
||||
claude-code.enable = osConfig.host.ai.enable;
|
||||
|
||||
# Windows applications that we need to figure out how to install
|
||||
guild-wars-2.enable = false;
|
||||
vortex.enable = false;
|
||||
dungeon-draft.enable = false;
|
||||
vmware-workstation.enable = true;
|
||||
})
|
||||
];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,31 +1,43 @@
|
|||
{pkgs, ...}: {
|
||||
{...}: {
|
||||
config = {
|
||||
gnome = {
|
||||
extraWindowControls = true;
|
||||
colorScheme = "prefer-dark";
|
||||
clockFormat = "24h";
|
||||
extensions = [
|
||||
pkgs.gnomeExtensions.dash-to-dock
|
||||
];
|
||||
nightLight = {
|
||||
enable = true;
|
||||
automatic = false;
|
||||
fromTime = 12.0;
|
||||
toTime = 11.999999999999;
|
||||
temperature = 2700;
|
||||
};
|
||||
extensions = {
|
||||
dash-to-dock = {
|
||||
enable = true;
|
||||
options = {
|
||||
"dock-position" = "LEFT";
|
||||
"intellihide-mode" = "ALL_WINDOWS";
|
||||
"show-trash" = false;
|
||||
"require-pressure-to-show" = false;
|
||||
"show-mounts" = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
hotkeys = {
|
||||
"Open Terminal" = {
|
||||
binding = "<Super>t";
|
||||
command = "kgx";
|
||||
};
|
||||
"Open Firefox" = {
|
||||
binding = "<Super>f";
|
||||
command = "firefox";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
dconf = {
|
||||
enable = true;
|
||||
settings = {
|
||||
"org/gnome/shell/extensions/dash-to-dock" = {
|
||||
"dock-position" = "LEFT";
|
||||
"intellihide-mode" = "ALL_WINDOWS";
|
||||
"show-trash" = false;
|
||||
"require-pressure-to-show" = false;
|
||||
"show-mounts" = false;
|
||||
};
|
||||
|
||||
"org/gnome/shell" = {
|
||||
favorite-apps = ["org.gnome.Nautilus.desktop" "firefox.desktop" "codium.desktop" "steam.desktop" "org.gnome.Console.desktop"];
|
||||
# app-picker-layout =
|
||||
|
|
|
|||
|
|
@ -87,7 +87,6 @@
|
|||
# TODO: move this into a fonts module
|
||||
home.packages = with pkgs; [
|
||||
aileron
|
||||
nerd-fonts.open-dyslexic
|
||||
];
|
||||
fonts.fontconfig.enable = true;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
config = lib.mkIf osConfig.host.impermanence.enable {
|
||||
config = lib.mkIf (config.impermanence.enable) {
|
||||
home.persistence."/persist/home/leyla" = {
|
||||
directories = [
|
||||
"desktop"
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ in {
|
|||
"javascript.updateImportsOnFileMove.enabled" = "always";
|
||||
"editor.tabSize" = 2;
|
||||
"editor.insertSpaces" = false;
|
||||
# "terminal.integrated.fontFamily" = "'Droid Sans Mono', 'monospace', monospace";
|
||||
}
|
||||
];
|
||||
|
||||
|
|
@ -40,6 +41,7 @@ in {
|
|||
oneDark.enable = true;
|
||||
atomKeybindings.enable = true;
|
||||
openRemoteSsh.enable = true;
|
||||
# openDyslexicFont.enable = false;
|
||||
|
||||
# html development
|
||||
autoRenameTag.enable = true;
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@
|
|||
"ata-ST18000NT001-3NF101_ZVTEF27J"
|
||||
"ata-ST18000NE000-3G6101_ZVTJ7359"
|
||||
]
|
||||
# TODO: this needs to be configured manually
|
||||
[
|
||||
"ata-ST4000NE001-2MA101_WS2275P3"
|
||||
"ata-ST4000NE001-2MA101_WS227B9F"
|
||||
|
|
@ -343,6 +342,20 @@
|
|||
openFirewall = true;
|
||||
};
|
||||
|
||||
crab-hole = {
|
||||
enable = true;
|
||||
port = 8085;
|
||||
openFirewall = true;
|
||||
show_doc = true;
|
||||
downstreams = {
|
||||
loopback = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
};
|
||||
upstreams.cloudFlare.enable = true;
|
||||
};
|
||||
|
||||
qbittorrent = {
|
||||
enable = true;
|
||||
mediaDir = "/srv/qbittorent";
|
||||
|
|
@ -350,21 +363,28 @@
|
|||
webuiPort = 8084;
|
||||
};
|
||||
|
||||
filebot-cleanup = {
|
||||
enable = true;
|
||||
licenseFile = "/srv/jellyfin/filebot_license.psm";
|
||||
};
|
||||
|
||||
sonarr = {
|
||||
enable = false;
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
radarr = {
|
||||
enable = false;
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
bazarr = {
|
||||
enable = false;
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
lidarr = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
jackett = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
flaresolverr = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,6 +4,5 @@
|
|||
./hardware-configuration.nix
|
||||
./configuration.nix
|
||||
./packages.nix
|
||||
./filebot.nix
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,82 +0,0 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
with lib; let
|
||||
cfg = config.services.filebot-cleanup;
|
||||
in {
|
||||
options.services.filebot-cleanup = {
|
||||
enable = mkEnableOption "Filebot cleanup service";
|
||||
|
||||
licenseFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = "Path to the Filebot license file";
|
||||
};
|
||||
|
||||
cleanupDirectory = mkOption {
|
||||
type = types.str;
|
||||
default = "/srv/jellyfin/filebot_cleanup";
|
||||
description = "Directory where cleaned up media files are stored";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
users.groups.filebot_cleanup = {};
|
||||
users.users.filebot_cleanup = {
|
||||
isSystemUser = true;
|
||||
group = "filebot_cleanup";
|
||||
extraGroups = ["jellyfin_media"];
|
||||
home = cfg.cleanupDirectory;
|
||||
createHome = true;
|
||||
};
|
||||
|
||||
nixpkgs.config.allowUnfreePredicate = pkg:
|
||||
builtins.elem (lib.getName pkg) [
|
||||
"filebot"
|
||||
];
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
filebot
|
||||
];
|
||||
|
||||
systemd.services.filebot-cleanup = {
|
||||
description = "Filebot media cleanup service";
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
User = "filebot_cleanup";
|
||||
Group = "filebot_cleanup";
|
||||
ExecStart = pkgs.writeShellScript "filebot-cleanup" ''
|
||||
${optionalString (cfg.licenseFile != null) ''
|
||||
${pkgs.filebot}/bin/filebot --license "${cfg.licenseFile}"
|
||||
''}
|
||||
${pkgs.filebot}/bin/filebot -rename -r "/srv/jellyfin/media/Movies/" --output "${cfg.cleanupDirectory}/" --format "{jellyfin}" -non-strict --action duplicate
|
||||
${pkgs.filebot}/bin/filebot -rename -r "/srv/jellyfin/media/Shows/" --output "${cfg.cleanupDirectory}/" --format "{jellyfin}" -non-strict --action duplicate
|
||||
'';
|
||||
StandardOutput = "journal";
|
||||
StandardError = "journal";
|
||||
};
|
||||
wantedBy = ["multi-user.target"];
|
||||
};
|
||||
|
||||
environment.persistence = lib.mkIf config.host.impermanence.enable {
|
||||
"/persist/system/jellyfin" = {
|
||||
enable = true;
|
||||
hideMounts = true;
|
||||
files = [
|
||||
cfg.licenseFile
|
||||
];
|
||||
directories = [
|
||||
{
|
||||
directory = cfg.cleanupDirectory;
|
||||
user = "filebot_cleanup";
|
||||
group = "filebot_cleanup";
|
||||
mode = "1770";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -40,13 +40,15 @@
|
|||
services.xserver.wacom.enable = true;
|
||||
|
||||
# installed opentabletdriver
|
||||
hardware.opentabletdriver.enable = true;
|
||||
# hardware.opentabletdriver.enable = true;
|
||||
hardware.keyboard.qmk.enable = true;
|
||||
|
||||
# Enable the GNOME Desktop Environment.
|
||||
services.displayManager.gdm.enable = true;
|
||||
services.desktopManager.gnome.enable = true;
|
||||
|
||||
host = {
|
||||
ai.enable = true;
|
||||
users = {
|
||||
eve = {
|
||||
isDesktopUser = true;
|
||||
|
|
@ -68,6 +70,9 @@
|
|||
};
|
||||
|
||||
services.tailscale.enable = true;
|
||||
# We were having weird build errors so this is disabled right now
|
||||
# error: The option `devices.emergent.folders.eve_records.path' was accessed but has no value defined. Try setting the option
|
||||
services.syncthing.enable = false;
|
||||
|
||||
# Configure keymap in X11
|
||||
# services.xserver.xkb.layout = "us";
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
(modulesPath + "/installer/scan/not-detected.nix")
|
||||
];
|
||||
|
||||
boot.initrd.availableKernelModules = ["xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod"];
|
||||
boot.initrd.availableKernelModules = ["xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" "wacom"];
|
||||
boot.initrd.kernelModules = [];
|
||||
boot.kernelModules = [];
|
||||
boot.extraModulePackages = [];
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@
|
|||
|
||||
fileSystems = {
|
||||
"/" = {
|
||||
device = "/dev/disk/by-uuid/8be49c65-2b57-48f1-b74d-244d26061adb";
|
||||
device = "/dev/disk/by-id/ata-TOSHIBA_DT01ACA100_77D21HVNS-part2";
|
||||
fsType = "ext4";
|
||||
};
|
||||
|
||||
"/boot" = {
|
||||
device = "/dev/disk/by-uuid/3006-3867";
|
||||
device = "/dev/disk/by-id/ata-TOSHIBA_DT01ACA100_77D21HVNS-part1";
|
||||
fsType = "vfat";
|
||||
options = ["fmask=0022" "dmask=0022"];
|
||||
};
|
||||
|
|
|
|||
98
flake.lock
generated
98
flake.lock
generated
|
|
@ -25,11 +25,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1758287904,
|
||||
"narHash": "sha256-IGmaEf3Do8o5Cwp1kXBN1wQmZwQN3NLfq5t4nHtVtcU=",
|
||||
"lastModified": 1760701190,
|
||||
"narHash": "sha256-y7UhnWlER8r776JsySqsbTUh2Txf7K30smfHlqdaIQw=",
|
||||
"owner": "nix-community",
|
||||
"repo": "disko",
|
||||
"rev": "67ff9807dd148e704baadbd4fd783b54282ca627",
|
||||
"rev": "3a9450b26e69dcb6f8de6e2b07b3fc1c288d85f5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -46,11 +46,11 @@
|
|||
},
|
||||
"locked": {
|
||||
"dir": "pkgs/firefox-addons",
|
||||
"lastModified": 1759403080,
|
||||
"narHash": "sha256-EteyL8KyG9R5xzqyOBzyag4n2cSemu61VFrl3opJSqE=",
|
||||
"lastModified": 1760673822,
|
||||
"narHash": "sha256-h+liPhhMw1yYvkDGLHzQJQShQs+yLjNgjfAyZX+sRrM=",
|
||||
"owner": "rycee",
|
||||
"repo": "nur-expressions",
|
||||
"rev": "8af6dfcbcbf1115a4f5aeed77ff0db5d3c02caf0",
|
||||
"rev": "5cca27f1bb30a26140d0cf60ab34daa45b4fa11f",
|
||||
"type": "gitlab"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -111,24 +111,6 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_3": {
|
||||
"inputs": {
|
||||
"systems": "systems_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flakey-profile": {
|
||||
"locked": {
|
||||
"lastModified": 1712898590,
|
||||
|
|
@ -151,11 +133,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1759337100,
|
||||
"narHash": "sha256-CcT3QvZ74NGfM+lSOILcCEeU+SnqXRvl1XCRHenZ0Us=",
|
||||
"lastModified": 1760662441,
|
||||
"narHash": "sha256-mlDqR1Ntgs9uYYEAUR1IhamKBO0lxoNS4zGLzEZaY0A=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "004753ae6b04c4b18aa07192c1106800aaacf6c3",
|
||||
"rev": "722792af097dff5790f1a66d271a47759f477755",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -182,11 +164,11 @@
|
|||
"lix": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1759624822,
|
||||
"narHash": "sha256-cf40qfsfpxJU/BnQ9PEj027LdPINNSsJqm+C6Ug93BA=",
|
||||
"rev": "57333a0e600c5e096a609410a2f1059b97194b1e",
|
||||
"lastModified": 1755787066,
|
||||
"narHash": "sha256-X2UwkUEban08GRSPXRr+kz8fckHqebr3P77qSvjoeOw=",
|
||||
"rev": "ac9721a92e8138d29707824dbedb484c76948493",
|
||||
"type": "tarball",
|
||||
"url": "https://git.lix.systems/api/v1/repos/lix-project/lix/archive/57333a0e600c5e096a609410a2f1059b97194b1e.tar.gz"
|
||||
"url": "https://git.lix.systems/api/v1/repos/lix-project/lix/archive/ac9721a92e8138d29707824dbedb484c76948493.tar.gz?rev=ac9721a92e8138d29707824dbedb484c76948493"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
|
|
@ -203,11 +185,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1756511062,
|
||||
"narHash": "sha256-IgD1JR7scSEwlK/YAbmrcTWpAYT30LPldCUHdzXkaMs=",
|
||||
"lastModified": 1759851320,
|
||||
"narHash": "sha256-n5dRAIC3/78drQtFxmQRrBLd6TKfotUnX7GWu0mAcSg=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "3f09a5eb772e02d98bb8878ab687d5b721f00d16",
|
||||
"revCount": 162,
|
||||
"rev": "7c31a18259b8358ac196cf803a26967c0fa1d3e4",
|
||||
"revCount": 163,
|
||||
"type": "git",
|
||||
"url": "https://git.lix.systems/lix-project/nixos-module.git"
|
||||
},
|
||||
|
|
@ -245,11 +227,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1758805352,
|
||||
"narHash": "sha256-BHdc43Lkayd+72W/NXRKHzX5AZ+28F3xaUs3a88/Uew=",
|
||||
"lastModified": 1760721282,
|
||||
"narHash": "sha256-aAHphQbU9t/b2RRy2Eb8oMv+I08isXv2KUGFAFn7nCo=",
|
||||
"owner": "LnL7",
|
||||
"repo": "nix-darwin",
|
||||
"rev": "c48e963a5558eb1c3827d59d21c5193622a1477c",
|
||||
"rev": "c3211fcd0c56c11ff110d346d4487b18f7365168",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -281,17 +263,16 @@
|
|||
},
|
||||
"nix-vscode-extensions": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils_3",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1759369908,
|
||||
"narHash": "sha256-IIhaE6jAge64z+fIyi/8Vtu0JdTtapbp4CvwiuIkZ1E=",
|
||||
"lastModified": 1760720017,
|
||||
"narHash": "sha256-ALb+L8zaP6IJ3BigQJ+ih7NqmaptzL/CbkNkLbhmsGE=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-vscode-extensions",
|
||||
"rev": "a66ad2141b1440a838ead278c6edfe8a4ce75e6c",
|
||||
"rev": "b0897a5d1d5829eb67ca7168680873ee7a0d52b8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -302,11 +283,11 @@
|
|||
},
|
||||
"nixos-hardware": {
|
||||
"locked": {
|
||||
"lastModified": 1759261527,
|
||||
"narHash": "sha256-wPd5oGvBBpUEzMF0kWnXge0WITNsITx/aGI9qLHgJ4g=",
|
||||
"lastModified": 1760106635,
|
||||
"narHash": "sha256-2GoxVaKWTHBxRoeUYSjv0AfSOx4qw5CWSFz2b+VolKU=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixos-hardware",
|
||||
"rev": "e087756cf4abbe1a34f3544c480fc1034d68742f",
|
||||
"rev": "9ed85f8afebf2b7478f25db0a98d0e782c0ed903",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -334,11 +315,11 @@
|
|||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1759381078,
|
||||
"narHash": "sha256-gTrEEp5gEspIcCOx9PD8kMaF1iEmfBcTbO0Jag2QhQs=",
|
||||
"lastModified": 1760524057,
|
||||
"narHash": "sha256-EVAqOteLBFmd7pKkb0+FIUyzTF61VKi7YmvP1tw4nEw=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "7df7ff7d8e00218376575f0acdcc5d66741351ee",
|
||||
"rev": "544961dfcce86422ba200ed9a0b00dd4b1486ec5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -389,11 +370,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1759188042,
|
||||
"narHash": "sha256-f9QC2KKiNReZDG2yyKAtDZh0rSK2Xp1wkPzKbHeQVRU=",
|
||||
"lastModified": 1760393368,
|
||||
"narHash": "sha256-8mN3kqyqa2PKY0wwZ2UmMEYMcxvNTwLaOrrDsw6Qi4E=",
|
||||
"owner": "Mic92",
|
||||
"repo": "sops-nix",
|
||||
"rev": "9fcfabe085281dd793589bdc770a2e577a3caa5d",
|
||||
"rev": "ab8d56e85b8be14cff9d93735951e30c3e86a437",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -431,21 +412,6 @@
|
|||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_3": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
src = fetchurl {
|
||||
url = "http://tools.mapillary.com/uploader/download/linux/${version}";
|
||||
name = "mapillary-uploader.AppImage";
|
||||
sha256 = "sha256-Oyx7AIdA/2mwBaq7UzXOoyq/z2SU2sViMN40sY2RCQw=";
|
||||
sha256 = "sha256-OY3SiMHUyjwPDrPWfa+mFg2BHZrz6GG/9/D5sCP2Da8=";
|
||||
};
|
||||
|
||||
appimageContents = appimageTools.extractType2 {
|
||||
|
|
@ -23,9 +23,6 @@ in
|
|||
# Install desktop file
|
||||
install -Dm644 ${appimageContents}/mapillary-desktop-uploader.desktop $out/share/applications/mapillary-uploader.desktop
|
||||
|
||||
# Install icon
|
||||
install -Dm644 ${appimageContents}/usr/share/icons/hicolor/0x0/apps/mapillary-desktop-uploader.png $out/share/pixmaps/mapillary-uploader.png
|
||||
|
||||
# Fix desktop file paths
|
||||
substituteInPlace $out/share/applications/mapillary-uploader.desktop \
|
||||
--replace 'Exec=AppRun' 'Exec=${pname}'
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
./user.nix
|
||||
./flipperzero.nix
|
||||
./i18n.nix
|
||||
./impermanence.nix
|
||||
./openssh.nix
|
||||
./gnome.nix
|
||||
./programs
|
||||
|
|
|
|||
|
|
@ -1,8 +1,16 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}: {
|
||||
}: let
|
||||
enabledExtensions =
|
||||
[]
|
||||
++ lib.optional config.gnome.extensions.dash-to-dock.enable pkgs.gnomeExtensions.dash-to-dock
|
||||
++ lib.optional config.gnome.extensions.dash-to-panel.enable pkgs.gnomeExtensions.dash-to-panel;
|
||||
|
||||
extensions = config.gnome.extraExtensions ++ enabledExtensions;
|
||||
in {
|
||||
options.gnome = {
|
||||
extraWindowControls = lib.mkEnableOption "Should we add back in the minimize and maximize window controls?";
|
||||
clockFormat = lib.mkOption {
|
||||
|
|
@ -34,7 +42,7 @@
|
|||
];
|
||||
default = "blue";
|
||||
};
|
||||
extensions = lib.mkOption {
|
||||
extraExtensions = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.package;
|
||||
default = [];
|
||||
description = "The set of extensions to install and enable in the user environment.";
|
||||
|
|
@ -60,16 +68,80 @@
|
|||
}));
|
||||
default = {};
|
||||
};
|
||||
displayScaling = lib.mkOption {
|
||||
type = lib.types.nullOr (lib.types.enum [100 125 150 175 200]);
|
||||
default = null;
|
||||
description = "Display scaling percentage for GNOME";
|
||||
};
|
||||
experimentalFeatures = lib.mkOption {
|
||||
type = lib.types.submodule {
|
||||
options = {
|
||||
scaleMonitorFramebuffer = lib.mkEnableOption "scale-monitor-framebuffer experimental feature";
|
||||
};
|
||||
};
|
||||
default = {};
|
||||
description = "GNOME experimental features to enable";
|
||||
};
|
||||
|
||||
nightLight = lib.mkOption {
|
||||
type = lib.types.submodule {
|
||||
options = {
|
||||
enable = lib.mkEnableOption "night light (blue light filter)";
|
||||
automatic = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Whether to automatically schedule night light based on sunset/sunrise";
|
||||
};
|
||||
fromTime = lib.mkOption {
|
||||
type = lib.types.float;
|
||||
default = 20.0;
|
||||
description = "Start time for night light in 24-hour format (e.g., 20.0 for 8:00 PM)";
|
||||
};
|
||||
toTime = lib.mkOption {
|
||||
type = lib.types.float;
|
||||
default = 6.0;
|
||||
description = "End time for night light in 24-hour format (e.g., 6.0 for 6:00 AM)";
|
||||
};
|
||||
temperature = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 4000;
|
||||
description = "Color temperature for night light (1000-10000K, lower is warmer)";
|
||||
};
|
||||
};
|
||||
};
|
||||
default = {};
|
||||
description = "Night light configuration";
|
||||
};
|
||||
|
||||
extensions = {
|
||||
dash-to-dock = {
|
||||
enable = lib.mkEnableOption "Dash to Dock extension";
|
||||
options = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.attrs;
|
||||
default = null;
|
||||
description = "Dash to Dock configuration options. If null, no custom configuration will be applied.";
|
||||
};
|
||||
};
|
||||
|
||||
dash-to-panel = {
|
||||
enable = lib.mkEnableOption "Dash to Panel extension";
|
||||
options = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.attrs;
|
||||
default = null;
|
||||
description = "Dash to Panel configuration options. If null, no custom configuration will be applied.";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
home.packages = config.gnome.extensions;
|
||||
home.packages = extensions;
|
||||
dconf = {
|
||||
settings = lib.mkMerge [
|
||||
{
|
||||
"org/gnome/shell" = {
|
||||
disable-user-extensions = false; # enables user extensions
|
||||
enabled-extensions = builtins.map (extension: extension.extensionUuid) config.gnome.extensions;
|
||||
enabled-extensions = builtins.map (extension: extension.extensionUuid) extensions;
|
||||
};
|
||||
|
||||
"org/gnome/desktop/wm/preferences".button-layout = lib.mkIf config.gnome.extraWindowControls ":minimize,maximize,close";
|
||||
|
|
@ -77,7 +149,23 @@
|
|||
"org/gnome/desktop/interface".color-scheme = config.gnome.colorScheme;
|
||||
"org/gnome/desktop/interface".accent-color = config.gnome.accentColor;
|
||||
"org/gnome/desktop/interface".clock-format = config.gnome.clockFormat;
|
||||
"org/gnome/desktop/interface".text-scaling-factor = lib.mkIf (config.gnome.displayScaling != null) (config.gnome.displayScaling / 100.0);
|
||||
|
||||
"org/gnome/mutter".experimental-features = lib.mkIf (builtins.any (x: x) (builtins.attrValues config.gnome.experimentalFeatures)) (
|
||||
lib.optional config.gnome.experimentalFeatures.scaleMonitorFramebuffer "scale-monitor-framebuffer"
|
||||
);
|
||||
}
|
||||
|
||||
# Night light configuration
|
||||
(lib.mkIf config.gnome.nightLight.enable {
|
||||
"org/gnome/settings-daemon/plugins/color" = {
|
||||
night-light-enabled = true;
|
||||
night-light-schedule-automatic = config.gnome.nightLight.automatic;
|
||||
night-light-schedule-from = lib.mkIf (!config.gnome.nightLight.automatic) config.gnome.nightLight.fromTime;
|
||||
night-light-schedule-to = lib.mkIf (!config.gnome.nightLight.automatic) config.gnome.nightLight.toTime;
|
||||
night-light-temperature = config.gnome.nightLight.temperature;
|
||||
};
|
||||
})
|
||||
(
|
||||
lib.mkMerge (
|
||||
builtins.map (value: let
|
||||
|
|
@ -100,6 +188,15 @@
|
|||
)
|
||||
)
|
||||
)
|
||||
|
||||
# Extension configurations
|
||||
(lib.mkIf (config.gnome.extensions.dash-to-dock.enable && config.gnome.extensions.dash-to-dock.options != null) {
|
||||
"org/gnome/shell/extensions/dash-to-dock" = config.gnome.extensions.dash-to-dock.options;
|
||||
})
|
||||
|
||||
(lib.mkIf (config.gnome.extensions.dash-to-panel.enable && config.gnome.extensions.dash-to-panel.options != null) {
|
||||
"org/gnome/shell/extensions/dash-to-panel" = config.gnome.extensions.dash-to-panel.options;
|
||||
})
|
||||
];
|
||||
};
|
||||
};
|
||||
|
|
|
|||
31
modules/home-manager-modules/impermanence.nix
Normal file
31
modules/home-manager-modules/impermanence.nix
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
osConfig,
|
||||
...
|
||||
}: let
|
||||
cfg = config.impermanence;
|
||||
in {
|
||||
options.impermanence = {
|
||||
enable = lib.mkEnableOption "impermanence for home directory";
|
||||
};
|
||||
|
||||
config = lib.mkMerge [
|
||||
(lib.mkIf config.impermanence.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = osConfig.impermanence.enable;
|
||||
message = "impermanence can not be enabled for a user when it is not enabled for a configuration";
|
||||
}
|
||||
];
|
||||
})
|
||||
(lib.mkIf osConfig.host.impermanence.enable {
|
||||
# If impermanence is not enabled for this user but system impermanence is enabled,
|
||||
# persist the entire home directory as fallback
|
||||
home.persistence."/persist/home/${config.home.username}" = lib.mkIf (!cfg.enable) {
|
||||
directories = ["."];
|
||||
allowOther = true;
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
|
|
@ -95,7 +95,7 @@
|
|||
);
|
||||
}
|
||||
)
|
||||
(lib.mkIf osConfig.host.impermanence.enable {
|
||||
(lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
files = lib.lists.flatten (
|
||||
builtins.map (hostKey: [".ssh/${hostKey.path}" ".ssh/${hostKey.path}.pub"]) config.programs.openssh.hostKeys
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.bitwarden = {
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/Bitwarden"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.bruno = {
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/bruno/"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.calibre = {
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/calibre"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.davinci-resolve = {
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.dataHome}/DaVinciResolve"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.dbeaver-bin = {
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.dataHome}/DBeaverData/"
|
||||
|
|
|
|||
|
|
@ -12,11 +12,13 @@
|
|||
./obsidian.nix
|
||||
./prostudiomasters.nix
|
||||
./idea.nix
|
||||
./kdenlive.nix
|
||||
./krita.nix
|
||||
./protonvpn.nix
|
||||
./calibre.nix
|
||||
./bruno.nix
|
||||
./dbeaver.nix
|
||||
./dungeon-draft.nix
|
||||
./steam.nix
|
||||
./vscode
|
||||
./ungoogled-chromium.nix
|
||||
|
|
@ -24,6 +26,7 @@
|
|||
./mapillary-uploader.nix
|
||||
./inkscape.nix
|
||||
./gimp.nix
|
||||
./guild-wars-2.nix
|
||||
./proxmark3.nix
|
||||
./freecad.nix
|
||||
./onionshare.nix
|
||||
|
|
@ -33,11 +36,14 @@
|
|||
./qflipper.nix
|
||||
./openvpn.nix
|
||||
./noisetorch.nix
|
||||
./olympus.nix
|
||||
./openrgb.nix
|
||||
./via.nix
|
||||
./vortex.nix
|
||||
./davinci-resolve.nix
|
||||
./gdx-liftoff.nix
|
||||
./tor-browser.nix
|
||||
./polycule.nix
|
||||
./vmware-workstation.nix
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.discord = {
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/discord/"
|
||||
|
|
|
|||
24
modules/home-manager-modules/programs/dungeon-draft.nix
Normal file
24
modules/home-manager-modules/programs/dungeon-draft.nix
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}: let
|
||||
cfg = config.programs.dungeon-draft;
|
||||
in {
|
||||
options.programs.dungeon-draft = {
|
||||
enable = lib.mkEnableOption "Dungeon Draft";
|
||||
};
|
||||
|
||||
config = {
|
||||
assertions = [
|
||||
{
|
||||
assertion = !cfg.enable;
|
||||
message = ''
|
||||
Dungeon Draft module is not yet fully configured.
|
||||
Please download the Dungeon Draft executable (.exe) from the official website,
|
||||
then configure the Wine environment and executable path as needed.
|
||||
'';
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: let
|
||||
buildProfilePersistence = profile: {
|
||||
|
|
@ -26,7 +25,7 @@
|
|||
allowOther = true;
|
||||
};
|
||||
in {
|
||||
config = lib.mkIf (config.programs.firefox.enable && osConfig.host.impermanence.enable) {
|
||||
config = lib.mkIf (config.programs.firefox.enable && config.impermanence.enable) {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = lib.mkMerge (
|
||||
(
|
||||
lib.attrsets.mapAttrsToList
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.freecad = {
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/FreeCAD"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.gdx-liftoff = {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.gimp = {
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/GIMP"
|
||||
|
|
|
|||
24
modules/home-manager-modules/programs/guild-wars-2.nix
Normal file
24
modules/home-manager-modules/programs/guild-wars-2.nix
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}: let
|
||||
cfg = config.programs.guild-wars-2;
|
||||
in {
|
||||
options.programs.guild-wars-2 = {
|
||||
enable = lib.mkEnableOption "Guild Wars 2";
|
||||
};
|
||||
|
||||
config = {
|
||||
assertions = [
|
||||
{
|
||||
assertion = !cfg.enable;
|
||||
message = ''
|
||||
Guild Wars 2 module is not yet fully configured.
|
||||
Please install Guild Wars 2 manually via Steam or the official client,
|
||||
then configure the Wine environment as needed.
|
||||
'';
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.jetbrains.idea-community = {
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
# configuration
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.inkscape = {
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/inkscape"
|
||||
|
|
|
|||
36
modules/home-manager-modules/programs/kdenlive.nix
Normal file
36
modules/home-manager-modules/programs/kdenlive.nix
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}: let
|
||||
cfg = config.programs.kdenlive;
|
||||
in {
|
||||
options.programs.kdenlive = {
|
||||
enable = lib.mkEnableOption "kdenlive";
|
||||
package = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
default = pkgs.kdePackages.kdenlive;
|
||||
description = "The kdenlive package to install.";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable (lib.mkMerge [
|
||||
{
|
||||
home.packages = [
|
||||
cfg.package
|
||||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/kdenliverc"
|
||||
"${config.xdg.dataHome}/kdenlive"
|
||||
];
|
||||
allowOther = true;
|
||||
};
|
||||
}
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.krita = {
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/kritarc"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.libreoffice = {
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/libreoffice"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.makemkv = {
|
||||
|
|
@ -30,7 +29,7 @@
|
|||
home.file.".MakeMKV/settings.conf".source = config.lib.file.mkOutOfStoreSymlink config.sops.templates."MakeMKV.settings.conf".path;
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
".MakeMKV"
|
||||
|
|
|
|||
|
|
@ -11,7 +11,20 @@ in {
|
|||
enable = mkEnableOption "Mapillary Desktop Uploader";
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
config = mkIf cfg.enable (mkMerge [
|
||||
{
|
||||
home.packages = [pkgs.mapillary-uploader];
|
||||
}
|
||||
(
|
||||
mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/mapillary-uploader"
|
||||
"${config.xdg.dataHome}/mapillary-uploader"
|
||||
];
|
||||
allowOther = true;
|
||||
};
|
||||
}
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.mfoc = {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.noisetorch = {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
config = lib.mkIf config.programs.obs-studio.enable (lib.mkMerge [
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
# TODO: map impermanence for obs
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/obs-studio"
|
||||
];
|
||||
allowOther = true;
|
||||
};
|
||||
}
|
||||
)
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
config = lib.mkIf config.programs.obsidian.enable (lib.mkMerge [
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/obsidian"
|
||||
|
|
|
|||
36
modules/home-manager-modules/programs/olympus.nix
Normal file
36
modules/home-manager-modules/programs/olympus.nix
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}: let
|
||||
cfg = config.programs.olympus;
|
||||
in {
|
||||
options.programs.olympus = {
|
||||
enable = lib.mkEnableOption "olympus";
|
||||
package = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
default = pkgs.olympus;
|
||||
description = "The olympus package to install.";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable (lib.mkMerge [
|
||||
{
|
||||
home.packages = [
|
||||
cfg.package
|
||||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/olympus"
|
||||
"${config.xdg.dataHome}/olympus"
|
||||
];
|
||||
allowOther = true;
|
||||
};
|
||||
}
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.onionshare = {
|
||||
|
|
|
|||
|
|
@ -2,16 +2,27 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.openrgb = {
|
||||
enable = lib.mkEnableOption "enable openrgb";
|
||||
};
|
||||
|
||||
config = lib.mkIf config.programs.openrgb.enable {
|
||||
config = lib.mkIf config.programs.openrgb.enable (lib.mkMerge [
|
||||
{
|
||||
home.packages = with pkgs; [
|
||||
openrgb
|
||||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/OpenRGB"
|
||||
];
|
||||
allowOther = true;
|
||||
};
|
||||
}
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.openvpn = {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.pdfarranger = {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.picard = {
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/MusicBrainz"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.piper = {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.polycule = {
|
||||
|
|
@ -17,7 +16,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
# TODO: check that these are actually the correct folders
|
||||
# directories = [
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.prostudiomasters = {
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/ProStudioMasters"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.protonvpn-gui = {
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/protonvpn"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.proxmark3 = {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.qbittorrent = {
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/qBittorrent"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.qflipper = {
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/qFlipper"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.signal-desktop-bin = {
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/Signal"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.steam = {
|
||||
|
|
@ -18,7 +17,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.tor-browser = {
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.dataHome}/torbrowser"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.ungoogled-chromium = {
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf osConfig.host.impermanence.enable {
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/chromium"
|
||||
|
|
|
|||
|
|
@ -2,16 +2,28 @@
|
|||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
options.programs.via = {
|
||||
enable = lib.mkEnableOption "enable via";
|
||||
};
|
||||
|
||||
config = lib.mkIf config.programs.via.enable {
|
||||
config = lib.mkIf config.programs.via.enable (lib.mkMerge [
|
||||
{
|
||||
home.packages = with pkgs; [
|
||||
via
|
||||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
"${config.xdg.configHome}/via"
|
||||
"${config.xdg.dataHome}/via"
|
||||
];
|
||||
allowOther = true;
|
||||
};
|
||||
}
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
37
modules/home-manager-modules/programs/vmware-workstation.nix
Normal file
37
modules/home-manager-modules/programs/vmware-workstation.nix
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}: {
|
||||
options.programs.vmware-workstation = {
|
||||
enable = lib.mkEnableOption "enable VMware Workstation";
|
||||
};
|
||||
|
||||
config = lib.mkIf config.programs.vmware-workstation.enable (
|
||||
lib.mkMerge [
|
||||
{
|
||||
home.packages = with pkgs; [
|
||||
vmware-workstation
|
||||
];
|
||||
}
|
||||
(
|
||||
lib.mkIf config.impermanence.enable {
|
||||
home.persistence."/persist${config.home.homeDirectory}" = {
|
||||
directories = [
|
||||
{
|
||||
directory = ".vmware";
|
||||
method = "symlink";
|
||||
}
|
||||
{
|
||||
directory = "vmware";
|
||||
method = "symlink";
|
||||
}
|
||||
];
|
||||
allowOther = true;
|
||||
};
|
||||
}
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
24
modules/home-manager-modules/programs/vortex.nix
Normal file
24
modules/home-manager-modules/programs/vortex.nix
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}: let
|
||||
cfg = config.programs.vortex;
|
||||
in {
|
||||
options.programs.vortex = {
|
||||
enable = lib.mkEnableOption "Vortex (Nexus Mods manager)";
|
||||
};
|
||||
|
||||
config = {
|
||||
assertions = [
|
||||
{
|
||||
assertion = !cfg.enable;
|
||||
message = ''
|
||||
Vortex module is not yet fully configured.
|
||||
Please download and install Vortex manually from the Nexus Mods website,
|
||||
then configure the Wine environment and dependencies as needed.
|
||||
'';
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
@ -23,5 +23,6 @@
|
|||
./vitest.nix
|
||||
./direnv.nix
|
||||
./conventionalCommits.nix
|
||||
./openDyslexicFont.nix
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}: {
|
||||
options.programs.vscode.profiles = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.submodule ({config, ...}: {
|
||||
options = {
|
||||
extraExtensions.openDyslexicFont = {
|
||||
enable = lib.mkEnableOption "should OpenDyslexic font be set as the default font for VSCode";
|
||||
package = lib.mkPackageOption pkgs "nerd-fonts.open-dyslexic" {
|
||||
default = ["nerd-fonts" "open-dyslexic"];
|
||||
};
|
||||
};
|
||||
};
|
||||
config = lib.mkIf config.extraExtensions.openDyslexicFont.enable {
|
||||
userSettings = {
|
||||
"editor.fontFamily" = "'OpenDyslexicM Nerd Font Mono', Droid Sans Mono, monospace";
|
||||
"editor.fontSize" = 14;
|
||||
"editor.letterSpacing" = -0.3;
|
||||
};
|
||||
};
|
||||
}));
|
||||
};
|
||||
|
||||
config = let
|
||||
enabledProfiles =
|
||||
lib.filter (profile: profile.extraExtensions.openDyslexicFont.enable or false)
|
||||
(lib.attrValues config.programs.vscode.profiles);
|
||||
|
||||
anyProfileUsesOpenDyslexicFont = enabledProfiles != [];
|
||||
|
||||
fontPackages = lib.unique (map (profile: profile.extraExtensions.openDyslexicFont.package) enabledProfiles);
|
||||
in {
|
||||
# Ensure OpenDyslexic font packages are installed when any VSCode profile uses them
|
||||
home.packages = fontPackages;
|
||||
|
||||
fonts.fontconfig.enable = lib.mkIf anyProfileUsesOpenDyslexicFont true;
|
||||
|
||||
# Add assertion to ensure the fonts are available
|
||||
assertions =
|
||||
map (fontPkg: {
|
||||
assertion = lib.elem fontPkg config.home.packages;
|
||||
message = "OpenDyslexic font package '${fontPkg.name or "unknown"}' must be installed when using openDyslexicFont extension for VSCode.";
|
||||
})
|
||||
fontPackages;
|
||||
};
|
||||
}
|
||||
144
modules/nixos-modules/server/crab-hole/crab-hole.nix
Normal file
144
modules/nixos-modules/server/crab-hole/crab-hole.nix
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}: let
|
||||
cfg = config.services.crab-hole;
|
||||
in {
|
||||
options.services.crab-hole = {
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 8080;
|
||||
description = "Port for the crab-hole API to listen on.";
|
||||
};
|
||||
|
||||
openFirewall = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Whether to open the firewall for the crab-hole API port.";
|
||||
};
|
||||
|
||||
listen = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "0.0.0.0";
|
||||
description = "Address for the crab-hole API to listen on.";
|
||||
};
|
||||
|
||||
show_doc = lib.mkEnableOption "OpenAPI documentation (loads content from third party websites)";
|
||||
|
||||
downstreams = {
|
||||
loopback = {
|
||||
enable = lib.mkEnableOption "loopback downstream DNS server on localhost:53";
|
||||
openFirewall = lib.mkEnableOption "automatic port forwarding for the loopback downstream";
|
||||
};
|
||||
};
|
||||
|
||||
extraDownstreams = lib.mkOption {
|
||||
type = lib.types.listOf (lib.types.submodule {
|
||||
options = {
|
||||
protocol = lib.mkOption {
|
||||
type = lib.types.enum ["udp" "tcp" "tls" "https" "quic"];
|
||||
description = "Protocol for the downstream server.";
|
||||
};
|
||||
|
||||
listen = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Address to listen on for downstream connections.";
|
||||
};
|
||||
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
description = "Port to listen on for downstream connections.";
|
||||
};
|
||||
};
|
||||
});
|
||||
default = [];
|
||||
description = "List of additional downstream DNS server configurations.";
|
||||
};
|
||||
|
||||
upstreams = {
|
||||
cloudFlare = {
|
||||
enable = lib.mkEnableOption "Cloudflare DNS over TLS upstream servers (1.1.1.1 and 1.0.0.1)";
|
||||
};
|
||||
};
|
||||
|
||||
extraUpstreams = lib.mkOption {
|
||||
type = lib.types.listOf (lib.types.submodule {
|
||||
options = {
|
||||
socket_addr = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Socket address of the upstream DNS server (e.g., \"1.1.1.1:853\" or \"[2606:4700:4700::1111]:853\").";
|
||||
};
|
||||
|
||||
protocol = lib.mkOption {
|
||||
type = lib.types.enum ["udp" "tcp" "tls" "https" "quic"];
|
||||
description = "Protocol to use for upstream DNS queries.";
|
||||
};
|
||||
};
|
||||
});
|
||||
default = [];
|
||||
description = "List of additional upstream DNS server configurations.";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
services.crab-hole.settings = lib.mkMerge [
|
||||
{
|
||||
api = {
|
||||
port = cfg.port;
|
||||
listen = cfg.listen;
|
||||
show_doc = cfg.show_doc;
|
||||
};
|
||||
downstream = cfg.extraDownstreams;
|
||||
upstream.name_servers = cfg.extraUpstreams;
|
||||
}
|
||||
(lib.mkIf cfg.downstreams.loopback.enable {
|
||||
downstream = [
|
||||
{
|
||||
protocol = "udp";
|
||||
listen = "localhost";
|
||||
port = 53;
|
||||
}
|
||||
];
|
||||
})
|
||||
(lib.mkIf cfg.upstreams.cloudFlare.enable {
|
||||
upstream.name_servers = [
|
||||
{
|
||||
socket_addr = "1.1.1.1:853";
|
||||
protocol = "tls";
|
||||
tls_dns_name = "1dot1dot1dot1.cloudflare-dns.com";
|
||||
trust_nx_responses = false;
|
||||
}
|
||||
{
|
||||
socket_addr = "1.0.0.1:853";
|
||||
protocol = "tls";
|
||||
tls_dns_name = "1dot1dot1dot1.cloudflare-dns.com";
|
||||
trust_nx_responses = false;
|
||||
}
|
||||
{
|
||||
socket_addr = "[2606:4700:4700::1111]:853";
|
||||
protocol = "tls";
|
||||
tls_dns_name = "1dot1dot1dot1.cloudflare-dns.com";
|
||||
trust_nx_responses = false;
|
||||
}
|
||||
{
|
||||
socket_addr = "[2606:4700:4700::1001]:853";
|
||||
protocol = "tls";
|
||||
tls_dns_name = "1dot1dot1dot1.cloudflare-dns.com";
|
||||
trust_nx_responses = false;
|
||||
}
|
||||
];
|
||||
})
|
||||
];
|
||||
|
||||
# Open firewall if requested
|
||||
networking.firewall = lib.mkMerge [
|
||||
(lib.mkIf cfg.openFirewall {
|
||||
allowedTCPPorts = [cfg.port];
|
||||
})
|
||||
(lib.mkIf (cfg.downstreams.loopback.enable && cfg.downstreams.loopback.openFirewall) {
|
||||
allowedUDPPorts = [53];
|
||||
})
|
||||
];
|
||||
};
|
||||
}
|
||||
6
modules/nixos-modules/server/crab-hole/default.nix
Normal file
6
modules/nixos-modules/server/crab-hole/default.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{...}: {
|
||||
imports = [
|
||||
./crab-hole.nix
|
||||
./impermanence.nix
|
||||
];
|
||||
}
|
||||
26
modules/nixos-modules/server/crab-hole/impermanence.nix
Normal file
26
modules/nixos-modules/server/crab-hole/impermanence.nix
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}: let
|
||||
workingDirectory = "/var/lib/private/crab-hole";
|
||||
in {
|
||||
config = lib.mkIf (config.services.immich.enable && config.host.impermanence.enable) {
|
||||
assertions = [
|
||||
{
|
||||
assertion =
|
||||
config.systemd.services.crab-hole.serviceConfig.WorkingDirectory == (builtins.replaceStrings ["/private"] [""] workingDirectory);
|
||||
message = "crab-hole working directory does not match persistence";
|
||||
}
|
||||
];
|
||||
environment.persistence."/persist/system/root" = {
|
||||
directories = [
|
||||
{
|
||||
directory = workingDirectory;
|
||||
user = "crab-hole";
|
||||
group = "crab-hole";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -8,10 +8,14 @@
|
|||
|
||||
./actual
|
||||
./bazarr
|
||||
./crab-hole
|
||||
./flaresolverr
|
||||
./forgejo
|
||||
./home-assistant
|
||||
./immich
|
||||
./jackett
|
||||
./jellyfin
|
||||
./lidarr
|
||||
./panoramax
|
||||
./paperless
|
||||
./qbittorent.nix
|
||||
|
|
|
|||
6
modules/nixos-modules/server/flaresolverr/default.nix
Normal file
6
modules/nixos-modules/server/flaresolverr/default.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{...}: {
|
||||
imports = [
|
||||
./proxy.nix
|
||||
./impermanence.nix
|
||||
];
|
||||
}
|
||||
19
modules/nixos-modules/server/flaresolverr/impermanence.nix
Normal file
19
modules/nixos-modules/server/flaresolverr/impermanence.nix
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}: {
|
||||
config = lib.mkIf (config.services.flaresolverr.enable && config.host.impermanence.enable) {
|
||||
# FlareSolverr typically doesn't need persistent storage as it's a proxy service
|
||||
# but we'll add basic structure in case it's needed for logs or configuration
|
||||
environment.persistence."/persist/system/root" = {
|
||||
directories = [
|
||||
{
|
||||
directory = "/var/lib/flaresolverr";
|
||||
user = "flaresolverr";
|
||||
group = "flaresolverr";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
28
modules/nixos-modules/server/flaresolverr/proxy.nix
Normal file
28
modules/nixos-modules/server/flaresolverr/proxy.nix
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}: {
|
||||
options.services.flaresolverr = {
|
||||
subdomain = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "Subdomain for reverse proxy. If null, service will be local only.";
|
||||
};
|
||||
extraSubdomains = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [];
|
||||
description = "Extra subdomains for reverse proxy.";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf (config.services.flaresolverr.enable && config.services.flaresolverr.subdomain != null) {
|
||||
host.reverse_proxy.subdomains.flaresolverr = {
|
||||
subdomain = config.services.flaresolverr.subdomain;
|
||||
extraSubdomains = config.services.flaresolverr.extraSubdomains;
|
||||
target = "http://127.0.0.1:${toString config.services.flaresolverr.port}";
|
||||
websockets.enable = true;
|
||||
forwardHeaders.enable = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
6
modules/nixos-modules/server/jackett/default.nix
Normal file
6
modules/nixos-modules/server/jackett/default.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{...}: {
|
||||
imports = [
|
||||
./proxy.nix
|
||||
./impermanence.nix
|
||||
];
|
||||
}
|
||||
26
modules/nixos-modules/server/jackett/impermanence.nix
Normal file
26
modules/nixos-modules/server/jackett/impermanence.nix
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}: let
|
||||
jackett_data_directory = "/var/lib/jackett/.config/Jackett";
|
||||
in {
|
||||
config = lib.mkIf (config.services.jackett.enable && config.host.impermanence.enable) {
|
||||
assertions = [
|
||||
{
|
||||
assertion = config.services.jackett.dataDir == jackett_data_directory;
|
||||
message = "jackett data directory does not match persistence";
|
||||
}
|
||||
];
|
||||
|
||||
environment.persistence."/persist/system/root" = {
|
||||
directories = [
|
||||
{
|
||||
directory = jackett_data_directory;
|
||||
user = "jackett";
|
||||
group = "jackett";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
28
modules/nixos-modules/server/jackett/proxy.nix
Normal file
28
modules/nixos-modules/server/jackett/proxy.nix
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}: {
|
||||
options.services.jackett = {
|
||||
subdomain = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "Subdomain for reverse proxy. If null, service will be local only.";
|
||||
};
|
||||
extraSubdomains = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [];
|
||||
description = "Extra subdomains for reverse proxy.";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf (config.services.jackett.enable && config.services.jackett.subdomain != null) {
|
||||
host.reverse_proxy.subdomains.jackett = {
|
||||
subdomain = config.services.jackett.subdomain;
|
||||
extraSubdomains = config.services.jackett.extraSubdomains;
|
||||
target = "http://127.0.0.1:9117";
|
||||
websockets.enable = true;
|
||||
forwardHeaders.enable = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
6
modules/nixos-modules/server/lidarr/default.nix
Normal file
6
modules/nixos-modules/server/lidarr/default.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{...}: {
|
||||
imports = [
|
||||
./proxy.nix
|
||||
./impermanence.nix
|
||||
];
|
||||
}
|
||||
26
modules/nixos-modules/server/lidarr/impermanence.nix
Normal file
26
modules/nixos-modules/server/lidarr/impermanence.nix
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}: let
|
||||
lidarr_data_directory = "/var/lib/lidarr/.config/Lidarr";
|
||||
in {
|
||||
config = lib.mkIf (config.services.lidarr.enable && config.host.impermanence.enable) {
|
||||
assertions = [
|
||||
{
|
||||
assertion = config.services.lidarr.dataDir == lidarr_data_directory;
|
||||
message = "lidarr data directory does not match persistence";
|
||||
}
|
||||
];
|
||||
|
||||
environment.persistence."/persist/system/root" = {
|
||||
directories = [
|
||||
{
|
||||
directory = lidarr_data_directory;
|
||||
user = "lidarr";
|
||||
group = "lidarr";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
28
modules/nixos-modules/server/lidarr/proxy.nix
Normal file
28
modules/nixos-modules/server/lidarr/proxy.nix
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}: {
|
||||
options.services.lidarr = {
|
||||
subdomain = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "Subdomain for reverse proxy. If null, service will be local only.";
|
||||
};
|
||||
extraSubdomains = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [];
|
||||
description = "Extra subdomains for reverse proxy.";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf (config.services.lidarr.enable && config.services.lidarr.subdomain != null) {
|
||||
host.reverse_proxy.subdomains.lidarr = {
|
||||
subdomain = config.services.lidarr.subdomain;
|
||||
extraSubdomains = config.services.lidarr.extraSubdomains;
|
||||
target = "http://127.0.0.1:8686";
|
||||
websockets.enable = true;
|
||||
forwardHeaders.enable = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
osConfig,
|
||||
...
|
||||
}: {
|
||||
config = lib.mkIf (config.services.panoramax.enable && osConfig.host.impermanence.enable) {
|
||||
config = lib.mkIf (config.services.panoramax.enable && config.host.impermanence.enable) {
|
||||
# TODO: configure impermanence for panoramax data
|
||||
# This would typically include directories like:
|
||||
# - /var/lib/panoramax
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@
|
|||
radarr = 2014;
|
||||
sonarr = 2015;
|
||||
bazarr = 2016;
|
||||
lidarr = 2017;
|
||||
crab-hole = 2018;
|
||||
};
|
||||
|
||||
gids = {
|
||||
|
|
@ -50,6 +52,8 @@
|
|||
radarr = 2014;
|
||||
sonarr = 2015;
|
||||
bazarr = 2016;
|
||||
lidarr = 2017;
|
||||
crab-hole = 2018;
|
||||
};
|
||||
|
||||
users = config.users.users;
|
||||
|
|
@ -221,6 +225,18 @@ in {
|
|||
isSystemUser = true;
|
||||
group = config.users.users.bazarr.name;
|
||||
};
|
||||
|
||||
lidarr = {
|
||||
uid = lib.mkForce uids.lidarr;
|
||||
isSystemUser = true;
|
||||
group = config.users.users.lidarr.name;
|
||||
};
|
||||
|
||||
crab-hole = {
|
||||
uid = lib.mkForce uids.crab-hole;
|
||||
isSystemUser = true;
|
||||
group = config.users.users.crab-hole.name;
|
||||
};
|
||||
};
|
||||
|
||||
groups = {
|
||||
|
|
@ -261,6 +277,7 @@ in {
|
|||
users.radarr.name
|
||||
users.sonarr.name
|
||||
users.bazarr.name
|
||||
users.lidarr.name
|
||||
leyla
|
||||
eve
|
||||
ivy
|
||||
|
|
@ -365,6 +382,20 @@ in {
|
|||
users.bazarr.name
|
||||
];
|
||||
};
|
||||
|
||||
lidarr = {
|
||||
gid = lib.mkForce gids.lidarr;
|
||||
members = [
|
||||
users.lidarr.name
|
||||
];
|
||||
};
|
||||
|
||||
crab-hole = {
|
||||
gid = lib.mkForce gids.crab-hole;
|
||||
members = [
|
||||
users.crab-hole.name
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
52
rebuild.sh
52
rebuild.sh
|
|
@ -1,5 +1,15 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Get current branch and git status for branch-aware behavior
|
||||
current_branch=$(git branch --show-current 2>/dev/null || echo "unknown")
|
||||
git_status=$(git status --porcelain 2>/dev/null || echo "")
|
||||
|
||||
# Default values
|
||||
default_target=$(hostname)
|
||||
default_user="$USER"
|
||||
default_host=$(hostname)
|
||||
default_mode=$(if [[ "$current_branch" != "main" ]]; then echo "test"; else echo "switch"; fi)
|
||||
|
||||
if [ -d "result" ];
|
||||
then
|
||||
preserve_result=true
|
||||
|
|
@ -42,14 +52,29 @@ while [ $# -gt 0 ]; do
|
|||
;;
|
||||
--help|-h)
|
||||
echo "--help -h: print this message"
|
||||
echo "--target -t: set the target system to rebuild on"
|
||||
echo "--flake -f: set the flake to rebuild on the target system"
|
||||
echo "--mode -m: set the mode to rebuild flake as on the target system"
|
||||
echo "--user -u: set the user to rebuild flake as on the target system"
|
||||
echo "--host: set the host that the flake will be rebuilt on (unset for current machine)"
|
||||
echo "--target -t: defaults to the current system"
|
||||
echo " currently: $default_target"
|
||||
echo "--flake -f: defaults to same as target"
|
||||
echo " currently: ${target:-$default_target}"
|
||||
echo "--mode -m: defaults to 'switch', but 'test' on non-main branches"
|
||||
echo " currently would be: $default_mode"
|
||||
echo "--user -u: defaults to the current user"
|
||||
echo " currently: $default_user"
|
||||
echo "--host: defaults to building on the current machine"
|
||||
echo " currently: $default_host"
|
||||
echo "--preserve-result: do not remove the generated result folder after building"
|
||||
echo "--no-preserve-result: remove any result folder after building"
|
||||
echo "--show-trace: show trace on builds"
|
||||
echo ""
|
||||
echo "Branch-aware behavior:"
|
||||
echo " - On non-main branches: defaults to test mode with warning"
|
||||
echo " - On main with uncommitted changes: shows warning about creating a branch"
|
||||
echo " - Current branch: $current_branch"
|
||||
if [[ -n "$git_status" ]]; then
|
||||
echo " - Git status: uncommitted changes detected"
|
||||
else
|
||||
echo " - Git status: clean working tree"
|
||||
fi
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
|
|
@ -60,10 +85,21 @@ while [ $# -gt 0 ]; do
|
|||
shift
|
||||
done
|
||||
|
||||
target=${target:-$(hostname)}
|
||||
target=${target:-$default_target}
|
||||
flake=${flake:-$target}
|
||||
mode=${mode:-switch}
|
||||
user=${user:-$USER}
|
||||
mode=${mode:-$default_mode}
|
||||
user=${user:-$default_user}
|
||||
|
||||
# Branch-aware warnings and behavior
|
||||
if [[ "$current_branch" != "main" ]] && [[ "$mode" == "test" ]]; then
|
||||
echo "⚠️ WARNING: You are on branch '$current_branch' (not main)"
|
||||
echo " Defaulting to test mode to prevent accidental system changes"
|
||||
echo " Specify --mode=switch explicitly if you want to apply changes"
|
||||
elif [[ "$current_branch" == "main" ]] && [[ -n "$git_status" ]] && [[ "$mode" != "test" ]]; then
|
||||
echo "⚠️ WARNING: You are on main branch with uncommitted changes"
|
||||
echo " Consider creating a feature branch for development:"
|
||||
echo " git checkout -b feature/your-feature-name"
|
||||
fi
|
||||
|
||||
command="nixos-rebuild $mode --sudo --ask-sudo-password --flake .#$flake"
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue