Compare commits

...

No commits in common. "master" and "before-git" have entirely different histories.

618 changed files with 360 additions and 67923 deletions

View file

@ -1,19 +0,0 @@
name: Discord
on:
- workflow_dispatch
- push
jobs:
discord_commits:
runs-on: ubuntu-latest
name: discord commits
if: contains(github.event.head_commit.message, '(servers)')
steps:
- name: Discommit
uses: https://github.com/matt1432/discommit@v0.0.2
with:
discord_webhook: ${{ secrets.DISCORD_WEBHOOK }}
api_url: 'https://git.nelim.org/api/v1/repos/$OWNER/$REPO/git/commits/$REF'
title: 'New commit containing changes to server configs:'

5
.gitattributes vendored
View file

@ -1,5 +0,0 @@
flake.lock -diff
flake.nix -diff
**/non-declarative-conf -diff
**/package-lock.json -diff
**/HomeAssistantGenerated -diff

23
.gitignore vendored
View file

@ -1,23 +0,0 @@
# Python
*.egg-info
# NPM
*node_modules
*build/
# Direnv
*.direnv/
# Generated by nix
result*
!results/
.nixd.json
## AGS
**/vars.ts
**/config.js
*icons
**/types
# Other
*.temp

View file

@ -1,16 +0,0 @@
MIT No Attribution
Copyright 2024 Mathis H.
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

324
README.md
View file

@ -1,100 +1,236 @@
# My NixOS configs
## AGS
You might find it weird that most of my config is written in TypeScript.
That's because all my desktops run
[AGS](https://github.com/Aylur/ags)
for UI. Click on
[this](https://git.nelim.org/matt1432/nixos-configs/src/branch/master/modules/ags)
to see my configuration.
I'm also a victim of Stockholm syndrome at this point and make my scripts
in TypeScript because it's the scripting language I am most comfortable with.
## About
### General
This repo is the complete configuration of machines I own running NixOS or Nix
and any other related smaller projects exposed by a Nix Flake.
Its main directory structure is based on a flake's
[outputs](https://wiki.nixos.org/wiki/Flakes#Output_schema).
I try to follow a few rules to better organise my Nix code:
- Every main subdirectory only has an optional `default.nix` and subfolders for each
of its attributes.
- Inside a subdirectory, if there is non nix code, it will be in a `config` folder.
- Every module should not do anything if imported. An enable option should be toggled
for it to have any effect.
- Any nix file that represents a module should be named `default.nix` (a nix file
which is imported directly can be called anything else alongside `default.nix`)
### Flake Location
This git repo will always be located at `$FLAKE` (`config.environment.variables.FLAKE`)
and symlinked to `/etc/nixos` to have everything where NixOS tools
expect things to be.
ie.
```bash
sudo rm -r /etc/nixos
echo "$FLAKE" # /home/matt/.nix
sudo ln -sf /home/matt/.nix /etc/nixos
# Archinstaller
```
loadkeys ca
setfont ter-132b
```
### Subdirectories
| Output / Directory | Description |
| -------------------- | ----------- |
| `apps` | [Misc scripts ran from the flake](./apps) |
| `configurations` | [device, ISO and nix-on-droid configurations](./configurations) |
| `devShells` | [Development shells for a bunch of projects and languages](./devShells) |
| `homeManagerModules` | [Modules made for home-manager](./homeManagerModules) |
| `inputs` | [Pre-evaluated flake inputs](./inputs) |
| `lib` | [Custom Nix functions made easily available](./lib) |
| `modules` | [Modules made for NixOS systems](./modules) |
| `nixFastChecks` | [Attribute set of derivations exposed by this flake](./nixFastChecks) |
| `overlays` | [Nixpkgs overlays](./overlays) |
| `packages` | [Some custom packages not available in nixpkgs or modified from it](./packages) |
| `results` | Directory where I neatly keep my result symlinks from `nixFastChecks` |
| `scopedPackages` | [Some custom package scopes not available in nixpkgs or modified from it](./scopedPackages) |
### Secrets
All my secrets are in a private git repo that makes use of
[sops-nix](https://github.com/Mic92/sops-nix).
I generate `.sops.yaml` from `.sops.nix`:
```nix
let
wim = "somekey";
binto = "somekey2";
in {
creation_rules = [
{
path_regex = "secrets/[^/]+\\.(yaml|json|env|ini)$";
key_groups = [
{
age = [wim binto];
}
];
}
];
}
## Partionning with [cryptsetup](https://wiki.archlinux.org/title/Dm-crypt/Encrypting_an_entire_system#LUKS_on_a_partition)
### Encrypting root partition
```
$ PART="encrypted partition number ie. 2"
$ cryptsetup -y -v luksFormat --type luks1 /dev/nvme0n1p$PART
$ cryptsetup open /dev/nvme0n1p$PART root
$ mkfs.btrfs /dev/mapper/root
$ mount /dev/mapper/root /mnt
```
### Mounting boot :
```
$ mount --mkdir /dev/nvme0n1p1 /mnt/boot
```
which is then converted to `.sops.yaml` using
[remarshal](https://github.com/remarshal-project/remarshal)
and this shell command:
```bash
nix eval --json --file ./.sops.nix | remarshal --if json --of yaml > .sops.yaml
### Installing packages on the device
```
$ pacstrap -K /mnt base linux-firmware linux amd-ucode patch dkms kmod btrfs-progs grub os-prober ntfs-3g efibootmgr efivar networkmanager iwd nano sudo texinfo man-db man-pages
```
TLDR: I **[hate](https://ruudvanasseldonk.com/2023/01/11/the-yaml-document-from-hell)** YAML
## Preparing for chroot
```
$ genfstab -U /mnt >> /mnt/etc/fstab
$ arch-chroot /mnt
```
# Chroot in Installed Arch
```
$ ln -sf /usr/share/zoneinfo/America/Montreal /etc/localtime
$ hwclock --systohc
$ echo matt-laptop > /etc/hostname
$ passwd
```
## Localization
Uncomment ca_FR.UTF-8 en_CA.UTF-8 en_US.UTF-8 fr_CA.UTF-8 and run
```
$ locale-gen
$ echo LANG=en_CA.UTF-8 > /etc/locale.conf
$ echo KEYMAP=ca > /etc/vconsole.conf
```
## Edit /etc/mkinitcpio.conf for LUKS
```
$ sed -i 's/BINARIES=.*/BINARIES=(btrfs)/' /etc/mkinitcpio.conf
...
$ sed -i 's/HOOKS=.*/HOOKS=(base udev autodetect modconf kms keyboard keymap consolefont block encrypt filesystems fsck)/' /etc/mkinitcpio.conf
```
then run ```mkinitpcio -P```
## Grub install
```
$ grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=wim --boot-directory=/boot/EFI/wim
```
### Edit /etc/default/grub for LUKS
```
$ CRYPT="cryptdevice=$(blkid | sed -n 's/.*nvme0n1p'$PART': \(.*\) TYPE.*/\1/p'):root"
$ sed -i 's#GRUB_CMDLINE_LINUX_DEFAULT.*#GRUB_CMDLINE_LINUX_DEFAULT="quiet loglevel 3 '$CRYPT' root=/dev/mapper/root"#' /etc/default/grub
```
make sure the UUID is the actual partition inside the LUKS container and run ```grub-mkconfig -o /boot/EFI/wim/grub/grub.cfg```
we can now reboot to the installed Arch
<br/><br/>
# Inside installed Arch
## Configure [internet](https://wiki.archlinux.org/title/Iwd) access
```
$ systemctl enable --now NetworkManager systemd-networkd systemd-resolved systemd-timesyncd
$ cat << EOF >> /etc/NetworkManager/conf.d/wifi_backend.conf
[device]
wifi.backend=iwd
EOF
$ cat << EOF >> /etc/iwd/main.conf
[General]
EnableNetworkConfiguration=true
EOF
$ systemctl stop iwd
$ systemctl restart NetworkManager
$ iwctl device list # check if powered on
$ iwctl station wlan0 scan
$ iwctl station wlan0 get-networks
$ iwctl station wlan0 connect $SSID
```
### Configure systemd-resolved
```
$ ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
$ sed -i 's/#DNS=.*/DNS=100.64.0.1/' /etc/systemd/resolved.conf
$ sed -i 's/#FallbackDNS=.*/FallbackDNS=1.1.1.1/' /etc/systemd/resolved.conf
$ sed -i 's/#DNSStubListener=.*/DNSStubListener=no/' /etc/systemd/resolved.conf
```
### Configure reflector for mirror management of pacman
```
$ pacman -Sy reflector
$ nano /etc/xdg/reflector/reflector.conf
$ systemctl enable --now reflector.timer
```
## User management
```
$ useradd -m matt -G wheel,input
$ passwd matt
$ env EDITOR=nano visudo # set wheel to allow sudo commands with pass
```
## A lot of packages to install
```
$ pacman -Sy htop pkgfile mlocate rsync tmux mosh usbutils wget git curl devtools mesa lib32-mesa vulkan-radeon lib32-vulkan-radeon libva-mesa-driver lib32-libva-mesa-driver mesa-vdpau lib32-mesa-vdpau bash-completion fzf
$ pkgfile --update
```
## su matt
## Install paru
```
$ sudo pacman -S --needed git base-devel
$ git clone https://aur.archlinux.org/paru-git.git
$ cd paru-git
$ makepkg -si
$ sed -i 's/#Color/Color/' /etc/pacman.conf
$ sed -i 's/#IgnorePkg.*/IgnorePkg = linux-xanmod-anbox linux-xanmod-anbox-headers/' /etc/pacman.conf
$ cat << EOF >> /etc/paru.conf
CombinedUpgrade
BatchInstall
BottomUp
NoWarn = plymouth-theme-arch-elegant
EOF
```
## Audio
### ALSA
```
$ yay -Sy alsa-utils alsa-firmware sof-firmware alsa-ucm-conf
#unmute speakers
$ amixer sset Master unmute
```
### Pipewire
```
$ yay -Sy pipewire-audio pipewire-alsa pipewire-pulse
$ yay -R pulseaudio-alsa
$ sudo systemctl stop pulseaudio.service
$ systemctl --user enable --now pipewire-pulse.service
```
## Fingerprint Sensor Hack
```
$ yay -Sy python pam-fprint-grosshack
$ sudo systemctl enable --now fprintd
$ fprintd-enroll
```
### Use the reader
add this to the top of every file in /etc/pam.d/ that you want ie. polkit-1, sudo uwu
```
auth sufficient pam_fprintd_grosshack.so
auth sufficient pam_unix.so try_first_pass nullok
```
OR (for gtklock and check for sddm)
```
auth sufficient pam_fprintd.so
```
## Plymouth and Silent Boot
By following the wiki pages on [watchdogs](https://wiki.archlinux.org/title/Improving_performance#Watchdogs), [silent booting](https://wiki.archlinux.org/title/Silent_boot#top-page) and [Plymouth](https://wiki.archlinux.org/title/Plymouth), I edited my grub config and mkinitcpio, installed and setup Plymouth, to get a satisfying booting experience
```
$ yay -Sy plymouth-git
```
/etc/mkinitcpio.conf
```
$ sudo sed -i 's/MODULES=()/MODULES=(amdgpu)/' /etc/mkinitcpio.conf
$ sudo sed -i 's/#COMPRESSION="lz4"/COMPRESSION="lz4"/' /etc/mkinitcpio.conf
$ sudo sed -i 's/HOOKS=(.* /HOOKS=(base udev plymouth encrypt autodetect modconf kms keyboard keymap consolefont block filesystems fsck)/' /etc/mkinitcpio.conf
COMPRESSION="lz4"
```
/etc/default/grub
```
sudo sed -i 's/quiet loglevel 3/quiet splash loglevel=3 systemd.show_status=auto rd.udev.log_level=3 splash nowatchdog psi=1/' /etc/default/grub
```
Mute watchdog
```
$ echo blacklist sp5100_tco | sudo tee /etc/modprobe.d/disable-sp5100-watchdog.conf
```
Apply changes [Theme](https://github.com/murkl/plymouth-theme-arch-elegant)
```
$ git clone https://github.com/murkl/plymouth-theme-arch-elegant.git
$ cd plymouth-theme-arch-elegant/aur
$ makepkg -si
$ sudo plymouth-set-default-theme -R arch-elegant
$ sudo grub-mkconfig -o /boot/grub/grub.cfg
$ sudo sed -i 's/echo/#ech~o/g' /boot/grub/grub.cfg
```
## Here are some random changes and tweaks
### Firefox touchscreen [tweak](https://wiki.archlinux.org/title/Firefox/Tweaks#Enable_touchscreen_gestures)
```
$ echo MOZ_USE_XINPUT2 DEFAULT=1 | sudo tee -a /etc/security/pam_env.conf
```
then logout
### More Packages that are most likely needed
```
run toinstall.sh script
$ sudo reboot
```
### Flatpak
```
$ flatpak install com.github.iwalton3.jellyfin-media-player com.github.tchx84.Flatseal xournalpp stemlink
$ sudo flatpak override --filesystem=xdg-config/gtk-3.0
```
## Finally, install dotfiles
### get access to repo first
```
$ mkdir ~/git && cd ~/git
$ git clone git@git.nelim.org:matt1432/dotfiles.git
$ cd dotfiles
$ bash getenv.sh
$ sudo bash setup.sh
$ sudo bash fzf.sh /usr/share/fzf
```

View file

@ -1,110 +0,0 @@
{
inputs = import ./inputs;
outputs = inputs @ {
self,
systems,
nixpkgs,
secrets,
...
}: let
inherit (self.lib) mkNixOS mkNixOnDroid mkPkgs;
perSystem = attrs:
nixpkgs.lib.genAttrs (import systems) (system:
attrs (mkPkgs {inherit system nixpkgs;}));
in {
lib = import ./lib {inherit inputs perSystem;};
nixOnDroidConfigurations.default =
mkNixOnDroid [./configurations/android];
nixosConfigurations = {
# Desktops
wim = mkNixOS {
extraModules = [
./configurations/wim
secrets.nixosModules.default
];
};
binto = mkNixOS {
cudaSupport = true;
extraModules = [./configurations/binto];
};
bbsteamie = mkNixOS {
mainUser = "mariah";
extraModules = [./configurations/bbsteamie];
};
# NAS
nos = mkNixOS {
cudaSupport = true;
extraModules = [
./configurations/nos
secrets.nixosModules.nos
];
};
# Build / test server
servivi = mkNixOS {
extraModules = [
./configurations/servivi
secrets.nixosModules.servivi
];
};
# Home-assistant
homie = mkNixOS {
extraModules = [
./configurations/homie
secrets.nixosModules.homie
];
};
# Cluster
thingone = mkNixOS {
extraModules = [
(import ./configurations/cluster "thingone")
secrets.nixosModules.thingy
];
};
thingtwo = mkNixOS {
extraModules = [
(import ./configurations/cluster "thingtwo")
secrets.nixosModules.thingy
];
};
live-image = mkNixOS {
mainUser = "nixos";
extraModules = [./configurations/live-image];
};
};
# For nix-fast-build. I use a custom output to alleviate eval time of this flake. ie. when doing nix flake show
nixFastChecks = import ./nixFastChecks {inherit perSystem self;};
homeManagerModules = import ./homeManagerModules {inherit self;};
nixosModules = import ./modules {inherit self;};
overlays = import ./overlays {inherit self;};
apps =
perSystem (pkgs:
import ./apps {inherit pkgs self;});
appsPackages = perSystem (pkgs: pkgs.appsPackages);
devShells =
perSystem (pkgs:
import ./devShells {inherit pkgs self;});
packages = perSystem (pkgs: pkgs.selfPackages);
scopedPackages = perSystem (pkgs: pkgs.scopedPackages);
formatter = perSystem (pkgs: pkgs.alejandra);
};
}

View file

@ -1,14 +0,0 @@
# Apps
This directory contains every derivations for apps exposed by this flake.
## List of my apps found in `self.apps`
| Name | Description |
| ---- | ----------- |
| `extract-subs` | Extract all `srt` subtitle files from a `mkv` video with the appropriate name. |
| `gen-docs` | Generates the READMEs in this repository from nix attributes. |
| `list2series` | Converts a Komga read list into a comics series for reading with mihon. |
| `mc-mods` | Checks if a list of mods have a version available for a specific Minecraft version and a specific loader. |
| `pin-inputs` | Takes a list of inputs to pin to their current rev in `flake.lock`. |
| `update-sources` | Updates all derivation sources in this repository and generates a commit message for the changes made. |

View file

@ -1,61 +0,0 @@
{
lib,
src,
npmDepsHash,
runtimeInputs,
buildNpmPackage,
meta,
makeWrapper,
nodejs_latest,
jq,
...
}: let
inherit (lib) boolToString concatMapStringsSep concatStringsSep mapAttrsToList;
inherit (builtins) fromJSON isBool readFile;
packageJSON = fromJSON (readFile "${src}/package.json");
tsconfig = fromJSON (readFile ./config/tsconfig.base.json);
pname = packageJSON.name;
inherit (packageJSON) version;
in
buildNpmPackage {
inherit pname version src runtimeInputs npmDepsHash;
prePatch = ''
# Patch tsconfig
mv ./tsconfig.json ./project.json
sed 's/^ *\/\/.*//' ${./config/tsconfig.base.json} > ./base.json
${jq}/bin/jq -sr '.[0] * .[1] | del(.extends)' ./project.json ./base.json > ./tsconfig.json
rm base.json project.json
'';
nativeBuildInputs = [makeWrapper];
postInstall = ''
wrapProgram $out/bin/${pname} \
--prefix PATH : ${concatMapStringsSep ":" (p: p + "/bin") runtimeInputs}
'';
doCheck = true;
checkPhase = ''
runHook preCheck
npx tsc ${concatStringsSep " " (mapAttrsToList (n: v:
if n == "lib"
then concatMapStringsSep " " (x: "--lib ${x}") v
else "--${n} ${
if isBool v
then boolToString v
else toString v
}")
tsconfig.compilerOptions)} src/app.ts
runHook postCheck
'';
nodejs = nodejs_latest;
meta = {mainProgram = pname;} // meta;
}

View file

@ -1,2 +0,0 @@
use flake $FLAKE#node
npm ci

View file

@ -1,451 +0,0 @@
import eslint from '@eslint/js';
import jsdoc from 'eslint-plugin-jsdoc';
import stylistic from '@stylistic/eslint-plugin';
import tseslint from 'typescript-eslint';
export default tseslint.config({
files: ['**/*.js', '**/*.ts'],
ignores: ['node_modules/**', 'types/**'],
extends: [
eslint.configs.recommended,
jsdoc.configs['flat/recommended-typescript'],
stylistic.configs['recommended-flat'],
...tseslint.configs.recommended,
...tseslint.configs.stylistic,
],
rules: {
// JSDoc settings
'jsdoc/tag-lines': ['warn', 'any', { startLines: 1 }],
'jsdoc/check-line-alignment': ['warn', 'always', {
tags: ['param', 'arg', 'argument', 'property', 'prop'],
}],
'jsdoc/no-types': 'off',
// Newer settings
'@typescript-eslint/no-extraneous-class': ['off'],
'@typescript-eslint/no-implied-eval': ['off'],
'class-methods-use-this': 'off',
'@stylistic/no-multiple-empty-lines': 'off',
// Pre-flat config
'@typescript-eslint/no-unused-vars': [
'error',
{
args: 'all',
argsIgnorePattern: '^_',
caughtErrors: 'all',
caughtErrorsIgnorePattern: '^_',
destructuredArrayIgnorePattern: '^_',
varsIgnorePattern: '^_',
ignoreRestSiblings: true,
},
],
'array-callback-return': [
'error',
{
allowImplicit: true,
checkForEach: true,
},
],
'no-constructor-return': [
'error',
],
'no-unreachable-loop': [
'error',
{
ignore: [
'ForInStatement',
'ForOfStatement',
],
},
],
'no-use-before-define': [
'error',
{
functions: false,
},
],
'block-scoped-var': [
'error',
],
'curly': [
'warn',
],
'default-case-last': [
'warn',
],
'default-param-last': [
'error',
],
'eqeqeq': [
'error',
'smart',
],
'func-names': [
'warn',
'never',
],
'func-style': [
'warn',
'expression',
],
'logical-assignment-operators': [
'warn',
'always',
],
'no-array-constructor': [
'error',
],
'no-empty-function': [
'warn',
],
'no-empty-static-block': [
'warn',
],
'no-extend-native': [
'error',
],
'no-extra-bind': [
'warn',
],
'no-implicit-coercion': [
'warn',
],
'no-iterator': [
'error',
],
'no-labels': [
'error',
],
'no-lone-blocks': [
'error',
],
'no-lonely-if': [
'error',
],
'no-loop-func': [
'error',
],
'no-magic-numbers': [
'error',
{
ignore: [
-1,
0.1,
0,
1,
2,
3,
4,
5,
10,
12,
33,
66,
100,
255,
360,
450,
500,
1000,
],
ignoreDefaultValues: true,
ignoreClassFieldInitialValues: true,
},
],
'no-multi-assign': [
'error',
],
'no-new-wrappers': [
'error',
],
'no-object-constructor': [
'error',
],
'no-proto': [
'error',
],
'no-return-assign': [
'error',
],
'no-sequences': [
'error',
],
'no-shadow': [
'error',
{
builtinGlobals: true,
allow: [
'Window',
],
},
],
'no-undef-init': [
'warn',
],
'no-undefined': [
'error',
],
'no-useless-constructor': [
'warn',
],
'no-useless-escape': [
'off',
],
'no-useless-return': [
'error',
],
'no-var': [
'error',
],
'no-void': [
'off',
],
'no-with': [
'error',
],
'object-shorthand': [
'error',
'always',
],
'one-var': [
'error',
'never',
],
'operator-assignment': [
'warn',
'always',
],
'prefer-arrow-callback': [
'error',
],
'prefer-const': [
'error',
],
'prefer-object-has-own': [
'error',
],
'prefer-regex-literals': [
'error',
],
'prefer-template': [
'warn',
],
'no-prototype-builtins': 'off',
'@typescript-eslint/no-var-requires': [
'off',
],
'@stylistic/array-bracket-newline': [
'warn',
'consistent',
],
'@stylistic/array-bracket-spacing': [
'warn',
'never',
],
'@stylistic/arrow-parens': [
'warn',
'always',
],
'@stylistic/brace-style': [
'warn',
'stroustrup',
],
'@stylistic/comma-dangle': [
'warn',
'always-multiline',
],
'@stylistic/comma-spacing': [
'warn',
{
before: false,
after: true,
},
],
'@stylistic/comma-style': [
'error',
'last',
],
'@stylistic/dot-location': [
'error',
'property',
],
'@stylistic/function-call-argument-newline': [
'warn',
'consistent',
],
'@stylistic/function-paren-newline': [
'warn',
'consistent',
],
'@stylistic/indent': [
'warn',
4,
{
SwitchCase: 1,
ignoreComments: true,
ignoredNodes: ['TemplateLiteral > *'],
},
],
'@stylistic/key-spacing': [
'warn',
{
beforeColon: false,
afterColon: true,
},
],
'@stylistic/keyword-spacing': [
'warn',
{
before: true,
},
],
'@stylistic/linebreak-style': [
'error',
'unix',
],
'@stylistic/lines-between-class-members': [
'warn',
'always',
{
exceptAfterSingleLine: true,
},
],
'@stylistic/max-len': [
'warn',
{
code: 105,
ignoreComments: true,
ignoreTrailingComments: true,
ignoreUrls: true,
},
],
'@stylistic/multiline-ternary': [
'warn',
'always-multiline',
],
'@stylistic/new-parens': [
'error',
],
'@stylistic/no-mixed-operators': [
'warn',
],
'@stylistic/no-mixed-spaces-and-tabs': [
'error',
],
'@stylistic/no-multi-spaces': [
'error',
],
'@stylistic/no-tabs': [
'error',
],
'@stylistic/no-trailing-spaces': [
'error',
],
'@stylistic/no-whitespace-before-property': [
'warn',
],
'@stylistic/nonblock-statement-body-position': [
'error',
'below',
],
'@stylistic/object-curly-newline': [
'warn',
{
consistent: true,
},
],
'@stylistic/object-curly-spacing': [
'warn',
'always',
],
'@stylistic/operator-linebreak': [
'warn',
'after',
],
'@stylistic/padded-blocks': [
'error',
'never',
],
'@stylistic/padding-line-between-statements': [
'warn',
{
blankLine: 'always',
prev: '*',
next: 'return',
},
{
blankLine: 'always',
prev: [
'const',
'let',
'var',
],
next: '*',
},
{
blankLine: 'any',
prev: [
'const',
'let',
'var',
],
next: [
'const',
'let',
'var',
],
},
{
blankLine: 'always',
prev: [
'case',
'default',
],
next: '*',
},
],
'@stylistic/quote-props': [
'error',
'consistent-as-needed',
],
'@stylistic/quotes': [
'error',
'single',
{
avoidEscape: true,
},
],
'@stylistic/semi': [
'error',
'always',
],
'@stylistic/semi-spacing': [
'warn',
],
'@stylistic/space-before-blocks': [
'warn',
],
'@stylistic/space-before-function-paren': [
'warn',
'never',
],
'@stylistic/space-infix-ops': [
'warn',
],
'@stylistic/spaced-comment': [
'warn',
'always',
],
'@stylistic/switch-colon-spacing': [
'warn',
],
'@stylistic/wrap-regex': [
'warn',
],
},
});

View file

@ -1,3 +0,0 @@
import eslintConf from './eslint.config';
export default eslintConf;

File diff suppressed because it is too large Load diff

View file

@ -1,16 +0,0 @@
{
"name": "eslint-conf",
"version": "0.0.0",
"type": "module",
"exports": "./index.ts",
"dependencies": {
"@eslint/js": "9.23.0",
"@stylistic/eslint-plugin": "4.2.0",
"eslint": "9.23.0",
"eslint-plugin-jsdoc": "50.6.9",
"jiti": "2.4.2",
"pkg-types": "2.1.0",
"typescript": "5.8.2",
"typescript-eslint": "8.28.0"
}
}

View file

@ -1,21 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ESNext",
"lib": [
"ESNext"
],
"module": "preserve",
"moduleResolution": "bundler",
"baseUrl": ".",
"noEmit": true,
"newLine": "LF",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"skipLibCheck": true,
"strict": true,
"noImplicitAny": false
}
}

View file

@ -1,10 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "./tsconfig.base.json",
"includes": [
"*.ts",
"**/*.ts",
"*.js",
"**/*.js"
]
}

View file

@ -1,9 +0,0 @@
{pkgs, ...}: let
inherit (pkgs.lib) getExe mapAttrs;
mkApp = pkg: {
program = getExe pkg;
type = "app";
};
in
mapAttrs (n: v: mkApp v) pkgs.appsPackages

View file

@ -1,3 +0,0 @@
use flake $FLAKE#subtitles-dev
(cd ../config; npm ci)
npm ci

View file

@ -1,17 +0,0 @@
{
buildApp,
ffmpeg-full,
...
}:
buildApp {
src = ./.;
npmDepsHash = "sha256-laq+WevDxgcuK/8r0alI186SCeKswPx9VRzrCjXRHXU=";
runtimeInputs = [
ffmpeg-full
];
meta.description = ''
Extract all `srt` subtitle files from a `mkv` video with the appropriate name.
'';
}

View file

@ -1,3 +0,0 @@
import eslintConf from 'eslint-conf';
export default eslintConf;

File diff suppressed because it is too large Load diff

View file

@ -1,21 +0,0 @@
{
"name": "extract-subs",
"version": "0.0.0",
"bin": "out/bin/app.cjs",
"type": "module",
"scripts": {
"build": "node_ver=$(node -v); esbuild src/app.ts --bundle --platform=node --target=\"node${node_ver:1:2}\" --outfile=out/bin/app.cjs"
},
"dependencies": {
"@types/fluent-ffmpeg": "2.1.27",
"fluent-ffmpeg": "2.1.3",
"@types/node": "22.13.14",
"esbuild": "0.25.1",
"eslint": "9.23.0",
"jiti": "2.4.2",
"typescript": "5.8.2"
},
"devDependencies": {
"eslint-conf": "file:../config"
}
}

View file

@ -1,157 +0,0 @@
import { spawnSync as spawn } from 'child_process';
import ffprobe from './ffprobe';
import { ISO6393To1 } from './lang-codes';
/* Types */
import { FfprobeStream } from 'fluent-ffmpeg';
const SPAWN_OPTS = {
stdio: [process.stdin, process.stdout, process.stderr],
};
/**
* These are the cli arguments
*
* @param videoPath the directory in which we want to sync the subtitles
* @param languages a comma-separated list of languages (3 letters) to sync the subtitles
*/
const video = process.argv[2];
const languages = process.argv[3]?.split(',');
// Global Vars
const subIndexes: number[] = [];
let videoPath: string;
let baseName: string;
/**
* Gets the relative path to the subtitle file of a ffmpeg stream.
*
* @param sub the stream of the subtitles to extract
* @returns the path of the subtitle file
*/
const getSubPath = (sub: FfprobeStream): string => {
const language = ISO6393To1.get(sub.tags.language);
const forced = sub.disposition?.forced === 0 ?
'' :
'.forced';
const hearingImpaired = sub.disposition?.hearing_impaired === 0 ?
'' :
'.sdh';
return `${baseName}${forced}.${language}${hearingImpaired}.srt`;
};
/**
* Removes all subtitles streams from the video file.
*/
const removeContainerSubs = (): void => {
spawn('mv', [
videoPath,
`${videoPath}.bak`,
], SPAWN_OPTS);
spawn('ffmpeg', [
'-i', `${videoPath}.bak`,
'-map', '0',
...subIndexes.map((i) => ['-map', `-0:${i}`]).flat(),
'-c', 'copy', videoPath,
], SPAWN_OPTS);
spawn('rm', [
`${videoPath}.bak`,
], SPAWN_OPTS);
};
/**
* Extracts a sub of a video file to a subtitle file.
*
* @param sub the stream of the subtitles to extract
*/
const extractSub = (sub: FfprobeStream): void => {
const subFile = getSubPath(sub);
spawn('ffmpeg', [
'-i', videoPath,
'-map', `0:${sub.index}`, subFile,
], SPAWN_OPTS);
subIndexes.push(sub.index);
};
/**
* Sorts the list of streams to only keep subtitles
* that can be extracted.
*
* @param lang the language of the subtitles
* @param streams the streams
* @returns the streams that represent subtitles
*/
const findSubs = (
lang: string,
streams: FfprobeStream[],
): FfprobeStream[] => {
const subs = streams.filter((s) => s.tags?.language &&
s.tags.language === lang &&
s.codec_type === 'subtitle');
const pgs = subs.filter((s) => s.codec_name === 'hdmv_pgs_subtitle');
// If we only have PGS subs, warn user
if (pgs.length === subs.length) {
console.warn(`No SRT subtitle tracks were found for ${lang}`);
}
// Remove PGS streams from subs
return subs.filter((s) => s.codec_name !== 'hdmv_pgs_subtitle');
};
/**
* Where the magic happens.
*/
const main = async(): Promise<void> => {
// Get rid of video extension
baseName = videoPath.split('/').at(-1)!.replace(/\.[^.]*$/, '');
// ffprobe the video file to see available sub tracks
const data = await ffprobe(videoPath);
if (!data?.streams) {
return;
}
// Check for languages wanted
languages.forEach((lang) => {
const subs = findSubs(lang, data.streams);
if (subs.length === 0) {
console.warn(`No subtitle tracks were found for ${lang}`);
return;
}
// Extract all subs
subs.forEach((sub) => {
extractSub(sub);
});
});
removeContainerSubs();
};
// Check if there are 2 params
if (video && languages) {
videoPath = video;
main();
}
else {
console.error('Error: no argument passed');
process.exit(1);
}

View file

@ -1,8 +0,0 @@
import Ffmpeg from 'fluent-ffmpeg';
export default (videoPath: string) => new Promise<Ffmpeg.FfprobeData>((resolve) => {
Ffmpeg.ffprobe(videoPath, (_e, data) => {
resolve(data);
});
});

View file

@ -1,373 +0,0 @@
export const ISO6391To3 = new Map([
['aa', 'aar'],
['ab', 'abk'],
['af', 'afr'],
['ak', 'aka'],
['am', 'amh'],
['ar', 'ara'],
['an', 'arg'],
['as', 'asm'],
['av', 'ava'],
['ae', 'ave'],
['ay', 'aym'],
['az', 'aze'],
['ba', 'bak'],
['bm', 'bam'],
['be', 'bel'],
['bn', 'ben'],
['bi', 'bis'],
['bo', 'bod'],
['bs', 'bos'],
['br', 'bre'],
['bg', 'bul'],
['ca', 'cat'],
['cs', 'ces'],
['ch', 'cha'],
['ce', 'che'],
['cu', 'chu'],
['cv', 'chv'],
['kw', 'cor'],
['co', 'cos'],
['cr', 'cre'],
['cy', 'cym'],
['da', 'dan'],
['de', 'deu'],
['dv', 'div'],
['dz', 'dzo'],
['el', 'ell'],
['en', 'eng'],
['eo', 'epo'],
['et', 'est'],
['eu', 'eus'],
['ee', 'ewe'],
['fo', 'fao'],
['fa', 'fas'],
['fj', 'fij'],
['fi', 'fin'],
['fr', 'fre'],
['fy', 'fry'],
['ff', 'ful'],
['gd', 'gla'],
['ga', 'gle'],
['gl', 'glg'],
['gv', 'glv'],
['gn', 'grn'],
['gu', 'guj'],
['ht', 'hat'],
['ha', 'hau'],
['sh', 'hbs'],
['he', 'heb'],
['hz', 'her'],
['hi', 'hin'],
['ho', 'hmo'],
['hr', 'hrv'],
['hu', 'hun'],
['hy', 'hye'],
['ig', 'ibo'],
['io', 'ido'],
['ii', 'iii'],
['iu', 'iku'],
['ie', 'ile'],
['ia', 'ina'],
['id', 'ind'],
['ik', 'ipk'],
['is', 'isl'],
['it', 'ita'],
['jv', 'jav'],
['ja', 'jpn'],
['kl', 'kal'],
['kn', 'kan'],
['ks', 'kas'],
['ka', 'kat'],
['kr', 'kau'],
['kk', 'kaz'],
['km', 'khm'],
['ki', 'kik'],
['rw', 'kin'],
['ky', 'kir'],
['kv', 'kom'],
['kg', 'kon'],
['ko', 'kor'],
['kj', 'kua'],
['ku', 'kur'],
['lo', 'lao'],
['la', 'lat'],
['lv', 'lav'],
['li', 'lim'],
['ln', 'lin'],
['lt', 'lit'],
['lb', 'ltz'],
['lu', 'lub'],
['lg', 'lug'],
['mh', 'mah'],
['ml', 'mal'],
['mr', 'mar'],
['mk', 'mkd'],
['mg', 'mlg'],
['mt', 'mlt'],
['mn', 'mon'],
['mi', 'mri'],
['ms', 'msa'],
['my', 'mya'],
['na', 'nau'],
['nv', 'nav'],
['nr', 'nbl'],
['nd', 'nde'],
['ng', 'ndo'],
['ne', 'nep'],
['nl', 'nld'],
['nn', 'nno'],
['nb', 'nob'],
['no', 'nor'],
['ny', 'nya'],
['oc', 'oci'],
['oj', 'oji'],
['or', 'ori'],
['om', 'orm'],
['os', 'oss'],
['pa', 'pan'],
['pi', 'pli'],
['pl', 'pol'],
['pt', 'por'],
['ps', 'pus'],
['qu', 'que'],
['rm', 'roh'],
['ro', 'ron'],
['rn', 'run'],
['ru', 'rus'],
['sg', 'sag'],
['sa', 'san'],
['si', 'sin'],
['sk', 'slk'],
['sl', 'slv'],
['se', 'sme'],
['sm', 'smo'],
['sn', 'sna'],
['sd', 'snd'],
['so', 'som'],
['st', 'sot'],
['es', 'spa'],
['sq', 'sqi'],
['sc', 'srd'],
['sr', 'srp'],
['ss', 'ssw'],
['su', 'sun'],
['sw', 'swa'],
['sv', 'swe'],
['ty', 'tah'],
['ta', 'tam'],
['tt', 'tat'],
['te', 'tel'],
['tg', 'tgk'],
['tl', 'tgl'],
['th', 'tha'],
['ti', 'tir'],
['to', 'ton'],
['tn', 'tsn'],
['ts', 'tso'],
['tk', 'tuk'],
['tr', 'tur'],
['tw', 'twi'],
['ug', 'uig'],
['uk', 'ukr'],
['ur', 'urd'],
['uz', 'uzb'],
['ve', 'ven'],
['vi', 'vie'],
['vo', 'vol'],
['wa', 'wln'],
['wo', 'wol'],
['xh', 'xho'],
['yi', 'yid'],
['yo', 'yor'],
['za', 'zha'],
['zh', 'zho'],
['zu', 'zul'],
]);
export const ISO6393To1 = new Map([
['aar', 'aa'],
['abk', 'ab'],
['afr', 'af'],
['aka', 'ak'],
['amh', 'am'],
['ara', 'ar'],
['arg', 'an'],
['asm', 'as'],
['ava', 'av'],
['ave', 'ae'],
['aym', 'ay'],
['aze', 'az'],
['bak', 'ba'],
['bam', 'bm'],
['bel', 'be'],
['ben', 'bn'],
['bis', 'bi'],
['bod', 'bo'],
['bos', 'bs'],
['bre', 'br'],
['bul', 'bg'],
['cat', 'ca'],
['ces', 'cs'],
['cha', 'ch'],
['che', 'ce'],
['chu', 'cu'],
['chv', 'cv'],
['cor', 'kw'],
['cos', 'co'],
['cre', 'cr'],
['cym', 'cy'],
['dan', 'da'],
['deu', 'de'],
['div', 'dv'],
['dzo', 'dz'],
['ell', 'el'],
['eng', 'en'],
['epo', 'eo'],
['est', 'et'],
['eus', 'eu'],
['ewe', 'ee'],
['fao', 'fo'],
['fas', 'fa'],
['fij', 'fj'],
['fin', 'fi'],
['fre', 'fr'],
['fry', 'fy'],
['ful', 'ff'],
['gla', 'gd'],
['gle', 'ga'],
['glg', 'gl'],
['glv', 'gv'],
['grn', 'gn'],
['guj', 'gu'],
['hat', 'ht'],
['hau', 'ha'],
['hbs', 'sh'],
['heb', 'he'],
['her', 'hz'],
['hin', 'hi'],
['hmo', 'ho'],
['hrv', 'hr'],
['hun', 'hu'],
['hye', 'hy'],
['ibo', 'ig'],
['ido', 'io'],
['iii', 'ii'],
['iku', 'iu'],
['ile', 'ie'],
['ina', 'ia'],
['ind', 'id'],
['ipk', 'ik'],
['isl', 'is'],
['ita', 'it'],
['jav', 'jv'],
['jpn', 'ja'],
['kal', 'kl'],
['kan', 'kn'],
['kas', 'ks'],
['kat', 'ka'],
['kau', 'kr'],
['kaz', 'kk'],
['khm', 'km'],
['kik', 'ki'],
['kin', 'rw'],
['kir', 'ky'],
['kom', 'kv'],
['kon', 'kg'],
['kor', 'ko'],
['kua', 'kj'],
['kur', 'ku'],
['lao', 'lo'],
['lat', 'la'],
['lav', 'lv'],
['lim', 'li'],
['lin', 'ln'],
['lit', 'lt'],
['ltz', 'lb'],
['lub', 'lu'],
['lug', 'lg'],
['mah', 'mh'],
['mal', 'ml'],
['mar', 'mr'],
['mkd', 'mk'],
['mlg', 'mg'],
['mlt', 'mt'],
['mon', 'mn'],
['mri', 'mi'],
['msa', 'ms'],
['mya', 'my'],
['nau', 'na'],
['nav', 'nv'],
['nbl', 'nr'],
['nde', 'nd'],
['ndo', 'ng'],
['nep', 'ne'],
['nld', 'nl'],
['nno', 'nn'],
['nob', 'nb'],
['nor', 'no'],
['nya', 'ny'],
['oci', 'oc'],
['oji', 'oj'],
['ori', 'or'],
['orm', 'om'],
['oss', 'os'],
['pan', 'pa'],
['pli', 'pi'],
['pol', 'pl'],
['por', 'pt'],
['pus', 'ps'],
['que', 'qu'],
['roh', 'rm'],
['ron', 'ro'],
['run', 'rn'],
['rus', 'ru'],
['sag', 'sg'],
['san', 'sa'],
['sin', 'si'],
['slk', 'sk'],
['slv', 'sl'],
['sme', 'se'],
['smo', 'sm'],
['sna', 'sn'],
['snd', 'sd'],
['som', 'so'],
['sot', 'st'],
['spa', 'es'],
['sqi', 'sq'],
['srd', 'sc'],
['srp', 'sr'],
['ssw', 'ss'],
['sun', 'su'],
['swa', 'sw'],
['swe', 'sv'],
['tah', 'ty'],
['tam', 'ta'],
['tat', 'tt'],
['tel', 'te'],
['tgk', 'tg'],
['tgl', 'tl'],
['tha', 'th'],
['tir', 'ti'],
['ton', 'to'],
['tsn', 'tn'],
['tso', 'ts'],
['tuk', 'tk'],
['tur', 'tr'],
['twi', 'tw'],
['uig', 'ug'],
['ukr', 'uk'],
['urd', 'ur'],
['uzb', 'uz'],
['ven', 've'],
['vie', 'vi'],
['vol', 'vo'],
['wln', 'wa'],
['wol', 'wo'],
['xho', 'xh'],
['yid', 'yi'],
['yor', 'yo'],
['zha', 'za'],
['zho', 'zh'],
['zul', 'zu'],
]);

View file

@ -1,10 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "../config/tsconfig.base.json",
"includes": [
"*.ts",
"**/*.ts",
"*.js",
"**/*.js"
]
}

View file

@ -1,15 +0,0 @@
{
writeShellApplication,
jq,
pandoc,
...
}:
writeShellApplication {
name = "gen-docs";
runtimeInputs = [jq pandoc];
text = builtins.readFile ./script.sh;
meta.description = ''
Generates the READMEs in this repository from nix attributes.
'';
}

View file

@ -1,13 +0,0 @@
attr: selfPath: let
inherit (builtins) mapAttrs replaceStrings;
modules = import "${selfPath}/${attr}" {description = true;};
trimNewlines = s: replaceStrings ["\n"] [" "] s;
in {
attrs =
mapAttrs (_: v: {
desc = trimNewlines v;
})
modules;
}

View file

@ -1,12 +0,0 @@
configs: let
inherit (builtins) mapAttrs replaceStrings;
trimNewlines = s: replaceStrings ["\n"] [" "] s;
in {
attrs =
mapAttrs (_: v: {
roleDesc = trimNewlines (v.config.meta.roleDescription or "");
hwDesc = trimNewlines (v.config.meta.hardwareDescription or "");
})
configs;
}

View file

@ -1,13 +0,0 @@
x: let
inherit (builtins) currentSystem mapAttrs removeAttrs replaceStrings;
trimNewlines = s: replaceStrings ["\n"] [" "] s;
packages = removeAttrs x.${currentSystem} ["default"];
in {
attrs =
mapAttrs (_: v: {
desc = trimNewlines (v.meta.description or "");
homepage = v.meta.homepage or "";
})
packages;
}

View file

@ -1,31 +0,0 @@
attr: selfPath: let
inherit (builtins) currentSystem getFlake mapAttrs removeAttrs replaceStrings;
self = getFlake selfPath;
scopes = ((import "${selfPath}/${attr}" {description = true;}) {} {}).scopedPackages;
trimNewlines = s: replaceStrings ["\n"] [" "] s;
in {
attrs =
mapAttrs (n: v: {
desc = trimNewlines v;
packages = let
scopePkgs = removeAttrs self.${attr}.${currentSystem}.${n} [
"buildFirefoxXpiAddon"
"callPackage"
"newScope"
"override"
"overrideDerivation"
"overrideScope"
"packages"
"recurseForDerivations"
];
in
mapAttrs (_: pkg: {
desc = trimNewlines (pkg.meta.description or "");
homepage = pkg.meta.homepage or "";
})
scopePkgs;
})
scopes;
}

View file

@ -1,43 +0,0 @@
substituteDerivs() {
echo '' | pandoc --metadata-file <(
nix eval \
--impure \
--json \
"$FLAKE"#"$1" \
--apply "import \"$FLAKE/apps/gen-docs/$3.nix\"" |
jq -r
) -t markdown --template "$FLAKE/apps/gen-docs/templates/$2.md" -o "$FLAKE/$2/README.md"
}
substituteAttrs() {
echo '' | pandoc --metadata-file <(
nix eval \
--impure \
--json \
--expr "\"$FLAKE\"" \
--apply "(import \"$FLAKE/apps/gen-docs/getAttrsMeta.nix\") \"$1\"" |
jq -r
) -t markdown --template "$FLAKE/apps/gen-docs/templates/$1.md" -o "$FLAKE/$1/README.md"
}
substituteScopes() {
echo '' | pandoc --metadata-file <(
nix eval \
--impure \
--json \
--expr "\"$FLAKE\"" \
--apply "(import \"$FLAKE/apps/gen-docs/getScopesMeta.nix\") \"$1\"" |
jq -r
) -t markdown --template "$FLAKE/apps/gen-docs/templates/$1.md" -o "$FLAKE/$1/README.md"
}
substituteDerivs "appsPackages" "apps" "getPackageMeta"
substituteDerivs "nixosConfigurations" "configurations" "getConfigMeta"
substituteDerivs "devShells" "devShells" "getPackageMeta"
substituteDerivs "packages" "packages" "getPackageMeta"
substituteAttrs "modules"
substituteAttrs "homeManagerModules"
substituteAttrs "overlays"
substituteScopes "scopedPackages"

View file

@ -1,11 +0,0 @@
# Apps
This directory contains every derivations for apps exposed by this flake.
## List of my apps found in `self.apps`
| Name | Description |
| ---- | ----------- |
$for(attrs/pairs)$
| `$it.key$` | $it.value.desc/nowrap$ |
$endfor$

View file

@ -1,13 +0,0 @@
# NixosConfigurations
This directory contains every device's main configuration file, their `hardware-configuration.nix` and some custom modules
unique to them.
## List of my devices
| Name | Model / Specs | Description |
| --------- | ------------- | ------------------------------------------------------------------------------------------------ |
| `android` | OnePlus 9 Pro | [Nix-On-Droid](https://github.com/nix-community/nix-on-droid) configuration for my OnePlus 9 Pro |
$for(attrs/pairs)$
| `$it.key$` | $it.value.hwDesc/nowrap$ | $it.value.roleDesc/nowrap$ |
$endfor$

View file

@ -1,11 +0,0 @@
# DevShells
This directory contains every derivations for devShells exposed by this flake.
## List of my devShells found in `self.devShells`
| Name | Description |
| ---- | ----------- |
$for(attrs/pairs)$
| `$it.key$` | $it.value.desc/nowrap$ |
$endfor$

View file

@ -1,11 +0,0 @@
# HomeManagerModules
This directory contains every home-manager modules exposed by this flake.
## List of my home-manager modules found in `self.homeManagerModules`
| Name | Description |
| ---- | ----------- |
$for(attrs/pairs)$
| `$it.key$` | $it.value.desc/nowrap$ |
$endfor$

View file

@ -1,11 +0,0 @@
# NixosModules
This directory contains every modules for NixOS exposed by this flake.
## List of my modules found in `self.nixosModules`
| Name | Description |
| ---- | ----------- |
$for(attrs/pairs)$
| `$it.key$` | $it.value.desc/nowrap$ |
$endfor$

View file

@ -1,11 +0,0 @@
# Overlays
This directory contains every overlay exposed by this flake.
## List of my overlays found in `self.overlays`
| Name | Description |
| ---- | ----------- |
$for(attrs/pairs)$
| `$it.key$` | $it.value.desc/nowrap$ |
$endfor$

View file

@ -1,11 +0,0 @@
# Packages
This directory contains every derivations for packages exposed by this flake.
## List of my packages found in `self.packages`
| Name | Description | Homepage |
| ---- | ----------- | -------- |
$for(attrs/pairs)$
| `$it.key$` | $it.value.desc/nowrap$ | $it.value.homepage/nowrap$ |
$endfor$

View file

@ -1,18 +0,0 @@
# ScopedPackages
This directory contains every package scopes exposed by this flake.
## List of my package scopes found in `self.scopedPackages`
$for(attrs/pairs)$
### $it.key$
$it.value.desc/nowrap$
| Name | Description | Homepage |
| ---- | ----------- | -------- |
$for(it.value.packages/pairs)$
| `$it.key$` | $it.value.desc/nowrap$ | $it.value.homepage/nowrap$ |
$endfor$
$endfor$

View file

@ -1,3 +0,0 @@
use flake $FLAKE#node
(cd ../config; npm ci)
npm ci

View file

@ -1 +0,0 @@
.env

View file

@ -1,11 +0,0 @@
{buildApp, ...}:
buildApp {
src = ./.;
npmDepsHash = "sha256-A5xtspKUPJaVtJYKNnGNXg1A+u0rg7J7HbDu+dE6sHs=";
runtimeInputs = [];
meta.description = ''
Converts a Komga read list into a comics series for reading with mihon.
'';
}

View file

@ -1,3 +0,0 @@
import eslintConf from 'eslint-conf';
export default eslintConf;

File diff suppressed because it is too large Load diff

View file

@ -1,21 +0,0 @@
{
"name": "list2series",
"version": "0.0.0",
"bin": "out/bin/app.cjs",
"type": "module",
"scripts": {
"build": "node_ver=$(node -v); esbuild src/app.ts --bundle --platform=node --target=\"node${node_ver:1:2}\" --outfile=out/bin/app.cjs"
},
"dependencies": {
"@types/node": "22.13.14",
"axios": "1.8.4",
"esbuild": "0.25.1",
"eslint": "9.23.0",
"jiti": "2.4.2",
"pkg-types": "2.1.0",
"typescript": "5.8.2"
},
"devDependencies": {
"eslint-conf": "file:../config"
}
}

View file

@ -1,214 +0,0 @@
import axios from 'axios';
import { linkSync, mkdirSync, readFileSync, rmSync } from 'fs';
import { basename } from 'path';
import { type Book, type ReadList, type Series } from './types';
// Examples of calling this script:
// $ just l2s copy 0K65Q482KK7SD
// $ just l2s meta 0K65Q482KK7SD
const API = JSON.parse(
readFileSync(`${process.env.FLAKE}/apps/list2series/.env`, { encoding: 'utf-8' }),
).API;
const LIST_ID = process.argv[3];
const getListInfo = async(): Promise<ReadList> => {
const res = await axios.request({
method: 'get',
maxBodyLength: Infinity,
url: `https://komga.nelim.org/api/v1/readlists/${LIST_ID}`,
headers: {
'Accept': 'application/json',
'X-API-Key': API,
},
});
return res.data;
};
const getSeries = async(seriesTitle: string, operator = true): Promise<Series[]> => {
return (await axios.request({
method: 'post',
maxBodyLength: Infinity,
url: 'https://komga.nelim.org/api/v1/series/list?unpaged=true',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-API-Key': API,
},
data: JSON.stringify({
condition: {
title: {
operator: operator ? 'is' : 'isNot',
value: seriesTitle,
},
},
}),
})).data.content;
};
const getSeriesBooks = async(listName: string, seriesPath: string): Promise<Book[]> => {
const thisSeries = (await getSeries('', false)).find((s) => s.url === seriesPath);
if (!thisSeries) {
throw new Error('Series could not be found');
}
// Reset Series metadata
axios.request({
method: 'patch',
maxBodyLength: Infinity,
url: `https://komga.nelim.org/api/v1/series/${thisSeries.id}/metadata`,
headers: {
'Content-Type': 'application/json',
'X-API-Key': API,
},
data: JSON.stringify({
ageRating: null,
ageRatingLock: true,
alternateTitles: null,
alternateTitlesLock: true,
genres: null,
genresLock: true,
language: null,
languageLock: true,
links: null,
linksLock: true,
publisherLock: true,
readingDirection: 'LEFT_TO_RIGHT',
readingDirectionLock: true,
sharingLabels: null,
sharingLabelsLock: true,
status: null,
statusLock: true,
summary: null,
summaryLock: true,
tags: null,
tagsLock: true,
title: listName,
titleLock: true,
titleSort: listName,
titleSortLock: true,
totalBookCountLock: true,
}),
});
const books = await axios.request({
method: 'post',
maxBodyLength: Infinity,
url: 'https://komga.nelim.org/api/v1/books/list?unpaged=true',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-API-Key': API,
},
data: JSON.stringify({
condition: {
seriesId: {
operator: 'is',
value: thisSeries.id,
},
},
}),
});
return books.data.content;
};
const getBookInfo = async(id: string): Promise<Book> => {
const res = await axios.request({
method: 'get',
maxBodyLength: Infinity,
url: `https://komga.nelim.org/api/v1/books/${id}`,
headers: {
'Accept': 'application/json',
'X-API-Key': API,
},
});
return res.data;
};
// There doesn't seem to be a way to wait for the scan to be done
const scanLibrary = (): void => {
axios.request({
method: 'post',
maxBodyLength: Infinity,
url: 'https://komga.nelim.org/api/v1/libraries/0K4QG58XA29DZ/scan',
headers: {
'X-API-Key': API,
},
});
};
const setBookMetadata = async(i: number, source: Book, target: Book): Promise<void> => {
const thisSeries = (await getSeries(source.seriesTitle))[0];
source.metadata.title = thisSeries.booksCount !== 1 ?
`${source.seriesTitle} Issue #${source.metadata.number}` :
source.metadata.title = source.seriesTitle;
source.metadata.number = i.toString();
source.metadata.numberSort = i;
const metadata = JSON.stringify(source.metadata);
axios.request({
method: 'patch',
maxBodyLength: Infinity,
url: `https://komga.nelim.org/api/v1/books/${target.id}/metadata`,
headers: {
'Content-Type': 'application/json',
'X-API-Key': API,
},
data: metadata,
});
};
const main = async(): Promise<void> => {
const list = await getListInfo();
const ids = list.bookIds;
const seriesPath = `/data/comics/[List] ${list.name}`;
const listBooks = [] as Book[];
for (let i = 0; i < ids.length; i++) {
const book = await getBookInfo(ids[i]);
listBooks[i] = book;
};
if (process.argv[2] === 'copy') {
rmSync(seriesPath, { recursive: true, force: true });
mkdirSync(seriesPath, { recursive: true });
for (const book of listBooks) {
const bookPath = book.url;
const inListPath = `${seriesPath}/${basename(bookPath)}`;
console.log(`hardlinking ${basename(bookPath)}`);
linkSync(bookPath, inListPath);
}
scanLibrary();
}
else if (process.argv[2] === 'meta') {
const seriesBooks = await getSeriesBooks(`[List] ${list.name}`, seriesPath);
for (const target of seriesBooks) {
const source = listBooks.find((b) => basename(b.url) === basename(target.url));
if (source) {
const i = listBooks.indexOf(source) + 1;
console.log(`Setting metadata for ${source.name}`);
setBookMetadata(i, source, target);
}
}
}
};
main();

View file

@ -1,135 +0,0 @@
export interface Media {
status: string
mediaType: string
pagesCount: number
comment: string
epubDivinaCompatible: boolean
epubIsKepub: boolean
mediaProfile: string
}
export interface Author {
name: string
role: string
}
export interface Link {
label: string
url: string
}
export interface ShortBookMetadata {
authors: Author[]
tags: unknown[]
releaseDate: string
summary: string
summaryNumber: string
created: string
lastModified: string
}
export interface BookMetadata {
title: string
titleLock: boolean
summary: string
summaryLock: boolean
number: string
numberLock: boolean
numberSort: number
numberSortLock: boolean
releaseDate: string
releaseDateLock: boolean
authors: Author[]
authorsLock: boolean
tags: unknown[]
tagsLock: boolean
isbn: string
isbnLock: boolean
links: Link[]
linksLock: boolean
created: string
lastModified: string
}
export interface Book {
id: string
seriesId: string
seriesTitle: string
libraryId: string
name: string
url: string
number: number
created: string
lastModified: string
fileLastModified: string
sizeBytes: number
size: string
media: Media
metadata: BookMetadata
readProgress: null | unknown
deleted: boolean
fileHash: string
oneshot: boolean
}
export interface SeriesMetadata {
status: string
statusLock: boolean
title: string
titleLock: boolean
titleSort: string
titleSortLock: boolean
summary: string
summaryLock: boolean
readingDirection: string
readingDirectionLock: boolean
publisher: string
publisherLock: boolean
ageRating: null | string
ageRatingLock: boolean
language: string
languageLock: boolean
genres: string[]
genresLock: boolean
tags: unknown[]
tagsLock: boolean
totalBookCount: null | number
totalBookCountLock: boolean
sharingLabels: unknown[]
sharingLabelsLock: boolean
links: Link[]
linksLock: boolean
alternateTitles: string[]
alternateTitlesLock: boolean
created: string
lastModified: string
}
export interface Series {
id: string
libraryId: string
name: string
url: string
created: string
lastModified: string
fileLastModified: string
booksCount: number
booksReadCount: number
booksUnreadCount: number
booksInProgressCount: number
metadata: SeriesMetadata
booksMetadata: ShortBookMetadata
deleted: boolean
oneshot: boolean
}
export interface ReadList {
id: string
name: string
summary: string
ordered: boolean
bookIds: string[]
createdDate: string
lastModifiedDate: string
filtered: boolean
}

View file

@ -1,10 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "../config/tsconfig.base.json",
"includes": [
"*.ts",
"**/*.ts",
"*.js",
"**/*.js"
]
}

View file

@ -1,3 +0,0 @@
use flake $FLAKE#node
(cd ../config; npm ci)
npm ci

View file

@ -1,18 +0,0 @@
{
buildApp,
nodejs_latest,
...
}:
buildApp {
src = ./.;
npmDepsHash = "sha256-+0jcz/lWziDQDnmeQGX9IuPKywHNi6Cd1Y/vwweWsdU=";
runtimeInputs = [
nodejs_latest
];
meta.description = ''
Checks if a list of mods have a version available for a specific Minecraft
version and a specific loader.
'';
}

View file

@ -1,3 +0,0 @@
import eslintConf from 'eslint-conf';
export default eslintConf;

File diff suppressed because it is too large Load diff

View file

@ -1,20 +0,0 @@
{
"name": "mc-mods",
"version": "0.0.0",
"bin": "out/bin/app.cjs",
"type": "module",
"scripts": {
"build": "node_ver=$(node -v); esbuild src/app.ts --bundle --platform=node --target=\"node${node_ver:1:2}\" --outfile=out/bin/app.cjs"
},
"dependencies": {
"@types/node": "22.13.14",
"esbuild": "0.25.1",
"eslint": "9.23.0",
"jiti": "2.4.2",
"pkg-types": "2.1.0",
"typescript": "5.8.2"
},
"devDependencies": {
"eslint-conf": "file:../config"
}
}

View file

@ -1,147 +0,0 @@
import { createWriteStream, mkdirSync, rmSync } from 'fs';
import { Readable } from 'stream';
interface Hashes {
sha1: string
sha512: string
}
interface File {
hashes: Hashes
url: string
filename: string
primary: boolean
size: number
file_type: string
}
interface Dependency {
version_id: string
project_id: string
file_name: string
dependency_type: string
}
interface ModVersion {
game_versions: string[]
loaders: string[]
id: string
project_id: string
author_id: string
featured: boolean
name: string
version_number: string
changelog: string
changelog_url: string
date_published: string
downloads: number
version_type: string
status: string
files: File[]
dependencies: Dependency[]
}
const LOADER = 'fabric';
const game_version = process.argv[2] ?? '';
const action = process.argv[3] ?? 'check';
const getVersions = async(slug: string): Promise<ModVersion[]> => {
const res = await fetch(`https://api.modrinth.com/v2/project/${slug}/version`);
return res.ok ?
await res.json() as ModVersion[] :
[];
};
const checkModCompat = async(slug: string): Promise<ModVersion | null> => {
const versions = await getVersions(slug);
const matching = versions.filter((ver) =>
ver.game_versions.includes(game_version) &&
ver.loaders.includes(LOADER));
if (matching.length === 0) {
return null;
}
const timeSorted = matching.sort((a, b) => {
const dateA = new Date(a.date_published);
const dateB = new Date(b.date_published);
return dateB.getTime() - dateA.getTime();
});
return timeSorted.some((v) => v.version_type === 'release') ?
timeSorted.filter((v) => v.version_type === 'release')[0] :
timeSorted[0];
};
const getDownloadUrls = (version: ModVersion): string[] => version.files.map((file) => file.url);
const showModDownloadUrls = async(modName: string): Promise<boolean> => {
const ver = await checkModCompat(modName);
if (ver) {
const urls = getDownloadUrls(ver);
console.log(`\n${modName}:`, urls);
return true;
}
else {
return false;
}
};
const download = async(url: string, path: string) => Readable
.fromWeb((await fetch(url)).body!).pipe(createWriteStream(path));
const main = () => {
const mods = [
'badpackets',
'c2me-fabric',
'cloth-config',
'clumps',
'fabric-api',
'ferrite-core',
'leaves-be-gone',
'lithium',
'modernfix',
'no-chat-reports',
'noisium',
'vmp-fabric',
'wthit',
];
if (action === 'check') {
mods.forEach(async(modName) => {
if (!(await showModDownloadUrls(modName))) {
console.error(`No matching releases of ${modName} were found for ${game_version}`);
}
});
}
else if (action === 'download') {
rmSync('./out', { force: true, recursive: true });
mkdirSync('./out');
mods.forEach(async(modName) => {
const ver = await checkModCompat(modName);
if (ver === null) {
console.error(`No matching releases of ${modName} were found for ${game_version}`);
return;
}
const fileName =
`${modName}-${ver.version_number}.jar`;
console.log(`Downloading ${fileName}`);
await download(
ver.files[0].url,
`./out/${fileName}`,
);
});
}
};
main();

View file

@ -1,10 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "../config/tsconfig.base.json",
"includes": [
"*.ts",
"**/*.ts",
"*.js",
"**/*.js"
]
}

View file

@ -1,16 +0,0 @@
{inputs, ...}: (final: prev: {
appsPackages = let
inherit (final.lib) listToAttrs nameValuePair;
buildApp = attrs: (final.callPackage ./buildApp.nix ({} // inputs // attrs));
callPackage = file: final.callPackage file ({inherit buildApp;} // inputs);
in
listToAttrs (map (x: nameValuePair x (callPackage ./${x})) [
"extract-subs"
"gen-docs"
"list2series"
"mc-mods"
"pin-inputs"
"update-sources"
]);
})

View file

@ -1,3 +0,0 @@
use flake $FLAKE#node
(cd ../config; npm ci)
npm ci

View file

@ -1,11 +0,0 @@
{buildApp, ...}:
buildApp {
src = ./.;
npmDepsHash = "sha256-O5rNGEtBoH4itUjUG9dX3EPAEGYkQxPz1FePohLLKuA=";
runtimeInputs = [];
meta.description = ''
Takes a list of inputs to pin to their current rev in `flake.lock`.
'';
}

View file

@ -1,3 +0,0 @@
import eslintConf from 'eslint-conf';
export default eslintConf;

File diff suppressed because it is too large Load diff

View file

@ -1,20 +0,0 @@
{
"name": "pin-inputs",
"version": "0.0.0",
"bin": "out/bin/app.cjs",
"type": "module",
"scripts": {
"build": "node_ver=$(node -v); esbuild src/app.ts --bundle --platform=node --target=\"node${node_ver:1:2}\" --outfile=out/bin/app.cjs"
},
"dependencies": {
"@types/node": "22.13.14",
"esbuild": "0.25.1",
"eslint": "9.23.0",
"jiti": "2.4.2",
"pkg-types": "2.1.0",
"typescript": "5.8.2"
},
"devDependencies": {
"eslint-conf": "file:../config"
}
}

View file

@ -1,62 +0,0 @@
import { readFileSync, writeFileSync } from 'fs';
export const replaceInFile = (replace: RegExp, replacement: string, file: string) => {
const fileContents = readFileSync(file);
const replaced = fileContents.toString().replace(replace, replacement);
writeFileSync(file, replaced);
};
/* Constants */
const FLAKE = process.env.FLAKE;
if (!FLAKE) {
console.error('Environment variable FLAKE not found');
process.exit(1);
}
const FLAKE_LOCK = JSON.parse(readFileSync(`${FLAKE}/flake.lock`, 'utf8')).nodes;
const INPUT_REVS = new Map<string, string>();
Object.entries(FLAKE_LOCK).forEach(([key, val]) => {
if (key !== 'root') {
// eslint-disable-next-line
INPUT_REVS.set(key, (val as any).locked.rev);
}
});
const INPUTS = process.argv.slice(2);
/**
* Gets the commit hash of the specified input in this flake.
*
* @param input the name of the input
* @returns the commit hash
*/
const getCurrentRev = (input: string): string => {
if (!INPUT_REVS.has(input)) {
throw new Error(`Input ${input} could not be found.`);
}
return INPUT_REVS.get(input) as string;
};
INPUTS.forEach((input) => {
try {
const inputsFile = `${FLAKE}/inputs/default.nix`;
const rev = getCurrentRev(input);
const msg = ['FIX', 'ME'].join('');
replaceInFile(
new RegExp(`(\\n[ ]*)${input} =.*\\n.*\\n.*`),
`$&\n$1 # ${msg}: $1 rev = "${rev}";`,
inputsFile,
);
}
catch (e) {
console.error((e as Error).message);
}
});

View file

@ -1,10 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "../config/tsconfig.base.json",
"includes": [
"*.ts",
"**/*.ts",
"*.js",
"**/*.js"
]
}

View file

@ -1,3 +0,0 @@
use flake $FLAKE#node
(cd ../config; npm ci)
npm ci

View file

@ -1,32 +0,0 @@
{
buildApp,
callPackage,
curl,
findutils,
go,
jq,
nix-update,
nodejs_latest,
prefetch-npm-deps,
...
}:
buildApp {
src = ./.;
npmDepsHash = "sha256-VkJXHhMLlRNCQvK1rP3bcXfkwrsSnBTDUuE6rpzPFyk=";
runtimeInputs = [
curl
findutils
go
jq
nix-update
nodejs_latest
prefetch-npm-deps
(callPackage ../../modules/docker/updateImage.nix {})
];
meta.description = ''
Updates all derivation sources in this repository and
generates a commit message for the changes made.
'';
}

View file

@ -1,3 +0,0 @@
import eslintConf from 'eslint-conf';
export default eslintConf;

File diff suppressed because it is too large Load diff

View file

@ -1,20 +0,0 @@
{
"name": "update-sources",
"version": "0.0.0",
"bin": "out/bin/app.cjs",
"type": "module",
"scripts": {
"build": "node_ver=$(node -v); esbuild src/app.ts --bundle --platform=node --target=\"node${node_ver:1:2}\" --outfile=out/bin/app.cjs"
},
"dependencies": {
"@types/node": "22.13.14",
"esbuild": "0.25.1",
"eslint": "9.23.0",
"jiti": "2.4.2",
"pkg-types": "2.1.0",
"typescript": "5.8.2"
},
"devDependencies": {
"eslint-conf": "file:../config"
}
}

View file

@ -1,193 +0,0 @@
import { spawnSync } from 'node:child_process';
import { writeFileSync } from 'node:fs';
import { parseArgs } from './lib';
import { updateDocker } from './docker';
import { updateFirefoxAddons } from '././firefox';
import { updateFlakeInputs } from './flake';
import updateNodeModules from './node-modules';
import {
runNixUpdate,
updateCaddyPlugins,
updateVuetorrent,
} from './misc';
/* Constants */
const FLAKE = process.env.FLAKE;
if (!FLAKE) {
console.error('Env var FLAKE not found');
process.exit(1);
}
const args = parseArgs();
const main = async() => {
if (args['c'] || args['custom-sidebar']) {
console.log(runNixUpdate('scopedPackages', 'lovelace-components', 'custom-sidebar'));
}
if (args['cp'] || args['caddy-plugins']) {
console.log(updateCaddyPlugins());
}
if (args['d'] || args['docker']) {
console.log(updateDocker());
}
if (args['f'] || args['firefox']) {
console.log(updateFirefoxAddons());
}
if (args['fcft']) {
console.log(runNixUpdate('fcft'));
}
if (args['h'] || args['homepage']) {
console.log(runNixUpdate('homepage'));
}
if (args['i'] || args['inputs']) {
console.log(updateFlakeInputs());
}
if (args['j'] || args['jmusicbot']) {
console.log(runNixUpdate('jmusicbot'));
}
if (args['m'] || args['material-rounded-theme']) {
console.log(runNixUpdate('scopedPackages', 'lovelace-components', 'material-rounded-theme'));
}
if (args['n'] || args['node_modules']) {
console.log(await updateNodeModules());
}
if (args['p'] || args['pam-fprint-grosshack']) {
console.log(runNixUpdate('pam-fprint-grosshack'));
}
if (args['ph'] || args['protonhax']) {
console.log(runNixUpdate('protonhax'));
}
if (args['s'] || args['some-sass-language-server']) {
console.log(runNixUpdate('some-sass-language-server'));
}
if (args['t'] || args['trash-d']) {
console.log(runNixUpdate('trash-d'));
}
if (args['v'] || args['vuetorrent']) {
console.log(updateVuetorrent());
}
if (args['a'] || args['all']) {
// Update this first because of nix run cmd
const firefoxOutput = updateFirefoxAddons();
console.log(firefoxOutput);
const flakeOutput = updateFlakeInputs();
console.log(flakeOutput);
const dockerOutput = updateDocker();
console.log(dockerOutput);
const nodeModulesOutput = await updateNodeModules();
console.log(nodeModulesOutput);
const vuetorrentOutput = updateVuetorrent();
console.log(vuetorrentOutput);
const caddyPluginsOutput = updateCaddyPlugins();
console.log(caddyPluginsOutput);
// nix-update executions
let nixUpdateOutputs = '';
const updatePackage = (
attr: string,
scope?: string,
scopeAttr?: string,
): void => {
const execution = runNixUpdate(attr, scope, scopeAttr);
nixUpdateOutputs += execution.stdout;
console.log(execution.stderr);
console.log(execution.stdout);
};
updatePackage('fcft');
updatePackage('homepage');
updatePackage('jmusicbot');
updatePackage('pam-fprint-grosshack');
updatePackage('protonhax');
updatePackage('some-sass-language-server');
updatePackage('trash-d');
updatePackage('scopedPackages', 'lovelace-components', 'custom-sidebar');
updatePackage('scopedPackages', 'lovelace-components', 'material-rounded-theme');
spawnSync('nixFastBuild', [], {
shell: true,
stdio: [process.stdin, process.stdout, process.stderr],
});
const indentOutput = (output: string): string => {
return ` ${output.split('\n').join('\n ')}`;
};
const output = [
'chore: update sources\n\n',
];
if (flakeOutput.length > 5) {
output.push(`Flake Inputs:\n${indentOutput(flakeOutput)}\n\n`);
}
if (dockerOutput.length > 5) {
output.push(`Docker Images:\n${indentOutput(dockerOutput)}\n`);
}
if (firefoxOutput.length > 5) {
output.push(`Firefox Addons:\n${indentOutput(firefoxOutput)}\n\n`);
}
if (nodeModulesOutput.length > 5) {
output.push(`Node modules:\n${indentOutput(nodeModulesOutput)}\n`);
}
if (vuetorrentOutput.length > 5) {
output.push(`Misc Sources:\n${indentOutput(vuetorrentOutput)}\n\n`);
}
if (caddyPluginsOutput.length > 5) {
output.push(`Caddy Plugins:\n${indentOutput(caddyPluginsOutput)}\n\n`);
}
if (nixUpdateOutputs.length > 5) {
output.push(`nix-update executions:\n${indentOutput(nixUpdateOutputs)}\n`);
}
if (args['f']) {
writeFileSync(args['f'] as string, output.join(''));
}
else {
console.log(output.join(''));
}
}
spawnSync('alejandra', ['-q', FLAKE], { shell: true });
};
main();

View file

@ -1,33 +0,0 @@
import { readdirSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
/* Constants */
const FLAKE = process.env.FLAKE;
const updateImages = (imagePath: string): string | undefined => {
console.log(`Updating ${imagePath.split('/').at(-1)} images`);
const out = spawnSync('updateImages', [imagePath], { shell: true }).stdout.toString();
if (!out.startsWith('# Locked')) {
return out;
}
};
export const updateDocker = () => {
let updates = '';
updates += updateImages(`${FLAKE}/configurations/nos/modules/jellyfin`) ?? '';
updates += updateImages(`${FLAKE}/configurations/homie/modules/home-assistant/netdaemon`) ?? '';
const DIR = `${FLAKE}/configurations/nos/modules/docker`;
readdirSync(DIR, { withFileTypes: true, recursive: true }).forEach((path) => {
if (path.name === 'compose.nix') {
updates += updateImages(path.parentPath) ?? '';
}
});
return updates;
};

View file

@ -1,61 +0,0 @@
import { spawnSync } from 'node:child_process';
import { readFileSync } from 'node:fs';
/* Constants */
const FLAKE = process.env.FLAKE;
export const updateFirefoxAddons = () => {
console.log('Updating firefox addons using mozilla-addons-to-nix');
const DIR = `${FLAKE}/scopedPackages/firefox-addons`;
const GENERATED_FILE = `${DIR}/generated-firefox-addons.nix`;
const SLUGS = `${DIR}/addons.json`;
const nameMap = Object.fromEntries([...JSON.parse(readFileSync(SLUGS, 'utf-8'))]
.map((addon) => [addon.slug, addon.pname || addon.slug]));
const nixExpr = `'
x: let
inherit (builtins) attrValues filter hasAttr isAttrs map;
in
map (d: d.name) (filter (y:
isAttrs y &&
hasAttr "type" y &&
y.type == "derivation") (attrValues x))
'`;
const OLD_VERS = Object.fromEntries([...JSON.parse(spawnSync('nix', [
'eval',
'.#scopedPackages.x86_64-linux.firefoxAddons',
'--apply',
nixExpr,
'--json',
], { shell: true }).stdout.toString())]
.map((p) => {
const pname = p.replace(/-[0-9].*$/, '');
return [pname, p.replace(`${pname}-`, '')];
})
.filter((pinfo) => pinfo[0] !== 'frankerfacez'));
const NEW_VERS = Object.fromEntries(spawnSync(
'nix',
['run', 'sourcehut:~rycee/mozilla-addons-to-nix',
SLUGS, GENERATED_FILE],
{ shell: true },
).stdout
.toString()
.split('\n')
.map((p) => {
const pinfo = p.replace('Fetched ', '').split(' ');
return [nameMap[pinfo[0]], pinfo[2]];
}));
return Object.keys(OLD_VERS)
.sort()
.filter((pname) => OLD_VERS[pname] !== NEW_VERS[pname])
.map((pname) => `${pname}: ${OLD_VERS[pname]} -> ${NEW_VERS[pname]}`)
.join('\n');
};

View file

@ -1,45 +0,0 @@
import { spawnSync } from 'node:child_process';
/* Constants */
const FLAKE = process.env.FLAKE;
export const updateFlakeInputs = () => {
const output = spawnSync(
`git restore flake.lock &> /dev/null; nix flake update --flake ${FLAKE}` +
' |& grep -v "warning: updating lock file" |& grep -v "unpacking"',
[],
{ shell: true },
).stdout
.toString()
// Add an extra blank line between inputs
.split('\n•')
// Filter out some inputs
.filter((input) => ![
'systems',
'flake-compat',
'flake-utils',
'flake-parts',
'treefmt-nix',
'lib-aggregate',
'lib-aggregate/nixpkgs-lib',
'nix-gaming/umu',
'nix-github-actions',
'pre-commit-hooks',
].some((inputName) => input.startsWith(` Updated input '${inputName}'`)))
.join('\n\n•')
// help readability of git revs
.split('\n')
.map((l) => l
.replace(
/\/(.{40})\?narHash=sha256[^']*(.*)/,
(_, backref1, backref2) => `${backref2} rev: ${backref1}`,
)
.replace(
/\?ref.*&rev=(.{40})[^'&]*(.*)/,
(_, backref1, backref2) => `${backref2} rev: ${backref1}`,
))
.join('\n');
return output;
};

View file

@ -1,43 +0,0 @@
import { spawnSync } from 'node:child_process';
import { readFileSync, writeFileSync } from 'node:fs';
export const parseArgs = () => {
const args = {} as Record<string, unknown>;
let lastFlag: string | null = null;
for (let i = 2; i < process.argv.length; ++i) {
const arg = process.argv[i];
if (arg.toString().startsWith('-')) {
lastFlag = arg.toString().replace(/^-{1,2}/, '');
args[lastFlag] = true;
}
else if (lastFlag) {
args[lastFlag] = arg;
lastFlag = null;
}
else {
console.error(`Could not parse args: ${arg.toString()}`);
}
}
return args;
};
export const parseFetchurl = (url: string) => JSON.parse(spawnSync(
'nix', ['store', 'prefetch-file', '--refresh', '--json',
'--hash-type', 'sha256', url, '--name', '"escaped"'], { shell: true },
).stdout.toString()).hash;
export const replaceInFile = (replace: RegExp, replacement: string, file: string) => {
const fileContents = readFileSync(file);
const replaced = fileContents.toString().replace(replace, replacement);
writeFileSync(file, replaced);
};
export const npmRun = (args: string[], workspaceDir: string) => spawnSync(
'npm', args, { cwd: workspaceDir },
).stdout.toString();

View file

@ -1,161 +0,0 @@
import { writeFileSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
import { parseFetchurl, replaceInFile } from './lib';
/* Constants */
const FLAKE = process.env.FLAKE;
const genVueText = (
version: string,
hash: string,
url: string,
) => `# This file was autogenerated. DO NOT EDIT!
{
version = "${version}";
url = "${url}";
hash = "${hash}";
}
`;
export const updateVuetorrent = () => {
const FILE = `${FLAKE}/configurations/nos/modules/qbittorrent/vuetorrent.nix`;
const OLD_VERSION = JSON.parse(spawnSync('nix',
['eval', '-f', FILE, '--json'],
{ shell: true }).stdout.toString()).version;
const VERSION = JSON.parse(spawnSync('curl',
['-s', 'https://api.github.com/repos/VueTorrent/VueTorrent/releases/latest'],
{ shell: true }).stdout.toString()).tag_name.replace('v', '');
const URL = `https://github.com/VueTorrent/VueTorrent/releases/download/v${VERSION}/vuetorrent.zip`;
const HASH = parseFetchurl(URL);
const fileText = genVueText(VERSION, HASH, URL);
writeFileSync(FILE, fileText);
return OLD_VERSION !== VERSION ? `Vuetorrent: ${OLD_VERSION} -> ${VERSION}` : '';
};
const getAttrVersion = (attr: string): string => spawnSync('nix',
['eval', '--raw', `${FLAKE}#${attr}.version`],
{ shell: true }).stdout.toString();
export const runNixUpdate = (
attr: string,
scope?: string,
scopeAttr?: string,
): { stdout: string, stderr: string } => {
const getJsonArray = (jsonObj: string) => Array(JSON.parse(jsonObj))[0].slice(1).join(' ');
const realAttr = scope ? `${attr}.x86_64-linux.${scope}.${scopeAttr}` : attr;
const OLD_VERSION = getAttrVersion(realAttr);
const execOptions = spawnSync(
`nix eval --json ${FLAKE}#${realAttr}.updateScript`,
[],
{ shell: true, cwd: FLAKE },
);
const options = execOptions.status === 0 ?
execOptions.stdout.toString().startsWith('[') ?
getJsonArray(execOptions.stdout.toString()) :
'-u' :
'';
const execution = spawnSync(
`nix-update --flake ${realAttr} ${options} --quiet --commit-message | head -n 1`,
[],
{ shell: true, cwd: FLAKE },
);
const NEW_VERSION = getAttrVersion(realAttr);
return {
stdout: OLD_VERSION !== NEW_VERSION ?
scope ?
execution.stdout.toString().replaceAll(`${attr}.x86_64-linux.${scope}.`, '') :
execution.stdout.toString() :
'',
stderr: execution.stderr.toString(),
};
};
const genPluginsText = (
plugins: Record<string, { url: string, version: string }>,
) => `# This file was autogenerated. DO NOT EDIT!
{
plugins = {
${Object.entries(plugins)
.map(([key, value]) => `
${key} = {
url = "${value.url}";
version = "${value.version}";
};
`)
.join('')}
};
hash = "";
}
`;
export const updateCaddyPlugins = () => {
let updates = '';
const dir = `${FLAKE}/configurations/cluster/modules/caddy`;
// Setup workspace
spawnSync(
[
'rm -rf /tmp/update-caddy',
'mkdir -p /tmp/update-caddy',
'cd /tmp/update-caddy || exit 1',
'go mod init temp',
].join('; '),
[],
{ shell: true, cwd: '/tmp' },
);
const plugins = JSON.parse(spawnSync('nix',
['eval', '-f', `${dir}/plugins.nix`, '--json'],
{ shell: true }).stdout.toString()).plugins as Record<string, { url: string, version: string }>;
// Get most recent versions of plugins
Object.entries(plugins).forEach(([key, value]) => {
const NEW_VERSION = spawnSync([
'go mod init temp > /dev/null',
`go get ${value.url} > /dev/null`,
`grep '${value.url}' go.mod`,
].join('; '), [], { shell: true, cwd: '/tmp/update-caddy' })
.stdout
.toString()
.trim()
.replace(' // indirect', '')
.split(' ')[1];
if (plugins[key].version !== NEW_VERSION) {
updates += `${key}: ${plugins[key].version} -> ${NEW_VERSION}`;
plugins[key].version = NEW_VERSION;
}
});
writeFileSync(`${dir}/plugins.nix`, genPluginsText(plugins));
// Get new hash
const caddyPkgAttr = 'nixosConfigurations.thingone.config.services.caddy.package';
const NEW_HASH = spawnSync(
`nix build "$FLAKE#${caddyPkgAttr}" |& sed -n 's/.*got: *//p'`,
[],
{ shell: true },
).stdout.toString().trim();
replaceInFile(/hash = ".*";/, `hash = "${NEW_HASH}";`, `${dir}/plugins.nix`);
return updates;
};

View file

@ -1,121 +0,0 @@
import { readPackageJSON, writePackageJSON } from 'pkg-types';
import { accessSync, constants, existsSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
import { replaceInFile, npmRun } from './lib';
/* Constants */
const FLAKE = process.env.FLAKE as string;
const PINS = new Map([]);
const updatePackageJson = async(workspaceDir: string, updates: object) => {
const currentPackageJson = await readPackageJSON(`${workspaceDir}/package.json`);
const outdated = JSON.parse(npmRun(['outdated', '--json'], workspaceDir));
const updateDeps = (deps: string) => {
Object.keys(currentPackageJson[deps]).forEach(async(dep) => {
if (dep === 'astal') {
const latestCommit = JSON.parse(spawnSync('curl',
['-s', 'https://api.github.com/repos/Aylur/astal/commits/main'],
{ shell: true }).stdout.toString()).sha;
currentPackageJson[deps][dep] = `https://gitpkg.vercel.app/Aylur/astal/lang/gjs/src?${latestCommit}`;
return;
}
if (PINS.has(dep)) {
currentPackageJson[deps][dep] = PINS.get(dep);
return;
}
const versions = outdated[dep];
const current = versions?.wanted || versions?.current;
if (!current) {
return;
}
if (current !== versions.latest) {
updates[dep] = `${current} -> ${versions.latest}`;
}
currentPackageJson[deps][dep] = versions.latest;
});
};
if (currentPackageJson.dependencies) {
updateDeps('dependencies');
}
if (currentPackageJson.devDependencies) {
updateDeps('devDependencies');
}
await writePackageJSON(`${workspaceDir}/package.json`, currentPackageJson);
};
const prefetchNpmDeps = (workspaceDir: string): string => {
npmRun(['update', '--package-lock-only'], workspaceDir);
return spawnSync(
'prefetch-npm-deps',
[`${workspaceDir}/package-lock.json`],
).stdout.toString().replace('\n', '');
};
export default async() => {
console.log('Updating node modules');
const updates = {};
const packages = spawnSync('find', [FLAKE, '-name', 'package.json']).stdout.toString().split('\n')
.filter((f) => f !== '')
.filter((f) => ![
'.direnv',
'node_modules',
'results',
].some((dirName) => f.includes(dirName)));
for (const path of packages) {
console.log(path);
try {
accessSync(path, constants.R_OK | constants.W_OK);
const parentPath = path.replace('/package.json', '');
await updatePackageJson(parentPath, updates);
if (existsSync(`${parentPath}/default.nix`)) {
const hash = prefetchNpmDeps(parentPath);
replaceInFile(
/npmDepsHash = ".*";/,
`npmDepsHash = "${hash}";`,
`${parentPath}/default.nix`,
);
}
// Make sure we update the apps' config package-lock.json
if (parentPath.includes('apps/config')) {
npmRun(['i'], parentPath);
}
}
catch (e) {
console.warn(`Could not write to ${path}`);
console.warn(e);
}
}
return Object.entries(updates)
.map(([key, dep]) => `${key}: ${dep}`)
.join('\n');
};

View file

@ -1,10 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "../config/tsconfig.base.json",
"includes": [
"*.ts",
"**/*.ts",
"*.js",
"**/*.js"
]
}

129
conf/pkgs.log Normal file
View file

@ -0,0 +1,129 @@
acpi
alacritty
asp
bat
blueman
bluez
bluez-plugins
bluez-tools
bluez-utils
brightnessctl
cliphist
cpio
cups
discord
doctest
dolphin
dosfstools
exfatprogs
f2fs-tools
ffmpeg-compat-57
ffmpegthumbs
firefox
flatery-icon-theme-git
fwupd
galaxybudsclient-bin
gimp
git-lfs
gnome-bluetooth-3.0
gnome-calculator
gnome-keyring
gnome-themes-extra
gparted
grim
grub-customizer
gtklock
gtklock-playerctl-module
gtklock-powerbar-module
gtklock-userinfo-module
hplip
hyprpaper-git
iio-sensor-proxy-git
jdk-temurin
kde-cli-tools
kdegraphics-thumbnailers
kimageformats
kio-admin
kvantum
libinput-gestures-git
libreoffice-fresh
light
lostfiles
nano
neofetch
neovim-git
network-manager-applet
nextcloud-client
nilfs-utils
nm-connection-editor
nwg-look-bin
otf-font-awesome
pavucontrol
photoqt
pkgfile
pokemon-colorscripts-git
polkit-kde-agent
progress
python-evdev
python-libevdev
python-pyclip
python-pyqt5
python-pyqt5-3d
python-pyqt5-chart
python-pyqt5-datavisualization
python-pyqt5-networkauth
python-pyqt5-purchasing
python-pyqt5-webengine
python-pyqt6
python-pyqt6-3d
python-pyqt6-charts
python-pyqt6-datavisualization
python-pyqt6-networkauth
python-pyqt6-webengine
python-uinput
qt5-charts
qt5-imageformats
qt5ct
reiserfsprogs
ripgrep
rofi-lbonn-wayland-only-git
rustup
sddm-git
seahorse
slurp
spicetify-cli-git
spicetify-themes-git
spotify-snapstore
spotifywm-git
squeekboard-git
swappy-git
swayidle
swaync-git
swayosd-git
tablet-mode
tlp
ttf-font-awesome
ttf-go-nerd
ttf-jetbrains-mono
ttf-jetbrains-mono-nerd
ttf-meslo-nerd-font-powerlevel10k
ttf-ms-win11-auto
ttf-nerd-fonts-symbols
ttf-nerd-fonts-symbols-common
ttf-space-mono-nerd
ttf-ubuntu-font-family
ttf-ubuntu-nerd
tutanota-desktop-bin
waybar-hyprland-git
waydroid-git
wev
wine-staging
winetricks
wl-clip-persist-git
wl-color-picker
xclip
xdg-desktop-portal-hyprland-git
xdg-utils
xorg-xwayland
zathura-pdf-mupdf
zenity

1
conf/toinstall.sh Executable file
View file

@ -0,0 +1 @@
yay -Sy acpi alacritty asp bat blueman bluez bluez-plugins bluez-tools bluez-utils brightnessctl cliphist cpio cups discord doctest dolphin dosfstools exfatprogs f2fs-tools ffmpeg-compat-57 ffmpegthumbs firefox flatery-icon-theme-git fwupd galaxybudsclient-bin gimp git-lfs gnome-bluetooth-3.0 gnome-calculator gnome-keyring gnome-themes-extra gparted grim grub-customizer gtklock gtklock-playerctl-module gtklock-powerbar-module gtklock-userinfo-module hplip hyprpaper-git iio-sensor-proxy-git jdk-temurin kde-cli-tools kdegraphics-thumbnailers kimageformats kio-admin kvantum libinput-gestures-git libreoffice-fresh light lostfiles nano neofetch neovim-git network-manager-applet nextcloud-client nilfs-utils nm-connection-editor nwg-look-bin otf-font-awesome pavucontrol photoqt pkgfile pokemon-colorscripts-git polkit-kde-agent progress python-evdev python-libevdev python-pyclip python-pyqt5 python-pyqt5-3d python-pyqt5-chart python-pyqt5-datavisualization python-pyqt5-networkauth python-pyqt5-purchasing python-pyqt5-webengine python-pyqt6 python-pyqt6-3d python-pyqt6-charts python-pyqt6-datavisualization python-pyqt6-networkauth python-pyqt6-webengine python-uinput qt5-charts qt5-imageformats qt5ct reiserfsprogs ripgrep rofi-lbonn-wayland-only-git rustup sddm-git seahorse slurp spicetify-cli-git spicetify-themes-git spotify-snapstore spotifywm-git squeekboard-git swappy-git swayidle swaync-git swayosd-git tablet-mode tlp ttf-font-awesome ttf-go-nerd ttf-jetbrains-mono ttf-jetbrains-mono-nerd ttf-meslo-nerd-font-powerlevel10k ttf-ms-win11-auto ttf-nerd-fonts-symbols ttf-nerd-fonts-symbols-common ttf-space-mono-nerd ttf-ubuntu-font-family ttf-ubuntu-nerd tutanota-desktop-bin waybar-hyprland-git waydroid-git wev wine-staging winetricks wl-clip-persist-git wl-color-picker xclip xdg-desktop-portal-hyprland-git xdg-utils xorg-xwayland zathura-pdf-mupdf zenity

View file

@ -1,19 +0,0 @@
# NixosConfigurations
This directory contains every device's main configuration file, their `hardware-configuration.nix` and some custom modules
unique to them.
## List of my devices
| Name | Model / Specs | Description |
| --------- | ------------- | ------------------------------------------------------------------------------------------------ |
| `android` | OnePlus 9 Pro | [Nix-On-Droid](https://github.com/nix-community/nix-on-droid) configuration for my OnePlus 9 Pro |
| `bbsteamie` | 512GB OLED | My wife's SteamDeck that has a pink case (it took a lot of convincing for this) |
| `binto` | NVIDIA 3070 with Ryzen 7 3700X | Desktop PC with a multi-monitor setup |
| `homie` | Lenovo Thinkcentre M910q | Mini PC that serves as a Home-assistant server |
| `live-image` | USB key | Basic configuration that serves as my custom ISO target |
| `nos` | NVIDIA 3060 with i5-3600 | Custom built NAS and seedbox for Linux ISOs ;) |
| `servivi` | Headless Ryzen 5 3600 | Gaming PC in a previous life, it is now used as a build farm and hosts game servers |
| `thingone` | Lenovo ThinkCentre M900 | Mini PC that makes use of [NixOS-pcsd](https://github.com/matt1432/nixos-pcsd) to form a cluster with its twin. Files located in `cluster` |
| `thingtwo` | Lenovo ThinkCentre M900 | Mini PC that makes use of [NixOS-pcsd](https://github.com/matt1432/nixos-pcsd) to form a cluster with its twin. Files located in `cluster` |
| `wim` | ThinkPad L13 Yoga Gen 3 (Ryzen 7 PRO 5875U) | 2-1 Lenovo Laptop that I use for university |

View file

@ -1,56 +0,0 @@
{
config,
lib,
pkgs,
...
}: let
inherit (lib) attrValues concatStringsSep;
in {
imports = [./nix-on-droid.nix];
environment.variables.FLAKE = "/data/data/com.termux.nix/files/home/.nix";
terminal.font = "${
pkgs.nerd-fonts.jetbrains-mono
}/share/fonts/truetype/NerdFonts/JetBrainsMono/JetBrainsMonoNerdFontMono-Regular.ttf";
environment.packages = [
(pkgs.writeShellApplication {
name = "switch";
runtimeInputs = attrValues {
inherit
(pkgs)
coreutils
nix-output-monitor
nvd
;
};
text = ''
oldProfile=$(realpath /nix/var/nix/profiles/per-user/nix-on-droid/profile)
nix-on-droid ${concatStringsSep " " [
"switch"
"--flake ${config.environment.variables.FLAKE}"
"--builders ssh-ng://matt@100.64.0.7"
''"$@"''
"|&"
"nom"
]} &&
nvd diff "$oldProfile" "$(realpath /nix/var/nix/profiles/per-user/nix-on-droid/profile)"
'';
})
];
environment.etcBackupExtension = ".bak";
environment.motd = null;
home-manager.backupFileExtension = "hm-bak";
# Set your time zone.
time.timeZone = "America/Montreal";
# No touchy
system.stateVersion = "23.05";
}

View file

@ -1,97 +0,0 @@
{
config,
self,
...
}: {
imports = [
self.nixosModules.base-droid
{
roles.base = {
enable = true;
user = "nix-on-droid";
};
}
self.nixosModules.tmux
{programs.tmux.enableCustomConf = true;}
];
nix = {
# Edit nix.conf
extraOptions = ''
experimental-features = nix-command flakes
keep-outputs = true
keep-derivations = true
warn-dirty = false
'';
substituters = [
# Nix-community
"https://nix-community.cachix.org"
# Personal cache
"https://cache.nelim.org"
];
trustedPublicKeys = [
# Nix-community
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
# Personal cache
"cache.nelim.org:JmFqkUdH11EA9EZOFAGVHuRYp7EbsdJDHvTQzG2pPyY="
];
};
# Global hm settings
home-manager.config = {
imports = [
self.homeManagerModules.neovim
{
programs.neovim = {
enable = true;
user = "nix-on-droid";
ideConfig = {
enableJava = false;
enableNix = false;
enablePython = false;
};
};
}
self.homeManagerModules.shell
{programs.bash.enable = true;}
{
programs.bash.sessionVariables = {
FLAKE = config.environment.variables.FLAKE;
};
programs.bash.shellAliases = {
# Make ping work on nix-on-droid
# https://github.com/nix-community/nix-on-droid/issues/185#issuecomment-1659294700
ping = "/android/system/bin/linker64 /android/system/bin/ping";
# SSH
# Desktop
pc = "ssh -t matt@100.64.0.6 'tmux -2u new -At phone'";
# NAS
nos = "ssh -t matt@100.64.0.4 'tmux -2u new -At phone'";
# Experimenting server
servivi = "ssh -t matt@100.64.0.7 'tmux -2u new -At phone'";
# Home-assistant
homie = "ssh -t matt@100.64.0.10 'tmux -2u new -At phone'";
# Cluster nodes
thingone = "ssh -t matt@100.64.0.8 'tmux -2u new -At phone'";
thingtwo = "ssh -t matt@100.64.0.9 'tmux -2u new -At phone'";
};
}
];
home.stateVersion = "23.05";
};
}

View file

@ -1,81 +0,0 @@
{
mainUser,
self,
...
}: {
# ------------------------------------------------
# Imports
# ------------------------------------------------
imports = [
./hardware-configuration.nix
./modules
self.nixosModules.base
self.nixosModules.meta
self.nixosModules.plymouth
self.nixosModules.server
];
# State Version: DO NOT CHANGE
system.stateVersion = "24.11";
# ------------------------------------------------
# User Settings
# ------------------------------------------------
users.users.${mainUser} = {
isNormalUser = true;
uid = 1000;
hashedPassword = "$y$j9T$b6YdvHx1b/HOD6Kt3Tw1W.$yIy5Km1xBViJA2kra9l38S/0auhEHPdXOMb6RBhgxID";
extraGroups = [
"wheel"
"adm"
];
};
networking = {
hostName = "bbsteamie";
networkmanager.enable = true;
};
time.timeZone = "America/Montreal";
# ------------------------------------------------
# `Self` Modules configuration
# ------------------------------------------------
meta = {
roleDescription = "My wife's SteamDeck that has a pink case (it took a lot of convincing for this)";
hardwareDescription = "512GB OLED";
};
roles.base = {
enable = true;
user = mainUser;
};
roles.server = {
enable = true;
user = mainUser;
sshd.enable = true;
};
boot.plymouth = {
enable = true;
theme = "bgrt";
};
home-manager.users.${mainUser} = {
imports = [
self.homeManagerModules.shell
];
programs = {
bash = {
enable = true;
promptMainColor = "pink";
};
};
};
}

View file

@ -1,61 +0,0 @@
{
config,
jovian,
modulesPath,
...
}: {
nixpkgs.hostPlatform = "x86_64-linux";
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
jovian.nixosModules.default
];
jovian = {
steamos.useSteamOSConfig = true;
devices.steamdeck = {
enable = true;
enableGyroDsuService = true;
};
hardware.has.amd.gpu = true;
};
boot = {
kernelModules = ["kvm-amd"];
initrd.availableKernelModules = ["nvme" "xhci_pci" "usbhid" "sdhci_pci"];
loader = {
efi.canTouchEfiVariables = true;
systemd-boot = {
enable = true;
configurationLimit = 30;
};
};
};
virtualisation.waydroid.enable = true;
fileSystems = {
"/" = {
device = "/dev/disk/by-label/NIXROOT";
fsType = "ext4";
};
"/boot" = {
device = "/dev/disk/by-label/NIXBOOT";
fsType = "vfat";
options = ["fmask=0022" "dmask=0022"];
};
};
swapDevices = [
{
device = "/.swapfile";
size = 16 * 1024; # 16GB
}
];
hardware.cpu.amd.updateMicrocode = config.hardware.enableRedistributableFirmware;
}

View file

@ -1,5 +0,0 @@
{...}: {
imports = [
./desktop
];
}

View file

@ -1,40 +0,0 @@
{pkgs, ...}: let
defaultSession = "plasma";
in {
imports = [
(import ./session-switching.nix defaultSession)
(import ./steam.nix defaultSession)
];
services.desktopManager.plasma6.enable = true;
programs = {
kdeconnect.enable = true;
xwayland.enable = true;
};
# Flatpak support for Discover
services.flatpak.enable = true;
services.packagekit.enable = true;
# Wayland env vars
environment.variables = {
NIXOS_OZONE_WL = "1";
ELECTRON_OZONE_PLATFORM_HINT = "auto";
};
environment.systemPackages = builtins.attrValues {
inherit
(pkgs)
firefox
wl-clipboard
xclip
;
inherit
(pkgs.kdePackages)
discover
krfb
;
};
}

View file

@ -1,81 +0,0 @@
# How to install Palia Map and Overwolf on Linux
Dependencies:
- latest GE-Proton: https://github.com/GloriousEggroll/proton-ge-custom
- protontricks: https://github.com/Matoking/protontricks
- protonhax: https://github.com/jcnils/protonhax
## First Step: Install and run Palia at least once
## Second Step: Setup Palia WINEPREFIX with latest GE-Proton
1. Delete prefix at /home/"$USER"/.steam/steam/steamapps/compatdata/2707930/pfx
```bash
rm -r /home/"$USER"/.steam/steam/steamapps/compatdata/2707930/pfx
```
2. Launch Palia with GE-Proton by going to game properties -> Compatibility -> Force the use ... -> Select GE-Proton...
3. Install dotnet48 and other Windows deps to allow for Overwolf installation
```bash
# Force proton version of protontricks
export PROTON_VERSION="GE-Proton9-10"
protontricks 2707930 dotnet48
# If VC is needed
protontricks 2707930 vcrun2010
protontricks 2707930 vcrun2012
protontricks 2707930 vcrun2013
```
## Third Step: Install Overwolf
1. Get this older version that worked for me here: https://overwolf.en.uptodown.com/windows/download/4714215
2. Install it with protontricks. Follow the GUI installer as if you were on Windows
```bash
export PROTON_VERSION="GE-Proton9-10"
protontricks-launch --appid 2707930 Downloads/overwolf-0-195-0-18.exe
```
## Fourth Step: Install Palia Map
1. Get the installer from here: https://www.overwolf.com/app/Leon_Machens-Palia_Map
2. Install it with protontricks.
```bash
export PROTON_VERSION="GE-Proton9-10"
protontricks-launch --appid 2707930 Downloads/Palia\ Map\ -\ Installer.exe
```
3. An error should popup saying `Installation failed` or something along those lines.
Close it and wait. You should see the Overwolf overlay on the left side of your screen
with the Palia Map icon saying `Installing` and then when it's done: `Palia Map`.
If nothing happens, try rebooting your PC and retrying the above shell commands until it works.
## Final Step: Setting it up in Steam
1. Add this line to Palia launch options
```bash
protonhax init %command%
```
2. Add random non-steam game to your library and then edit its properties:
Change the name to `Palia Map` or anything you like
Change the target to this command:
```bash
protonhax run 2707930 "/home/$USER/.local/share/Steam/steamapps/compatdata/2707930/pfx/drive_c/users/steamuser/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Overwolf/Palia Map.lnk"
```

View file

@ -1,134 +0,0 @@
defaultSession: {
config,
lib,
mainUser,
pkgs,
...
}: {
config = let
inherit (lib) findFirst getExe mkForce;
restartNetwork = getExe (pkgs.writeShellApplication {
name = "restart-network";
runtimeInputs = with pkgs; [systemd];
text = "systemctl restart NetworkManager";
});
switch-session = pkgs.writeShellApplication {
name = "switch-session";
text = ''
mkdir -p /etc/sddm.conf.d
cat <<EOF | tee /etc/sddm.conf.d/autologin.conf
[Autologin]
User=${mainUser}
Session=$1
Relogin=true
EOF
'';
};
gaming-mode = pkgs.writeShellScriptBin "gaming-mode" ''
sudo ${pkgs.systemd}/bin/systemctl start to-gaming-mode.service
'';
in {
services.displayManager.sddm = {
enable = true;
autoLogin.relogin = true;
wayland = {
enable = true;
compositorCommand = "kwin";
};
};
# Sets the default session at launch
systemd.services."set-session" = {
wantedBy = ["multi-user.target"];
before = ["display-manager.service"];
path = [switch-session];
script = ''
switch-session "${defaultSession}"
'';
};
# Disable getty on tty1 for seamless DE transitions
systemd.services.display-manager.conflicts = ["getty@tty1.service"];
# Allows switching to gaming mode
systemd.services."to-gaming-mode" = {
wantedBy = mkForce [];
path = [switch-session];
script = ''
switch-session "gamescope-wayland"
systemctl restart display-manager
sleep 10
switch-session "${defaultSession}"
'';
};
security.sudo.extraRules = [
{
users = [mainUser];
groups = [100];
commands = [
# Make it so we don't need root to switch to gaming mode
{
command = "${pkgs.systemd}/bin/systemctl start to-gaming-mode.service";
options = ["SETENV" "NOPASSWD"];
}
# Make it so we don't need root to restart the network on launch
{
command = restartNetwork;
options = ["SETENV" "NOPASSWD"];
}
];
}
];
home-manager.users.${mainUser} = {
# Add desktop entry to make it GUI friendly
xdg.desktopEntries."Gaming Mode" = {
name = "Gaming Mode";
exec = getExe gaming-mode;
icon = "steam";
terminal = false;
type = "Application";
};
home.file."Desktop/Gaming Mode.desktop".source =
(
findFirst
(x: x.meta.name == "Gaming Mode.desktop") {}
config.home-manager.users.mariah.home.packages
)
+ "/share/applications/Gaming Mode.desktop";
# Fix remote control prompt showing up everytime
xdg.configFile = let
mkAutostart = name: exe: {
"autostart/${name}.desktop".text = "[Desktop Entry]\nType=Application\nExec=${exe}";
};
in (
(mkAutostart "restart-network" "sudo ${restartNetwork}")
// (mkAutostart "steam" "steam -silent %U")
// (mkAutostart "krfb" "krfb --nodialog %c")
// (mkAutostart "kde-authorize-steam" (getExe (pkgs.writeShellApplication {
name = "kde-authorize-steam";
text = ''
flatpak permission-set kde-authorized remote-desktop org.kde.krdpserver yes
flatpak permission-set kde-authorized remote-desktop "" yes
'';
})))
);
};
};
# For accurate stack trace
_file = ./session-switching.nix;
}

View file

@ -1,86 +0,0 @@
defaultSession: {
config,
lib,
mainUser,
pkgs,
...
}: let
inherit (lib) attrValues makeSearchPathOutput;
in {
config = {
# Normal Steam Stuff
programs.steam = {
enable = true;
protontricks.enable = true;
remotePlay.openFirewall = true;
extraCompatPackages = [
pkgs.selfPackages.proton-ge-latest
];
# https://github.com/NixOS/nixpkgs/issues/25444#issuecomment-1977416787
extraPackages = with pkgs; [
kdePackages.breeze
];
};
# Jovian Steam settings
jovian.steam = {
# Steam > Settings > System > Enable Developer Mode
# Steam > Developer > CEF Remote Debugging
enable = true;
user = mainUser;
environment = {
STEAM_EXTRA_COMPAT_TOOLS_PATHS =
makeSearchPathOutput
"steamcompattool"
""
config.programs.steam.extraCompatPackages;
};
desktopSession = defaultSession;
};
# Decky settings
jovian.decky-loader = {
enable = true;
user = mainUser;
stateDir = "/home/${mainUser}/.local/share/decky"; # Keep scoped to user
# https://github.com/Jovian-Experiments/Jovian-NixOS/blob/1171169117f63f1de9ef2ea36efd8dcf377c6d5a/modules/decky-loader.nix#L80-L84
extraPackages = attrValues {
inherit
(pkgs)
curl
unzip
util-linux
gnugrep
readline
procps
pciutils
libpulseaudio
;
};
extraPythonPackages = p:
with p; [
click
];
};
# Misc Packages
environment.systemPackages = [
pkgs.steam-rom-manager
pkgs.r2modman
pkgs.selfPackages.protonhax
# Ryujinx ACNH crashes on Vulkan
pkgs.ryujinx
];
};
# For accurate stack trace
_file = ./steam.nix;
}

View file

@ -1,107 +0,0 @@
{
mainUser,
self,
...
}: {
# ------------------------------------------------
# Imports
# ------------------------------------------------
imports = [
./hardware-configuration.nix
./modules
self.nixosModules.base
self.nixosModules.desktop
self.nixosModules.kmscon
self.nixosModules.meta
self.nixosModules.server
];
# State Version: DO NOT CHANGE
system.stateVersion = "23.11";
# ------------------------------------------------
# User Settings
# ------------------------------------------------
users.users.${mainUser} = {
isNormalUser = true;
uid = 1000;
hashedPassword = "$y$j9T$uCv3kB5LI3Shj/5liU9cS0$4s3wWoH4iY29DLC3lJwNaIcurcjsj8L02cMY4EDtnC6";
extraGroups = [
"wheel"
"input"
"uinput"
"adm"
"video"
"libvirtd"
"adbusers"
];
};
programs.adb.enable = true;
networking = {
hostName = "binto";
networkmanager.enable = true;
firewall.enable = false;
};
time.timeZone = "America/Montreal";
# ------------------------------------------------
# `Self` Modules configuration
# ------------------------------------------------
meta = {
roleDescription = "Desktop PC with a multi-monitor setup";
hardwareDescription = "NVIDIA 3070 with Ryzen 7 3700X";
};
roles.base = {
enable = true;
user = mainUser;
};
roles.desktop = {
enable = true;
user = mainUser;
ags.enable = true;
mainMonitor = "desc:GIGA-BYTE TECHNOLOGY CO. LTD. G27QC 0x00000B1D";
fontSize = 12.5;
};
roles.server = {
enable = true;
user = mainUser;
tailscale.enable = true;
sshd.enable = true;
};
services.kmscon.enable = true;
home-manager.users.${mainUser} = {
imports = [
self.homeManagerModules.firefox
self.homeManagerModules.neovim
self.homeManagerModules.shell
];
programs = {
bash = {
enable = true;
promptMainColor = "purple";
};
firefox.enableCustomConf = true;
neovim = {
enable = true;
user = mainUser;
};
};
};
}

View file

@ -1,117 +0,0 @@
{
config,
modulesPath,
pkgs,
...
}: {
nixpkgs.hostPlatform = "x86_64-linux";
imports = [(modulesPath + "/installer/scan/not-detected.nix")];
boot = {
kernelPackages = pkgs.linuxPackages_zen;
kernelParams = [
"amd_pstate=active"
# Remove these if I use plymouth module
"quiet"
"splash"
"boot.shell_on_fail"
"i915.fastboot=1"
"loglevel=3"
"rd.systemd.show_status=false"
"rd.udev.log_level=3"
"udev.log_priority=3"
];
kernelModules = ["kvm-amd"];
# Zenpower for ryzen cpu monitoring
extraModulePackages = builtins.attrValues {
inherit
(config.boot.kernelPackages)
v4l2loopback
zenpower
;
};
blacklistedKernelModules = ["k10temp"];
supportedFilesystems = ["ntfs"];
consoleLogLevel = 0;
initrd = {
verbose = false;
systemd.enable = true;
availableKernelModules = ["nvme" "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod"];
};
loader = {
efi.canTouchEfiVariables = true;
timeout = 0;
systemd-boot = {
enable = true;
consoleMode = "max";
configurationLimit = 30;
};
};
};
fileSystems = {
"/" = {
device = "/dev/disk/by-label/NIXROOT";
fsType = "btrfs";
};
# sudo btrfs subvolume create /@swap
"/swap" = {
device = "/dev/disk/by-label/NIXROOT";
fsType = "btrfs";
# Idk why this is the subvol
options = ["subvol=@/@swap"];
};
"/boot" = {
device = "/dev/disk/by-label/NIXBOOT";
fsType = "vfat";
};
"/run/media/matt/Games" = {
device = "/dev/disk/by-uuid/da62f4ee-d4a6-4fdd-ab12-9c5e131c6f30";
fsType = "ext4";
};
};
swapDevices = [
{
device = "/swap/swapfile";
size = 16 * 1024;
}
];
zramSwap.enable = true;
hardware = {
cpu.amd.updateMicrocode = config.hardware.enableRedistributableFirmware;
uinput.enable = true;
};
virtualisation = {
libvirtd.enable = true;
spiceUSBRedirection.enable = true;
};
environment.systemPackages = builtins.attrValues {
inherit
(pkgs)
qemu
virtiofsd
;
};
nvidia = {
enable = true;
enableNvidiaSettings = true;
enableWayland = true;
enableCUDA = true;
};
}

View file

@ -1,6 +0,0 @@
{...}: {
imports = [
./gpu-replay
./nix-gaming
];
}

View file

@ -1,98 +0,0 @@
{
config,
lib,
mainUser,
pkgs,
...
}: let
inherit (lib) concatStringsSep getExe removePrefix;
inherit (pkgs.selfPackages) gpu-screen-recorder gsr-kms-server;
hyprPkgs = config.home-manager.users.${mainUser}.wayland.windowManager.hyprland.finalPackage;
cfgDesktop = config.roles.desktop;
in {
security.wrappers = {
gpu-screen-recorder = {
owner = "root";
group = "video";
capabilities = "cap_sys_nice+ep";
source = getExe gpu-screen-recorder;
};
gsr-kms-server = {
owner = "root";
group = "video";
capabilities = "cap_sys_admin+ep";
source = getExe gsr-kms-server;
};
};
home-manager.users.${mainUser} = {
home.packages = [
(pkgs.writeShellApplication {
name = "gpu-save-replay";
runtimeInputs = with pkgs; [procps];
text = ''
pkill --signal SIGUSR1 -f gpu-screen-recorder
'';
})
(pkgs.writeShellApplication {
name = "gsr-start";
runtimeInputs = [
pkgs.pulseaudio
pkgs.xorg.xrandr
hyprPkgs
];
text = ''
main="${removePrefix "desc:" cfgDesktop.mainMonitor}"
WINDOW=$(hyprctl -j monitors | jq '.[] |= (.description |= gsub(","; ""))' | jq -r ".[] | select(.description | test(\"$main\")) | .name")
# Fix fullscreen game resolution
xrandr --output "$WINDOW" --primary
gpu-screen-recorder ${concatStringsSep " " [
# Prints fps and damage info once per second.
"-v no"
# Replay buffer time in seconds.
"-r 1200"
# Organise replays in folders based on the current date.
"-df yes"
"-o /home/matt/Videos/Replay"
# Audio codec to use.
"-ac aac"
# Audio device or application to record from (pulse audio device).
"-a desktop/default_output"
"-a microphone/default_input"
# Window id to record, display (monitor name), "screen", "screen-direct", "focused" or "portal".
"-w \"$WINDOW\""
# Frame rate to record at.
"-f 60"
# Container format for output file.
"-c mkv"
# Video codec to use.
"-k hevc"
]}
'';
})
];
wayland.windowManager.hyprland.settings = {
bind = [",F8, exec, ags request 'save-replay'"];
};
};
}

View file

@ -1,37 +0,0 @@
{
nix-gaming,
pkgs,
...
}: {
imports = [
nix-gaming.nixosModules.platformOptimizations
];
programs = {
steam = {
enable = true;
protontricks.enable = true;
remotePlay.openFirewall = true;
extraCompatPackages = [
pkgs.selfPackages.proton-ge-latest
];
platformOptimizations.enable = true;
};
};
environment.systemPackages = [
(pkgs.lutris.override {
extraLibraries = pkgs: [
# List library dependencies here
];
extraPkgs = pkgs: [
# List extra packages available to lutris here
];
})
pkgs.r2modman
pkgs.ryujinx
];
}

View file

@ -1,113 +0,0 @@
deviceName: {
config,
mainUser,
self,
...
}: let
clusterIP = (builtins.head config.services.pcsd.virtualIps).ip;
in {
# ------------------------------------------------
# Imports
# ------------------------------------------------
imports = [
./hardware-configuration.nix
./modules
self.nixosModules.base
self.nixosModules.kmscon
self.nixosModules.meta
self.nixosModules.server
];
config = {
# State Version: DO NOT CHANGE
system.stateVersion = "24.05";
# ------------------------------------------------
# User Settings
# ------------------------------------------------
users.users.${mainUser} = {
isNormalUser = true;
uid = 1000;
hashedPassword =
if deviceName == "thingone"
then "$y$j9T$H.Uu5T7k5OLomqiPtFkVX0$ojaLWjxi.MDjxY00rT5r2dhJkt.9h.pXHgOtlhf3sN/"
else "$y$j9T$dXC7oiLsG7fCBXS1HUxo21$JjDm17jEwM41gnjMUaFdvgSzWXoGYQbqm867VtDAjF7";
extraGroups = [
"wheel"
"adm"
];
};
networking = {
hostName = deviceName;
resolvconf.enable = true;
nameservers = [
clusterIP
"1.0.0.1"
];
extraHosts = ''
10.0.0.244 thingone
10.0.0.159 thingtwo
'';
firewall.enable = false;
};
time.timeZone = "America/Montreal";
# ------------------------------------------------
# `Self` Modules configuration
# ------------------------------------------------
meta = {
roleDescription = ''
Mini PC that makes use of [NixOS-pcsd](https://github.com/matt1432/nixos-pcsd)
to form a cluster with its twin. Files located in `cluster`
'';
hardwareDescription = "Lenovo ThinkCentre M900";
};
roles.base = {
enable = true;
user = mainUser;
};
roles.server = {
enable = true;
user = mainUser;
tailscale.enable = true;
sshd.enable = true;
};
services.kmscon.enable = true;
home-manager.users.${mainUser} = {
imports = [
self.homeManagerModules.neovim
self.homeManagerModules.shell
];
programs = {
bash = {
enable = true;
promptMainColor =
if deviceName == "thingone"
then "green"
else if deviceName == "thingtwo"
then "red"
else "purple";
};
neovim = {
enable = true;
user = mainUser;
};
};
};
};
# For accurate stack trace
_file = ./default.nix;
}

View file

@ -1,59 +0,0 @@
{
config,
modulesPath,
...
}: {
nixpkgs.hostPlatform = "x86_64-linux";
imports = [(modulesPath + "/installer/scan/not-detected.nix")];
boot = {
loader = {
efi.canTouchEfiVariables = true;
timeout = 2;
systemd-boot = {
enable = true;
consoleMode = "max";
configurationLimit = 30;
};
};
initrd.availableKernelModules = [
"xhci_pci"
"ahci"
"usbhid"
"usb_storage"
"sd_mod"
];
};
fileSystems = {
"/" = {
device = "/dev/disk/by-label/NIXROOT";
fsType = "btrfs";
};
# sudo btrfs subvolume create /@swap
"/swap" = {
device = "/dev/disk/by-label/NIXROOT";
fsType = "btrfs";
options = ["subvol=@swap"];
};
"/boot" = {
device = "/dev/disk/by-label/NIXBOOT";
fsType = "vfat";
};
};
swapDevices = [
{
device = "/swap/swapfile";
size = 16 * 1024;
}
];
zramSwap.enable = true;
hardware.cpu.intel.updateMicrocode = config.hardware.enableRedistributableFirmware;
}

View file

@ -1,24 +0,0 @@
{...}: {
services = {
blocky = {
enable = true;
settings = {
upstream = {
default = [
"127.0.0.1:5335"
"127.0.0.1:5335"
];
};
blocking = {
blackLists = {
ads = [
"https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"
];
};
};
};
};
};
}

View file

@ -1,188 +0,0 @@
{
config,
lib,
mainUser,
pkgs,
self,
...
}: let
inherit (lib) attrValues head removeAttrs;
inherit (config.sops) secrets;
inherit (config.networking) hostName;
in {
imports = [self.nixosModules.caddy-plus];
users.users.${mainUser}.extraGroups = ["caddy"];
boot.kernel.sysctl."net.ipv4.ip_nonlocal_bind" = 1;
systemd.services.caddy.serviceConfig = {
EnvironmentFile = secrets.caddy-cloudflare.path;
};
services.caddy = {
enable = true;
enableReload = false;
package = let
pluginsInfo = import ./plugins.nix;
in
pkgs.caddy.withPlugins {
plugins = map (x: "${x.url}@${x.version}") (attrValues pluginsInfo.plugins);
inherit (pluginsInfo) hash;
};
virtualHosts = let
clusterIP = (head config.services.pcsd.virtualIps).ip;
nosIP = "10.0.0.121";
serviviIP = "10.0.0.249";
homieIP = "100.64.0.10";
tlsConf = ''
tls {
dns cloudflare {$CLOUDFLARE_API_TOKEN}
resolvers 1.0.0.1
}
'';
mkPublicReverseProxy = subdomain: ip: extraConf:
{
hostName = "${subdomain}.nelim.org";
reverseProxy = ip;
listenAddresses = [clusterIP];
extraConfig = tlsConf + (extraConf.extraConfig or "");
}
// (removeAttrs extraConf ["extraConfig"]);
in {
# Public
"Home-Assistant" = mkPublicReverseProxy "homie" "${homieIP}:8123" {};
"Vaultwarden" = mkPublicReverseProxy "vault" "${nosIP}:8781" {};
"Hauk" = mkPublicReverseProxy "hauk" "${nosIP}:3003" {};
"Headscale" = mkPublicReverseProxy "headscale" "${clusterIP}:8085" {};
"SearXNG" = mkPublicReverseProxy "search" "${clusterIP}:8080" {};
"Jellyfin" = mkPublicReverseProxy "jelly" "${nosIP}:8096" {
subDirectories.jfa-go = {
subDirName = "accounts";
reverseProxy = "${nosIP}:8056";
};
};
"Jellyseer" = mkPublicReverseProxy "seerr" "${nosIP}:5055" {};
"Komga" = mkPublicReverseProxy "komga" "${nosIP}:7080" {};
"Gameyfin" = mkPublicReverseProxy "games" "${nosIP}:8074" {};
"Forgejo" = mkPublicReverseProxy "git" "${nosIP}:3000" {};
"Nextcloud" = mkPublicReverseProxy "cloud" "${nosIP}:8042" {
extraConfig = ''
redir /.well-known/carddav /remote.php/dav 301
redir /.well-known/caldav /remote.php/dav 301
redir /.well-known/webfinger /index.php/.well-known/webfinger 301
redir /.well-known/nodeinfo /index.php/.well-known/nodeinfo 301
'';
};
"OnlyOffice" = mkPublicReverseProxy "office" "http://${nosIP}:8055" {};
"Immich" = mkPublicReverseProxy "photos" "${nosIP}:2283" {};
"ObsidianLiveSync" = mkPublicReverseProxy "livesync" "${nosIP}:5984" {};
"Binary Cache" = mkPublicReverseProxy "cache" "${serviviIP}:5000" {};
# Private
"nelim.org" = {
serverAliases = ["*.nelim.org"];
extraConfig = tlsConf;
listenAddresses = [
(
if hostName == "thingone"
then "100.64.0.8"
else "100.64.0.9"
)
];
subDomains = {
esphome.reverseProxy = "${homieIP}:6052";
pr-tracker.reverseProxy = "${serviviIP}:3000";
pcsd = {
extraConfig = ''
reverse_proxy https://${clusterIP}:2224 {
transport http {
tls_insecure_skip_verify
}
}
'';
};
komf.reverseProxy = "${nosIP}:8085";
# Resume builder
resume.reverseProxy = "${nosIP}:3060";
resauth.reverseProxy = "${nosIP}:3100";
# FreshRSS & Co
bridge.reverseProxy = "${nosIP}:3006";
drss.reverseProxy = "${nosIP}:3007";
freshrss = {
subDomainName = "rss";
reverseProxy = "${nosIP}:2800";
};
wgui.reverseProxy = "${nosIP}:51821";
lan = {
reverseProxy = "${nosIP}:3020";
extraConfig = ''
redir /index.html /
'';
subDirectories = {
bazarr.reverseProxy = "${nosIP}:6767";
jellystat.reverseProxy = "${nosIP}:3070";
prowlarr.reverseProxy = "${nosIP}:9696";
radarr.reverseProxy = "${nosIP}:7878";
sonarr.reverseProxy = "${nosIP}:8989";
kapowarr.reverseProxy = "${nosIP}:5676";
jdownloader2 = {
subDirName = "jd2";
experimental = true;
reverseProxy = "${nosIP}:5800";
};
qbittorent = {
subDirName = "qbt";
experimental = true;
reverseProxy = "${nosIP}:8080";
};
vaultwarden = {
subDirName = "vault";
experimental = true;
reverseProxy = "${nosIP}:8780";
};
};
};
# Top secret Business
joal.extraConfig = ''
route {
rewrite * /joal/ui{uri}
reverse_proxy * ${nosIP}:5656
}
'';
joalws.extraConfig = ''
route {
reverse_proxy ${nosIP}:5656
}
'';
};
};
};
};
}

View file

@ -1,11 +0,0 @@
# This file was autogenerated. DO NOT EDIT!
{
plugins = {
cloudflare = {
url = "github.com/caddy-dns/cloudflare";
version = "v0.0.0-20250228175314-1fb64108d4de";
};
};
hash = "sha256-YYpsf8HMONR1teMiSymo2y+HrKoxuJMKIea5/NEykGc=";
}

View file

@ -1,11 +0,0 @@
{...}: {
imports = [
./blocky
./caddy
./headscale
./nfs-client
./pcsd
./searxng
./unbound
];
}

View file

@ -1,70 +0,0 @@
{
config,
mainUser,
...
}: let
inherit (config.networking) hostName;
clusterIP = (builtins.head config.services.pcsd.virtualIps).ip;
in {
users.users.${mainUser}.extraGroups = ["headscale"];
services.headscale = {
enable = true;
settings = {
server_url = "https://headscale.nelim.org";
listen_addr = "${clusterIP}:8085";
prefixes = {
v4 = "100.64.0.0/10";
v6 = "fd7a:115c:a1e0::/48";
};
metrics_listen_addr = "127.0.0.1:9090";
grpc_listen_addr = "0.0.0.0:50443";
grpc_allow_insecure = false;
disable_check_updates = true;
ephemeral_node_inactivity_timeout = "30m";
unix_socket = "/run/headscale/headscale.sock";
unix_socket_permission = "0770";
database = {
type = "sqlite";
sqlite.path = "/var/lib/headscale/db.sqlite";
};
private_key_path = "/var/lib/headscale/private.key";
noise.private_key_path = "/var/lib/headscale/noise_private.key";
dns = let
caddyIp =
if hostName == "thingone"
then "100.64.0.8"
else "100.64.0.9";
in {
magic_dns = false;
override_local_dns = true;
nameservers.global = [caddyIp];
};
log = {
format = "text";
level = "info";
};
derp = {
auto_update_enable = true;
update_frequency = "24h";
server = {
enabled = true;
stun_listen_addr = "${clusterIP}:3479";
private_key_path = "/var/lib/headscale/derp_server_private.key";
region_id = 995;
region_code = "mon";
region_name = "montreal";
};
};
};
};
}

Some files were not shown because too many files have changed in this diff Show more