Apparently, depot could be stopped. Who knew?

This commit is contained in:
kat witch 2021-08-18 02:25:15 +01:00
parent c3fe9a355e
commit b383c70492
No known key found for this signature in database
GPG key ID: 1B477797DCA5EC72
196 changed files with 21 additions and 21 deletions

View file

@ -0,0 +1,17 @@
{ config, lib, kw, ... }: with lib; {
deploy.targets.infra = {
tf = {
resources.athame = {
provider = "null";
type = "resource";
connection = {
port = head config.network.nodes.athame.services.openssh.ports;
host = config.network.nodes.athame.network.addresses.public.ipv4.address;
};
};
};
};
network.nodes.athame = {
imports = kw.nodeImport "athame";
};
}

View file

@ -0,0 +1,107 @@
{ meta, config, lib, pkgs, ... }:
with lib;
{
# Imports
imports = with meta; [
profiles.hardware.hcloud-imperative
users.kat.server
users.kat.services.weechat
services.fail2ban
services.filehost
services.gitea
services.grafana
services.logrotate
services.loki
services.mail
services.matrix
services.murmur
services.netdata
services.nginx
services.node-exporter
services.postgres
services.prometheus
services.promtail
services.radicale
services.restic
services.syncplay
services.taskserver
services.vaultwarden
services.website
services.weechat
services.xmpp
services.znc
];
# File Systems and Swap
fileSystems = {
"/" = {
device = "/dev/sda1";
fsType = "ext4";
};
};
# Bootloader
boot.loader.grub = {
enable = true;
version = 2;
device = "/dev/sda";
};
# Networking
networking = {
hostName = "athame";
domain = "kittywit.ch";
hostId = "7b0ac74e";
useDHCP = false;
interfaces = {
enp1s0 = {
useDHCP = true;
ipv6.addresses = [{
address = config.network.addresses.public.ipv6.address;
prefixLength = 64;
}];
};
};
defaultGateway6 = {
address = "fe80::1";
interface = "enp1s0";
};
};
network = {
addresses = {
public = {
enable = true;
ipv4.address = "168.119.126.111";
ipv6.address = "2a01:4f8:c2c:b7a8::1";
};
};
yggdrasil = {
enable = true;
pubkey = "55e3f29c252d16e73ac849a6039824f94df1dee670c030b9e29f90584f935575";
listen.enable = true;
listen.endpoints = [ "tcp://${config.network.addresses.public.ipv4.address}:52969" "tcp://[${config.network.addresses.public.ipv6.address}]:52969" ];
};
};
# Firewall
network.firewall = {
public = {
interfaces = singleton "enp1s0";
tcp.ports = singleton 52969;
};
private.interfaces = singleton "yggdrasil";
};
# State
system.stateVersion = "20.09";
}

View file

@ -0,0 +1 @@
{ ... }: { imports = [ ./home/sway.nix ]; }

View file

@ -0,0 +1,48 @@
{ config, pkgs, lib, ... }:
with lib;
{
imports = [ ./swayidle.nix ];
wayland.windowManager.sway = {
config =
let
lockCommand = "swaylock -i VGA-1:${builtins.elemAt config.kw.wallpapers 0} -s fill";
cfg = config.wayland.windowManager.sway.config;
in
{
#startup = [{ command = "${pkgs.ckb-next}/bin/ckb-next -b"; }];
output =
let
middle = {
res = "1280x1024@75Hz";
pos = "1920 0";
};
in
{
"VGA-1" = middle;
};
keybindings = {
"${cfg.modifier}+x" = "exec ${lockCommand}";
};
modes = {
"System (l) lock, (e) logout, (s) suspend, (h) hibernate, (r) reboot, (Shift+s) shutdown" =
{
"l" = "exec ${lockCommand}, mode default";
"e" = "exec swaymsg exit, mode default";
"s" = "exec systemctl suspend, mode default";
"h" = "exec systemctl hibernate, mode default";
"r" = "exec systemctl reboot, mode default";
"Shift+s" = "exec systemctl shutdown, mode default";
"Return" = "mode default";
"Escape" = "mode default";
};
};
};
};
}

View file

@ -0,0 +1,24 @@
{ config, pkgs, lib, ... }:
{
systemd.user.services.swayidle = {
Unit = {
Description = "swayidle";
Documentation = [ "man:swayidle(1)" ];
PartOf = [ "graphical-session.target" ];
};
Service = {
Type = "simple";
ExecStart = ''
${pkgs.swayidle}/bin/swayidle -w \
timeout 300 '${pkgs.swaylock}/bin/swaylock -f -i VGA-1:${builtins.elemAt config.kw.wallpapers 0} \
timeout 600 'swaymsg "output * dpms off"' \
resume 'swaymsg "output * dpms on"' \
before-sleep '${pkgs.swaylock}/bin/swaylock -f -i VGA-1:${builtins.elemAt config.kw.wallpapers 0}'
'';
RestartSec = 3;
Restart = "always";
};
Install = { WantedBy = [ "sway-session.target" ]; };
};
}

View file

@ -0,0 +1,20 @@
{ config, lib, kw, ... }: with lib; {
config = {
deploy.targets.beltane = {
tf = {
resources.beltane = {
provider = "null";
type = "resource";
connection = {
port = head config.network.nodes.beltane.services.openssh.ports;
host = config.network.nodes.beltane.network.addresses.private.ipv4.address;
};
};
};
};
network.nodes.beltane = {
imports = kw.nodeImport "beltane";
};
};
}

View file

@ -0,0 +1,129 @@
{ meta, tf, config, pkgs, lib, ... }:
with lib;
{
# Imports
imports = with meta; [
profiles.hardware.rm-310
profiles.gui
users.kat.guiFull
services.fusionpbx
services.jellyfin
services.kattv-ingest
services.promtail
services.postgres
services.netdata
services.nfs
services.nginx
services.node-exporter
services.transmission
services.tvheadend
services.zfs
];
# File Systems and Swap
boot.supportedFilesystems = singleton "zfs";
fileSystems = {
"/" = {
device = "zroot/safe/root";
fsType = "zfs";
};
"/nix" = {
device = "zroot/local/nix";
fsType = "zfs";
};
"/home" = {
device = "zroot/safe/home";
fsType = "zfs";
};
"/boot" = {
device = "/dev/disk/by-uuid/44CC-7137";
fsType = "vfat";
};
"/boot-fallback" = {
device = "/dev/disk/by-uuid/4520-4E5F";
fsType = "vfat";
};
"/mnt/zraw" = {
device = "zstore/raw";
fsType = "zfs";
};
"/mnt/zenc" = {
device = "zstore/enc";
fsType = "zfs";
};
};
swapDevices = [
{ device = "/dev/disk/by-uuid/682df001-bad8-4d94-a86b-9068ce5eee4c"; }
{ device = "/dev/disk/by-uuid/1ee2d322-235c-41de-b272-7ceded4e2624"; }
];
# Bootloader
boot.loader = {
efi.canTouchEfiVariables = true;
grub = {
enable = true;
efiSupport = true;
device = "nodev";
mirroredBoots = [
{
devices = [ "/dev/disk/by-uuid/4520-4E5F" ];
path = "/boot-fallback";
}
];
};
};
# Networking
networking = {
hostName = "beltane";
hostId = "3ef9a419";
useDHCP = false;
interfaces.eno1.ipv4.addresses = singleton {
inherit (config.network.addresses.private.ipv4) address;
prefixLength = 24;
};
defaultGateway = config.network.privateGateway;
};
network = {
extraCerts = {
"private_root" = "altar.kittywit.ch";
};
addresses = {
private = {
ipv4.address = "192.168.1.2";
# TODO ipv6.address
};
};
yggdrasil = {
enable = true;
pubkey = "d3e488574367056d3ae809b678f799c29ebfd5c7151bb1f4051775b3953e5f52";
listen.enable = false;
listen.endpoints = [ "tcp://0.0.0.0:0" ];
};
};
# Firewall
network.firewall = {
private.interfaces = singleton "yggdrasil";
public.interfaces = singleton "eno1";
};
# Yggdrasil
# State
system.stateVersion = "21.05";
}

View file

@ -0,0 +1,6 @@
{ config, lib, kw, ... }: with lib; {
deploy.targets.dummy.enable = false;
network.nodes.dummy = {
imports = kw.nodeImport "dummy";
};
}

View file

@ -0,0 +1,48 @@
{ config, lib, pkgs, sources, ... }:
with lib;
let
hexchen = (import sources.hexchen) { };
hexYgg = filterAttrs (_: c: c.enable)
(mapAttrs (_: host: host.config.network.yggdrasil) hexchen.hosts);
in
{
# stuff so dummy host is buildable (you probably don't want/need this???)
# but idk your config sooooo
boot.isContainer = true;
networking.useDHCP = false;
users.users.root.hashedPassword = "";
network.yggdrasil = {
enable = true;
pubkey = "0000000000000000000000000000000000000000000000000000000000000000";
listen.enable = true;
listen.endpoints = flatten (map (c: c.listen.endpoints) (filter
(c:
c.listen.enable && (c.pubkey
!= "0000000000000000000000000000000000000000000000000000000000000000"))
(attrValues hexYgg)));
extra.pubkeys = {
satorin =
"53d99a74a648ff7bd5bc9ba68ef4f472fb4fb8b2e26dfecea33c781f0d5c9525";
shanghai =
"0cc3c26366cbfddfb1534b25c5655733d8f429edc941bcce674c46566fc87027";
grimoire =
"2a1567a2848540070328c9e938c58d40f2b1a3f08982c15c7edc5dcabfde3330";
boline =
"89684441745467da0d1bf7f47dc74ec3ca65e05c72f752298ef3c22a22024d43";
} // (mapAttrs (_: c: c.pubkey) hexYgg);
};
# snippet for single host
# hexchen.network = {
# enable = true;
# pubkey = "0000000000000000000000000000000000000000000000000000000000000000";
# # if server, enable this and set endpoint:
# listen.enable = false;
# listen.endpoints = [
# "tcp://0.0.0.0:0"
# ];
# };
}

View file

@ -0,0 +1,19 @@
{ config, lib, kw, ... }: with lib; {
config = {
deploy.targets.ostara = {
tf = {
resources.ostara = {
provider = "null";
type = "resource";
connection = {
port = head config.network.nodes.ostara.services.openssh.ports;
host = config.network.nodes.ostara.network.addresses.private.ipv4.address;
};
};
};
};
network.nodes.ostara = {
imports = kw.nodeImport "ostara";
};
};
}

View file

@ -0,0 +1,69 @@
{ meta, config, pkgs, lib, ... }:
with lib;
{
# Imports
imports = with meta; [
profiles.hardware.eeepc-1015pem
services.kattv
];
# File Systems and Swap
fileSystems = {
"/" = {
device = "/dev/disk/by-uuid/469a684b-eb8f-48a8-8f98-be58528312c4";
fsType = "ext4";
};
};
swapDevices = [{ device = "/dev/disk/by-uuid/2223e305-79c9-45b3-90d7-560dcc45775a"; }];
# Bootloader
boot.loader.grub = {
enable = true;
version = 2;
device = "/dev/sda";
};
# Hardware
services.logind.lidSwitchExternalPower = "ignore";
# Networking
networking = {
hostName = "ostara";
hostId = "9f89b327";
useDHCP = false;
interfaces.enp1s0.ipv4.addresses = singleton {
inherit (config.network.addresses.private.ipv4) address;
prefixLength = 24;
};
defaultGateway = config.network.privateGateway;
};
network = {
addresses = {
private = {
ipv4.address = "192.168.1.32";
};
};
};
# Firewall
network.firewall = {
public = {
interfaces = singleton "enp1s0";
tcp.ports = [ 9981 9982 ];
};
};
# State
system.stateVersion = "20.09";
}

View file

@ -0,0 +1 @@
{ ... }: { imports = [ ./home/sway.nix ]; }

View file

@ -0,0 +1,83 @@
{ config, pkgs, lib, ... }:
with lib;
{
imports = [ ./swayidle.nix ];
wayland.windowManager.sway = {
config =
let
lockCommand = "swaylock -i HDMI-A-1:${builtins.elemAt config.kw.wallpapers 0} -i DP-1:${builtins.elemAt config.kw.wallpapers 1} -i DVI-D-1:${builtins.elemAt config.kw.wallpapers 2} -s fill";
cfg = config.wayland.windowManager.sway.config;
in
{
#startup = [{ command = "${pkgs.ckb-next}/bin/ckb-next -b"; }];
output =
let
left = {
res = "1920x1080";
pos = "0 0";
};
middle = {
res = "1920x1200";
pos = "1920 0";
};
right = {
res = "1920x1080";
pos = "3840 0";
};
in
{
"DP-1" = right;
"DVI-D-1" = middle;
"HDMI-A-1" = left;
};
keybindings = {
"${cfg.modifier}+x" = "exec ${lockCommand}";
};
modes = {
"System (l) lock, (e) logout, (s) suspend, (h) hibernate, (r) reboot, (Shift+s) shutdown" =
{
"l" = "exec ${lockCommand}, mode default";
"e" = "exec swaymsg exit, mode default";
"s" = "exec systemctl suspend, mode default";
"h" = "exec systemctl hibernate, mode default";
"r" = "exec systemctl reboot, mode default";
"Shift+s" = "exec systemctl shutdown, mode default";
"Return" = "mode default";
"Escape" = "mode default";
};
};
input = {
"5426:103:Razer_Razer_Naga_Trinity" = {
accel_profile = "adaptive";
pointer_accel = "-0.5";
};
"5824:1503:screenstub-tablet" = { events = "disabled"; };
"5824:1503:screenstub-mouse" = { events = "disabled"; };
"5824:1503:screenstub-kbd" = { events = "disabled"; };
"1386:215:Wacom_BambooPT_2FG_Small_Pen" = {
map_to_output = "HDMI-A-1";
};
"1386:215:Wacom_BambooPT_2FG_Small_Finger" = {
natural_scroll = "enabled";
middle_emulation = "enabled";
tap = "enabled";
dwt = "enabled";
accel_profile = "flat";
pointer_accel = "0.05";
};
};
};
extraConfig = ''
workspace "1" output "DP-1"
workspace "11:F1" output "DVI-1"
workspace "12:F2" output "HDMI-A-1"
'';
};
}

View file

@ -0,0 +1,26 @@
{ config, pkgs, lib, ... }:
with lib;
{
systemd.user.services.swayidle = {
Unit = {
Description = "swayidle";
Documentation = [ "man:swayidle(1)" ];
PartOf = [ "graphical-session.target" ];
};
Service = {
Type = "simple";
ExecStart = ''
${pkgs.swayidle}/bin/swayidle -w \
timeout 300 '${pkgs.swaylock}/bin/swaylock -f -i HDMI-A-1:${builtins.elemAt config.kw.wallpapers 0} -i DP-1:${builtins.elemAt config.kw.wallpapers 1} -i DVI-D-1:${builtins.elemAt config.kw.wallpapers 2}' \
timeout 600 'swaymsg "output * dpms off"' \
resume 'swaymsg "output * dpms on"' \
before-sleep '${pkgs.swaylock}/bin/swaylock -f -i HDMI-A-1:${builtins.elemAt config.kw.wallpapers 0} -i DP-1:${builtins.elemAt config.kw.wallpapers 1} -i DVI-D-1:${builtins.elemAt config.kw.wallpapers 2}'
'';
RestartSec = 3;
Restart = "always";
};
Install = { WantedBy = [ "sway-session.target" ]; };
};
}

View file

@ -0,0 +1,19 @@
{ config, lib, kw, ... }: with lib; {
config = {
deploy.targets.personal = {
tf = {
resources.samhain = {
provider = "null";
type = "resource";
connection = {
port = head config.network.nodes.samhain.services.openssh.ports;
host = config.network.nodes.samhain.network.addresses.private.ipv4.address;
};
};
};
};
network.nodes.samhain = {
imports = kw.nodeImport "samhain";
};
};
}

View file

@ -0,0 +1,153 @@
{ meta, tf, config, pkgs, lib, sources, ... }:
with lib;
let
hexchen = (import sources.hexchen) { };
hexYgg = filterAttrs (_: c: c.enable)
(mapAttrs (_: host: host.config.network.yggdrasil) hexchen.hosts);
in {
# Imports
imports = with meta; [
profiles.hardware.ms-7b86
profiles.gui
profiles.vfio
users.kat.guiFull
services.netdata
services.nginx
services.katsplash
services.node-exporter
services.promtail
services.restic
services.zfs
];
# File Systems and Swap
boot.supportedFilesystems = [ "zfs" "xfs" ];
fileSystems = {
"/" = {
device = "rpool/safe/root";
fsType = "zfs";
};
"/nix" = {
device = "rpool/local/nix";
fsType = "zfs";
};
"/home" = {
device = "rpool/safe/home";
fsType = "zfs";
};
"/boot" = {
device = "/dev/disk/by-uuid/AED6-D0D1";
fsType = "vfat";
};
"/mnt/xstore" = {
device = "/dev/disk/by-uuid/64269102-a278-4919-9118-34e37f4afdb0";
fsType = "xfs";
};
};
swapDevices = [
{ device = "/dev/disk/by-uuid/89831a0f-93e6-4d30-85e4-09061259f140"; }
{ device = "/dev/disk/by-uuid/8f944315-fe1c-4095-90ce-50af03dd5e3f"; }
];
# Bootloader
boot.loader = {
systemd-boot.enable = true;
efi.canTouchEfiVariables = true;
};
# Hardware
deploy.profile.hardware.acs-override = true;
hardware.openrazer = {
enable = true;
};
environment.systemPackages = [ pkgs.razergenie ];
boot.modprobe.modules = {
vfio-pci = let
vfio-pci-ids = [
"1002:67df" "1002:aaf0" # RX 580
"1912:0014" # Renesas USB 3
"1022:149c" # CPU USB 3
];
in mkIf (vfio-pci-ids != [ ]) {
options.ids = concatStringsSep "," vfio-pci-ids;
};
};
services.udev.extraRules = ''
SUBSYSTEM=="usb", ACTION=="add", ATTRS{idVendor}=="1532", ATTRS{idProduct}=="0067", GROUP="vfio"
SUBSYSTEM=="block", ACTION=="add", ATTRS{model}=="HFS256G32TNF-N3A", ATTRS{wwid}=="t10.ATA HFS256G32TNF-N3A0A MJ8BN15091150BM1Z ", OWNER="kat"
'';
# TODO: Replace this drive forward with one half of the 1.82TiB drive.
# SUBSYSTEM=="block", ACTION=="add", ATTR{partition}=="2", ATTR{size}=="1953503232", ATTRS{wwid}=="naa.5000039fe6e8614e", OWNER="kat"
# Networking
networking = {
hostName = "samhain";
hostId = "617050fc";
useDHCP = false;
useNetworkd = true;
firewall.allowPing = true;
};
systemd.network = {
networks.enp34s0 = {
matchConfig.Name = "enp34s0";
bridge = singleton "br";
};
networks.br = {
matchConfig.Name = "br";
address = singleton "${config.network.addresses.private.ipv4.address}/24" ;
gateway = singleton config.network.privateGateway;
};
netdevs.br = {
netdevConfig = {
Name = "br";
Kind = "bridge";
MACAddress = "00:d8:61:c7:f4:9d";
};
};
};
services.avahi.enable = true;
network = {
addresses = {
private = {
ipv4.address = "192.168.1.1";
};
};
dns.dynamic = true;
yggdrasil = {
enable = true;
pubkey = "a7110d0a1dc9ec963d6eb37bb6922838b8088b53932eae727a9136482ce45d47";
listen.enable = false;
listen.endpoints = [ "tcp://0.0.0.0:0" ];
};
};
# Firewall
network.firewall = {
public.interfaces = singleton "br";
private = {
interfaces = singleton "yggdrasil";
};
};
# State
system.stateVersion = "20.09";
}

View file

@ -0,0 +1 @@
{ ... }: { }

View file

@ -0,0 +1,21 @@
{ lib, config, ... }: with lib; {
deploy.targets.mabon = {
nodeNames = lib.singleton "mabon";
tf = {
resources.mabon = {
provider = "null";
type = "resource";
connection = {
port = 62954;
host = "192.168.1.119";
};
};
};
};
network.nodes.mabon = {
imports = lib.hostImport "mabon";
networking = {
hostName = "mabon";
};
};
}

View file

@ -0,0 +1,29 @@
{ config, users, pkgs, profiles, ... }:
{
imports = [
./hw.nix
profiles.gui
profiles.fvwm
profiles.laptop
users.kairi.guiFull
];
networking.wireless.interfaces = [ "wlp3s0" ];
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.kernelPackages = pkgs.linuxPackages;
boot.kernelModules = [ "wl" ];
boot.extraModulePackages = [ config.boot.kernelPackages.broadcom_sta ];
networking.hostId = "d199ad70";
networking.hostName = "mabon";
networking.useDHCP = false;
networking.interfaces.enp1s0.useDHCP = false;
networking.interfaces.wlp2s0.useDHCP = true;
system.stateVersion = "20.09";
}

View file

@ -0,0 +1,38 @@
{ config, lib, pkgs, modulesPath, ... }:
{
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
boot.initrd.availableKernelModules = [
"uhci_hcd"
"ehci_pci"
"ahci"
"firewire_ohci"
"usbhid"
"usb_storage"
"sd_mod"
"sr_mod"
"sdhci_pci"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" "wl" ];
boot.extraModulePackages = [ config.boot.kernelPackages.broadcom_sta ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/236f9363-19ee-46e3-8db4-5dd1e28b742d";
fsType = "ext4";
};
boot.initrd.luks.devices."cryptroot".device =
"/dev/disk/by-uuid/b0435b6c-fd76-44d0-8b63-2c2c059df814";
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/84FB-4F88";
fsType = "vfat";
};
swapDevices =
[{ device = "/dev/disk/by-uuid/926c41d6-c06a-4dcc-b55d-f4cfaafe4bac"; }];
powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
}

View file

@ -0,0 +1 @@
{ ... }: { imports = [ ./home/sway.nix ./home/base16.nix ]; }

View file

@ -0,0 +1,5 @@
{ config, ... }:
{
base16.alias.default = "atelier.atelier-cave-light";
}

View file

@ -0,0 +1,51 @@
{ config, lib, pkgs, ... }:
with lib;
{
imports = [ ./swayidle.nix ];
wayland.windowManager.sway.config =
let
lockCommand = "swaylock -i eDP-1:${builtins.elemAt config.kw.wallpapers 0} -s fill";
cfg = config.wayland.windowManager.sway.config;
in
{
output =
let
laptop = {
res = "1920x1080";
pos = "0 0";
};
in
{ "eDP-1" = laptop; };
keybindings = {
"${cfg.modifier}+x" = "exec ${lockCommand}";
};
modes = {
"System (l) lock, (e) logout, (s) suspend, (h) hibernate, (r) reboot, (Shift+s) shutdown" =
{
"l" = "exec ${lockCommand}, mode default";
"e" = "exec swaymsg exit, mode default";
"s" = "exec systemctl suspend, mode default";
"h" = "exec systemctl hibernate, mode default";
"r" = "exec systemctl reboot, mode default";
"Shift+s" = "exec systemctl shutdown, mode default";
"Return" = "mode default";
"Escape" = "mode default";
};
};
input = {
"1739:33362:Synaptics_TM3336-002" = {
dwt = "enabled";
tap = "enabled";
natural_scroll = "enabled";
middle_emulation = "enabled";
click_method = "clickfinger";
};
};
};
}

View file

@ -0,0 +1,26 @@
{ config, pkgs, lib, ... }:
with lib;
{
systemd.user.services.swayidle = {
Unit = {
Description = "swayidle";
Documentation = [ "man:swayidle(1)" ];
PartOf = [ "graphical-session.target" ];
};
Service = {
Type = "simple";
ExecStart = ''
${pkgs.swayidle}/bin/swayidle -w \
timeout 300 '${pkgs.swaylock}/bin/swaylock -f -i eDP-1:${builtins.elemAt config.kw.wallpapers 0} \
timeout 600 'swaymsg "output * dpms off"' \
resume 'swaymsg "output * dpms on"' \
before-sleep '${pkgs.swaylock}/bin/swaylock -f -i eDP-1:${builtins.elemAt config.kw.wallpapers 0}'
'';
RestartSec = 3;
Restart = "always";
};
Install = { WantedBy = [ "sway-session.target" ]; };
};
}

View file

@ -0,0 +1,19 @@
{ config, lib, kw, ... }: with lib; {
config = {
deploy.targets.personal = {
tf = {
resources.yule = {
provider = "null";
type = "resource";
connection = {
port = head config.network.nodes.yule.services.openssh.ports;
host = config.network.nodes.yule.network.addresses.private.ipv4.address;
};
};
};
};
network.nodes.yule = {
imports = kw.nodeImport "yule";
};
};
}

View file

@ -0,0 +1,93 @@
{ meta, config, pkgs, lib, ... }:
with lib;
{
# Imports
imports = with meta; [
profiles.hardware.v330-14arr
profiles.gui
users.kat.guiFull
services.netdata
services.nginx
services.node-exporter
services.promtail
services.restic
services.zfs
];
# File Systems and Swap
boot.supportedFilesystems = singleton "zfs";
fileSystems = {
"/" = {
device = "rpool/safe/root";
fsType = "zfs";
};
"/home" = {
device = "rpool/safe/home";
fsType = "zfs";
};
"/nix" = {
device = "rpool/local/nix";
fsType = "zfs";
};
"/boot" = {
device = "/dev/disk/by-uuid/2552-18F2";
fsType = "vfat";
};
};
swapDevices = [{ device = "/dev/disk/by-uuid/87ff4f68-cc00-494e-8eba-050469c3bf03"; }];
# Bootloader
boot.loader = {
systemd-boot.enable = true;
efi.canTouchEfiVariables = true;
};
# Networking
networking = {
hostId = "dddbb888";
hostName = "yule";
useDHCP = false;
wireless.interfaces = singleton "wlp2s0";
interfaces = {
wlp2s0.ipv4.addresses = singleton {
inherit (config.network.addresses.private.ipv4) address;
prefixLength = 24;
};
};
defaultGateway = config.network.privateGateway;
};
network = {
addresses = {
private = {
ipv4.address = "192.168.1.3";
};
};
yggdrasil = {
enable = true;
pubkey = "9779fd6b5bdba6b9e0f53c96e141f4b11ce5ef749d1b9e77a759a3fdbd33a653";
listen.enable = false;
listen.endpoints = [ "tcp://0.0.0.0:0" ];
};
};
# Firewall
network.firewall = {
public.interfaces = [ "enp1s0" "wlp2s0" ];
private.interfaces = singleton "yggdrasil";
};
# State
system.stateVersion = "20.09";
}

View file

@ -0,0 +1,15 @@
{ sources, ... }:
{
disabledModules = [ "programs/vim.nix" ];
imports = [
(import (sources.arcexprs + "/modules")).home-manager
(import (sources.katexprs + "/modules")).home
./vim.nix
./fvwm.nix
./deploy.nix
./theme.nix
./secrets.nix
(sources.tf-nix + "/modules/home/secrets.nix")
];
}

View file

@ -0,0 +1,39 @@
{ config, lib, ... }:
/*
This module:
* Provides in-scope TF config for home-manager.
*/
with lib;
let
cfg = config.deploy.tf;
unmergedValues = types.mkOptionType {
name = "unmergedValues";
merge = loc: defs: map (def: def.value) defs;
};
in
{
options.deploy.tf = mkOption {
type = types.submodule {
freeformType = types.attrsOf unmergedValues;
options = {
attrs = mkOption {
type = types.listOf types.str;
default = [ ];
};
out.set = mkOption { type = types.unspecified; };
};
};
};
config = {
deploy.tf = {
attrs = [ "out" "attrs" ];
out.set = removeAttrs cfg cfg.attrs;
};
};
}

View file

@ -0,0 +1,9 @@
{ config, lib, ... }:
with lib;
{
options.programs.fvwm = {
enable = mkEnableOption "Enable FVWM";
};
}

View file

@ -0,0 +1,15 @@
{ config, lib, ... }:
with lib;
{
options.kw = {
secrets = mkOption {
type = types.nullOr (types.listOf types.str);
default = null;
};
};
config = mkIf (config.kw.secrets != null) {
deploy.tf.variables = genAttrs config.kw.secrets (n: { externalSecret = true; });
};
}

View file

@ -0,0 +1,32 @@
{ config, lib, ... }:
/*
This module:
* provides a central way to change the font my system uses.
*/
with lib;
let cfg = config.kw; in {
options.kw = {
wallpapers = mkOption {
type = types.nullOr (types.listOf types.path);
default = [ ../../users/kat/sway/wallpapers/left.jpg ../../users/kat/sway/wallpapers/main.png ../../users/kat/sway/wallpapers/right.jpg ];
};
font = {
name = mkOption {
type = types.str;
default = "Cozette";
};
size = mkOption {
type = types.float;
default = 9.0;
};
size_css = mkOption {
type = types.str;
default = "${toString (cfg.font.size + 3)}px";
};
};
};
}

201
config/modules/home/vim.nix Normal file
View file

@ -0,0 +1,201 @@
{ config, lib, pkgs, ... }:
/*
This module:
* is from an unmerged PR from home-manager.
See: https://github.com/nix-community/home-manager/pull/1745
*/
with lib;
let
cfg = config.programs.vim;
defaultPlugins = [ pkgs.vimPlugins.vim-sensible ];
knownSettings = {
background = types.enum [ "dark" "light" ];
backupdir = types.listOf types.str;
copyindent = types.bool;
directory = types.listOf types.str;
expandtab = types.bool;
hidden = types.bool;
history = types.int;
ignorecase = types.bool;
modeline = types.bool;
mouse = types.enum [ "n" "v" "i" "c" "h" "a" "r" ];
mousefocus = types.bool;
mousehide = types.bool;
mousemodel = types.enum [ "extend" "popup" "popup_setpos" ];
number = types.bool;
relativenumber = types.bool;
shiftwidth = types.int;
smartcase = types.bool;
tabstop = types.int;
undodir = types.listOf types.str;
undofile = types.bool;
};
vimSettingsType = types.submodule {
options =
let
opt = name: type:
mkOption {
type = types.nullOr type;
default = null;
visible = false;
};
in
mapAttrs opt knownSettings;
};
setExpr = name: value:
let
v =
if isBool value then
(if value then "" else "no") + name
else
"${name}=${
if isList value then concatStringsSep "," value else toString value
}";
in
optionalString (value != null) ("set " + v);
plugins =
let
vpkgs = pkgs.vimPlugins;
getPkg = p:
if isDerivation p then
[ p ]
else
optional (isString p && hasAttr p vpkgs) vpkgs.${p};
in
concatMap getPkg cfg.plugins;
in
{
options = {
programs.vim = {
enable = mkEnableOption "Vim";
package = mkOption {
type = types.package;
default = pkgs.vim_configurable;
defaultText = literalExample "pkgs.vim_configurable";
description = "The package to use for the vim binary.";
};
finalPackage = mkOption {
type = types.package;
visible = false;
readOnly = true;
description = "Resulting customized vim package.";
};
plugins = mkOption {
type = with types; listOf (either str package);
default = defaultPlugins;
example = literalExample "[ pkgs.vimPlugins.YankRing ]";
description = ''
List of vim plugins to install. To get a list of supported plugins run:
<command>nix-env -f '&lt;nixpkgs&gt;' -qaP -A vimPlugins</command>.
</para><para>
Note: String values are deprecated, please use actual packages.
'';
};
settings = mkOption {
type = vimSettingsType;
default = { };
example = literalExample ''
{
expandtab = true;
history = 1000;
background = "dark";
}
'';
description = ''
At attribute set of Vim settings. The attribute names and
corresponding values must be among the following supported
options.
<informaltable frame="none"><tgroup cols="1"><tbody>
${concatStringsSep "\n" (mapAttrsToList (n: v: ''
<row>
<entry><varname>${n}</varname></entry>
<entry>${v.description}</entry>
</row>
'') knownSettings)}
</tbody></tgroup></informaltable>
See the Vim documentation for detailed descriptions of these
options. Note, use <varname>extraConfig</varname> to
manually set any options not listed above.
'';
};
extraConfig = mkOption {
type = types.lines;
default = "";
example = ''
set nocompatible
set nobackup
'';
description = "Custom .vimrc lines";
};
};
};
config = (
let
customRC = ''
${concatStringsSep "\n" (filter (v: v != "") (mapAttrsToList setExpr
(builtins.intersectAttrs knownSettings cfg.settings)))}
${cfg.extraConfig}
'';
vim = cfg.package.customize {
name = "vim";
vimrcConfig = {
inherit customRC;
packages.home-manager.start = plugins;
};
};
in
mkIf cfg.enable {
assertions =
let
packagesNotFound =
filter (p: isString p && (!hasAttr p pkgs.vimPlugins)) cfg.plugins;
in
[{
assertion = packagesNotFound == [ ];
message = "Following VIM plugin not found in pkgs.vimPlugins: ${
concatMapStringsSep ", " (p: ''"${p}"'') packagesNotFound
}";
}];
warnings =
let stringPlugins = filter isString cfg.plugins;
in
optional (stringPlugins != [ ]) ''
Specifying VIM plugins using strings is deprecated, found ${
concatMapStringsSep ", " (p: ''"${p}"'') stringPlugins
} as strings.
'';
home.packages = [ cfg.finalPackage ];
programs.vim = {
finalPackage = vim;
plugins = defaultPlugins;
};
}
);
}

View file

@ -0,0 +1,9 @@
{ ... }:
{
imports = [
./imports.nix
./deploy.nix
./network.nix
];
}

View file

@ -0,0 +1,122 @@
{ sources, config, pkgs, lib, ... }:
/*
This module:
* makes tf-nix a part of the meta config
* handles the trusted import for tf-nix
* provides the target interface
* imports the per-host TF config for each target
*/
with lib;
let
cfg = config.deploy;
meta = config;
tfModule = { lib, ... }: with lib; {
config._module.args = {
pkgs = mkDefault pkgs;
};
};
tfType = types.submoduleWith {
modules = [
tfModule
"${toString sources.tf-nix}/modules"
./secrets.nix
];
};
in {
imports = [
(toString (sources.tf-nix + "/modules/run.nix"))
] ++ (optional (builtins.pathExists ../../trusted/tf/tf.nix) (../../trusted/tf/tf.nix));
options = {
deploy = {
dataDir = mkOption {
type = types.path;
};
local = {
isRoot = mkOption {
type = types.bool;
default = builtins.getEnv "HOME_UID" == "0";
};
hostName = mkOption {
type = types.nullOr types.str;
default = let
hostName = builtins.getEnv "HOME_HOSTNAME";
in if hostName == "" then null else hostName;
};
};
targets = let
type = types.submodule ({ config, name, ... }: {
options = {
enable = mkEnableOption "Enable the target" // { default = true; };
name = mkOption {
type = types.str;
default = name;
};
nodeNames = mkOption {
type = types.listOf types.str;
default = [ ];
};
tf = mkOption {
type = tfType;
default = { };
};
};
config.tf = mkMerge (singleton {
imports = [
../../targets/common
];
deps = {
select.allProviders = true;
enable = true;
};
terraform = {
version = "1.0";
logPath = cfg.dataDir + "/terraform-${config.name}.log";
dataDir = cfg.dataDir + "/tfdata/${config.name}";
environment.TF_CLI_ARGS_apply = "-backup=-";
environment.TF_CLI_ARGS_taint = "-backup=-";
};
state = {
file = cfg.dataDir + "/terraform-${config.name}.tfstate";
};
runners = {
lazy = {
inherit (meta.runners.lazy) file args;
attrPrefix = "deploy.targets.${name}.tf.runners.run.";
};
run = {
apply.name = "${name}-apply";
terraform.name = "${name}-tf";
};
};
continue.envVar = "TF_NIX_CONTINUE_${replaceStrings [ "-" ] [ "_" ] config.name}";
} ++ map (nodeName: mapAttrs (_: mkMerge) meta.network.nodes.${nodeName}.deploy.tf.out.set) config.nodeNames);
});
in mkOption {
type = types.attrsOf type;
default = { };
};
};
};
config = {
deploy.targets = let
nodeNames = attrNames config.network.nodes;
targets = config.deploy.targets;
explicitlyDefinedHosts = concatLists (mapAttrsToList (targetName: target: remove targetName target.nodeNames) config.deploy.targets);
in genAttrs nodeNames ( nodeName: {
enable = mkDefault (! elem nodeName explicitlyDefinedHosts);
nodeNames = singleton nodeName;
});
runners = {
run = mkMerge (mapAttrsToList (targetName: target: mapAttrs' (k: run:
nameValuePair run.name run.set
) target.tf.runners.run) (filterAttrs (_: v: v.enable) cfg.targets));
lazy.run = mkMerge (mapAttrsToList (targetName: target: mapAttrs' (k: run:
nameValuePair run.name run.set
) target.tf.runners.lazy.run) (filterAttrs (_: v: v.enable) cfg.targets));
};
};
}

View file

@ -0,0 +1,41 @@
{ config, lib, profiles, root, ... }:
with lib;
{
options = {
lib = mkOption {
type = types.attrsOf (types.attrsOf types.unspecified);
};
network.importing = {
nixosImports = mkOption {
type = types.listOf types.str;
};
homeImports = mkOption {
type = types.listOf types.str;
};
users = mkOption {
type = types.listOf types.str;
};
};
};
config = {
network.importing = {
nixosImports = mkDefault (map (path: toString path) [
(root + "/config/hosts/HN/nixos.nix")
(root + "/config/trusted/hosts/HN/nixos.nix")
]);
homeImports = mkDefault (map (path: toString path) [
(root + "/config/hosts/HN/home.nix")
(root + "/config/trusted/hosts/HN/home.nix")
]);
users = mkDefault (singleton "kat");
};
lib.kw.nodeImport = hostName: lib.nodeImport {
inherit (config.network.importing) nixosImports homeImports users;
inherit profiles hostName;
};
_module.args = { inherit (config.lib) kw; };
};
}

View file

@ -0,0 +1,66 @@
{ pkgs, sources, lib, meta, config, ... }:
/*
This module:
* Makes hosts nixosModules.
* Manages module imports and specialArgs.
* Builds network.nodes.
*/
with lib;
{
options.network = {
nixos = {
extraModules = mkOption {
type = types.listOf types.unspecified;
default = [ ];
};
specialArgs = mkOption {
type = types.attrsOf types.unspecified;
default = { };
};
modulesPath = mkOption {
type = types.path;
default = toString (pkgs.path + "/nixos/modules");
};
};
nodes = let
nixosModule = { name, config, meta, modulesPath, lib, ... }: with lib; {
config = {
nixpkgs = {
system = mkDefault pkgs.system;
pkgs = mkDefault pkgs;
};
};
};
nixosType = let
baseModules = import (config.network.nixos.modulesPath + "/module-list.nix");
in types.submoduleWith {
modules = baseModules
++ singleton nixosModule
++ config.network.nixos.extraModules;
specialArgs = {
inherit baseModules;
inherit (config.network.nixos) modulesPath;
} // config.network.nixos.specialArgs;
};
in mkOption {
type = types.attrsOf nixosType;
default = { };
};
};
config.network = {
nixos = {
extraModules = [
"${toString sources.home-manager}/nixos"
../../modules/nixos
];
specialArgs = {
inherit (config.network) nodes;
inherit sources meta;
};
};
};
}

View file

@ -0,0 +1,30 @@
{ config, lib, ... }:
with lib;
{
options = let tf = config; in {
variables = mkOption {
type = types.attrsOf (types.submodule ({ name, config, ... }: {
options.externalSecret = mkEnableOption "Is ths secret to be templated into a command provided?";
config = mkIf config.externalSecret {
type = "string";
value.shellCommand = "${tf.commandPrefix} ${tf.folderPrefix}${tf.folderDivider}${escapeShellArg name}";
sensitive = true;
};
}));
};
commandPrefix = mkOption {
type = types.nullOr types.str;
default = null;
};
folderPrefix = mkOption {
type = types.str;
default = "";
};
folderDivider = mkOption {
type = types.str;
default = "";
};
};
}

View file

@ -0,0 +1,25 @@
{ meta, sources, lib, ... }:
{
imports =
[
(import (sources.arcexprs + "/modules")).nixos
(import (sources.katexprs + "/modules")).nixos
./deploy.nix
./dyndns.nix
./secrets.nix
(sources.tf-nix + "/modules/nixos/secrets.nix")
(sources.tf-nix + "/modules/nixos/secrets-users.nix")
(sources.hexchen + "/modules/network/yggdrasil")
];
options.hexchen.dns = lib.mkOption { };
options.hexchen.deploy = lib.mkOption { };
/*
This maps hosts to network.nodes from the meta config. This is required for hexchen's yggdrasil network module.
*/
config = {
_module.args.hosts = lib.mapAttrs (_: config: { inherit config; } ) meta.network.nodes;
};
}

View file

@ -0,0 +1,83 @@
{ tf, target, name, meta, config, lib, ... }:
/*
This module:
* aliases <hostname>.system.build.toplevel to <hostname>.deploy.system for ease of use.
* marries meta config to NixOS configs for each host.
* provides in-scope TF config in NixOS and home-manager, instead of only as a part of meta config.
*/
with lib;
let
cfg = config.deploy;
unmergedValues = types.mkOptionType {
name = "unmergedValues";
merge = loc: defs: map (def: def.value) defs;
};
in
{
options.deploy = {
targetName = mkOption {
type = types.nullOr types.str;
default = null;
};
system = mkOption {
type = types.unspecified;
readOnly = true;
};
};
options.deploy.tf = mkOption {
type = types.submodule {
freeformType = types.attrsOf unmergedValues;
options = {
import = mkOption {
type = types.attrsOf types.unspecified;
default = [ ];
};
imports = mkOption {
type = types.listOf types.str;
description = "Other targets to depend on";
default = [ ];
};
attrs = mkOption {
type = types.listOf types.str;
default = [ ];
};
out.set = mkOption { type = types.unspecified; };
};
};
};
config = {
deploy = {
system = config.system.build.toplevel;
targetName = let targetsList = attrNames ( filterAttrs (_: target: target.enable && elem name target.nodeNames) meta.deploy.targets ); in
if (builtins.length targetsList == 0) then null
else lib.warnIf (builtins.length targetsList > 1) "The host ${name} is assigned to several targets: ${concatMapStrings (x: "${x},") targetsList}." (head targetsList);
};
deploy.tf = mkMerge (singleton (lib.mkIf (config.deploy.targetName != null) {
attrs = [ "import" "imports" "out" "attrs" ];
import = genAttrs cfg.tf.imports (target: meta.deploy.targets.${target}.tf);
out.set = removeAttrs cfg.tf cfg.tf.attrs;
deploy.systems.${config.networking.hostName} =
with tf.resources; {
isRemote =
(config.networking.hostName != builtins.getEnv "HOME_HOSTNAME");
nixosConfig = config;
connection = tf.resources.${config.networking.hostName}.connection.set;
triggers.copy.${config.networking.hostName} =
tf.resources.${config.networking.hostName}.refAttr "id";
triggers.secrets.${config.networking.hostName} =
tf.resources.${config.networking.hostName}.refAttr "id";
};
}) ++ mapAttrsToList
(_: user:
mapAttrs (_: mkMerge) user.deploy.tf.out.set)
config.home-manager.users);
_module.args.target = mapNullable (targetName: meta.deploy.targets.${targetName}) cfg.targetName;
_module.args.tf = mapNullable (target: target.tf) target;
};
}

View file

@ -0,0 +1,54 @@
{ config, pkgs, lib, tf, ... }:
with lib;
{
options = {
network.dns.dynamic = mkEnableOption "Enable Glauca Dynamic DNS Updater";
};
config = mkIf (config.network.dns.dynamic) {
kw.secrets = [
"hexdns-key"
"hexdns-secret"
"hexdns-host"
];
secrets.files.kat-glauca-dns = {
text = ''
user="${tf.variables.hexdns-key.ref}"
pass="${tf.variables.hexdns-secret.ref}"
hostname="${tf.variables.hexdns-host.ref}"
'';
};
systemd.services.kat-glauca-dns =
let updater = pkgs.writeShellScriptBin "glauca-dyndns" ''
#!/usr/bin/env bash
set -eu
ip4=$(${pkgs.curl}/bin/curl -s --ipv4 https://dns.glauca.digital/checkip)
ip6=$(${pkgs.curl}/bin/curl -s --ipv6 https://dns.glauca.digital/checkip)
source $passFile
echo "$ip4, $ip6"
${pkgs.curl}/bin/curl -u ''${user}:''${pass} --data-urlencode "hostname=''${hostname}" --data-urlencode "myip=''${ip4}" "https://dns.glauca.digital/nic/update"
echo ""
${pkgs.curl}/bin/curl -u ''${user}:''${pass} --data-urlencode "hostname=''${hostname}" --data-urlencode "myip=''${ip6}" "https://dns.glauca.digital/nic/update"
''; in {
serviceConfig = {
ExecStart = "${updater}/bin/glauca-dyndns";
};
environment = { passFile = config.secrets.files.kat-glauca-dns.path; };
wantedBy = [ "default.target" ];
};
systemd.timers.kat-glauca-dns = {
timerConfig = {
Unit = "kat-glauca-dns.service";
OnBootSec = "5m";
OnUnitActiveSec = "1h";
};
wantedBy = [ "default.target" ];
};
};
}

View file

@ -0,0 +1,15 @@
{ config, lib, ... }:
with lib;
{
options.kw = {
secrets = mkOption {
type = types.nullOr (types.listOf types.str);
default = null;
};
};
config = mkIf (config.kw.secrets != null) {
deploy.tf.variables = genAttrs config.kw.secrets (n: { externalSecret = true; });
};
}

View file

@ -0,0 +1,18 @@
{ config, lib, pkgs, ... }:
{
security.sudo.wheelNeedsPassword = lib.mkForce false;
users.users.root = {
hashedPassword =
"$6$i28yOXoo$/WokLdKds5ZHtJHcuyGrH2WaDQQk/2Pj0xRGLgS8UcmY2oMv3fw2j/85PRpsJJwCB2GBRYRK5LlvdTleHd3mB.";
openssh.authorizedKeys.keys = with pkgs.lib;
concatLists (mapAttrsToList
(name: user:
if elem "wheel" user.extraGroups then
user.openssh.authorizedKeys.keys
else
[ ])
config.users.users);
};
}

View file

@ -0,0 +1,10 @@
{ config, ... }:
{
base16 = {
console = {
enable = true;
scheme = "rebecca.rebecca";
};
};
}

View file

@ -0,0 +1,23 @@
{ config, meta, lib, pkgs, ... }:
{
imports = with meta; [
users.kat.base
# users.kairi.base TODO
users.arc
users.hexchen
./system.nix
./dns.nix
./home.nix
./profiles.nix
./shell.nix
./base16.nix
./net.nix
./access.nix
./locale.nix
./nix.nix
./ssh.nix
./packages.nix
./secrets.nix
];
}

View file

@ -0,0 +1,8 @@
{ config, ... }:
{
network.dns = {
email = "kat@kittywit.ch";
tld = "kittywit.ch.";
};
}

View file

@ -0,0 +1,23 @@
{ meta, config, lib, sources, tf, ... }:
with lib;
{
options.home-manager.users = mkOption {
type = types.attrsOf (types.submoduleWith {
modules = singleton ../../modules/home;
specialArgs = {
inherit sources tf meta;
superConfig = config;
modulesPath = sources.home-manager + "/modules";
};
});
};
config = {
home-manager = {
useUserPackages = true;
useGlobalPkgs = true;
};
};
}

View file

@ -0,0 +1,13 @@
{ config, pkgs, ... }:
{
fonts.fonts = [
pkgs.tamzen
];
i18n.defaultLocale = "en_GB.UTF-8";
time.timeZone = "Europe/London";
console = {
packages = [ pkgs.tamzen ];
keyMap = "uk";
};
}

View file

@ -0,0 +1,7 @@
{ config, lib, ... }:
{
network.nftables.enable = lib.mkDefault true;
network.enable = true;
network.dns.enable = true;
}

View file

@ -0,0 +1,31 @@
{ config, lib, pkgs, sources, ... }:
{
boot.loader.grub.configurationLimit = 8;
boot.loader.systemd-boot.configurationLimit = 8;
environment.systemPackages = [
(pkgs.writeShellScriptBin "nixFlakes" ''
exec ${pkgs.nixUnstable}/bin/nix --experimental-features "nix-command flakes" "$@"
'')
];
nix = {
nixPath = [
"nixpkgs=${sources.nixpkgs}"
"nur=${sources.nur}"
"arc=${sources.arcexprs}"
"ci=${sources.ci}"
];
sandboxPaths = [
"/var/run/nscd/socket"
];
binaryCaches = [ "https://arc.cachix.org" "https://kittywitch.cachix.org" ];
binaryCachePublicKeys =
[ "arc.cachix.org-1:DZmhclLkB6UO0rc0rBzNpwFbbaeLfyn+fYccuAy7YVY=" "kittywitch.cachix.org-1:KIzX/G5cuPw5WgrXad6UnrRZ8UDr7jhXzRTK/lmqyK0=" ];
autoOptimiseStore = true;
gc.automatic = lib.mkDefault true;
gc.options = lib.mkDefault "--delete-older-than 1w";
trustedUsers = [ "root" "@wheel" ];
};
}

View file

@ -0,0 +1,12 @@
{ config, lib, pkgs, ... }:
{
environment.systemPackages = with pkgs; [
smartmontools
hddtemp
lm_sensors
cachix
pinentry-curses
gnupg
];
}

View file

@ -0,0 +1,55 @@
{ config, lib, ... }:
with lib;
{
options = {
deploy.profile = {
gui = mkEnableOption "Graphical System";
vfio = mkEnableOption "VFIO";
trusted = mkEnableOption "Trusted Submodule";
hardware = {
acs-override = mkEnableOption "ACS IOMMU Override";
amdgpu = mkEnableOption "AMD GPU";
hcloud-imperative = mkEnableOption "Imperative Hetzner Cloud Setup";
intel = mkEnableOption "Intel CPU";
laptop = mkEnableOption "Laptop";
wifi = mkEnableOption "WiFi, home network";
ryzen = mkEnableOption "AMD Ryzen CPU";
ms-7b86 = mkEnableOption "MSI B450-A Pro Max";
rm-310 = mkEnableOption "Intel DQ67OW";
eeepc-1015pem = mkEnableOption "Asus Eee PC 1015PEM";
v330-14arr = mkEnableOption "Lenovo Ideapad v330-14ARR";
};
};
home-manager.users = mkOption {
type = types.attrsOf (types.submoduleWith {
modules = [
({ superConfig, ... }: {
options.deploy.profile = {
gui = mkEnableOption "Graphical System";
vfio = mkEnableOption "VFIO";
trusted = mkEnableOption "Trusted Submodule";
hardware = {
acs-override = mkEnableOption "ACS IOMMU Override";
amdgpu = mkEnableOption "AMD GPU";
hcloud-imperative = mkEnableOption "Imperative Hetzner Cloud Setup";
intel = mkEnableOption "Intel CPU";
laptop = mkEnableOption "Laptop";
wifi = mkEnableOption "WiFi, home network";
ryzen = mkEnableOption "AMD Ryzen CPU";
ms-7b86 = mkEnableOption "MSI B450-A Pro Max";
rm-310 = mkEnableOption "Intel DQ67OW";
eeepc-1015pem = mkEnableOption "Asus Eee PC 1015PEM";
v330-14arr = mkEnableOption "Lenovo Ideapad v330-14ARR";
};
};
config = {
deploy.profile = superConfig.deploy.profile;
};
})
];
});
};
};
}

View file

@ -0,0 +1,9 @@
{ config, lib, pkgs, ... }:
{
secrets = {
root = "/var/lib/kat/secrets";
persistentRoot = "/var/lib/kat/secrets";
external = true;
};
}

View file

@ -0,0 +1,8 @@
{ config, ... }:
{
programs.zsh = {
enable = true;
enableCompletion = true;
};
}

View file

@ -0,0 +1,36 @@
{ config, lib, pkgs, ... }:
with lib;
{
network.firewall = {
public = {
tcp.ports = singleton 62954;
udp.ranges = [{
from = 60000;
to = 61000;
}];
};
private = {
tcp.ports = singleton 62954;
udp.ranges = [{
from = 60000;
to = 61000;
}];
};
};
services.openssh = {
enable = true;
ports = lib.mkDefault [ 62954 ];
passwordAuthentication = false;
challengeResponseAuthentication = false;
permitRootLogin = lib.mkDefault "prohibit-password";
kexAlgorithms = [ "curve25519-sha256@libssh.org" ];
extraConfig = ''
StreamLocalBindUnlink yes
LogLevel VERBOSE
'';
};
programs.mosh.enable = true;
}

View file

@ -0,0 +1,16 @@
{ config, lib, pkgs, ... }:
{
boot.kernelPackages = lib.mkDefault pkgs.linuxPackages_latest;
hardware.enableRedistributableFirmware = lib.mkDefault true;
boot.tmpOnTmpfs = true;
boot.zfs.enableUnstable = true;
boot.kernel.sysctl = {
"net.core.rmem_max" = "16777216";
"net.core.wmem_max" ="16777216";
"net.ipv4.tcp_rmem" = "4096 87380 16777216";
"net.ipv4.tcp_wmem" = "4096 65536 16777216";
};
services.journald.extraConfig = "SystemMaxUse=512M";
users.mutableUsers = false;
}

View file

@ -0,0 +1,6 @@
{ config, ... }:
{
programs.adb.enable = true;
users.users.kat.extraGroups = [ "adbusers" ];
}

View file

@ -0,0 +1,24 @@
{ config, pkgs, ... }:
{
imports = [
./adb.nix
./fonts.nix
./sway.nix
./fvwm.nix
./filesystems.nix
./gpg.nix
./xdg-portals.nix
./dns.nix
./nfs.nix
./nix-doc.nix
./mpd.nix
./nixpkgs.nix
./mingetty.nix
./sound.nix
];
services.tumbler.enable = true;
deploy.profile.gui = true;
}

View file

@ -0,0 +1,37 @@
{ config, lib, pkgs, ... }: {
networking = {
# networkmanager.enable = true;
resolvconf.useLocalResolver = true;
networkmanager.dns = "none";
};
services.dnscrypt-proxy2 = {
enable = true;
settings = {
ipv6_servers = true;
require_dnssec = true;
sources.public-resolvers = {
urls = [
"https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/public-resolvers.md"
"https://download.dnscrypt.info/resolvers-list/v3/public-resolvers.md"
];
cache_file = "/var/lib/dnscrypt-proxy2/public-resolvers.md";
minisign_key =
"RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3";
};
# You can choose a specific set of servers from https://github.com/DNSCrypt/dnscrypt-resolvers/blob/master/v2/public-resolvers.md
server_names = [
"acsacsar-ams-ipv4"
"acsacsar-ams-ipv6"
"dnscrypt.eu-dk"
"dnscrypt.eu-dk-ipv6"
"dnscrypt.eu-nl"
"dnscrypt.eu-nl-ipv6"
"meganerd"
"meganerd-ipv6"
];
};
};
}

View file

@ -0,0 +1,5 @@
{ config, pkgs, ... }:
{
environment.systemPackages = with pkgs; [ ntfs3g exfat-utils ];
}

View file

@ -0,0 +1,27 @@
{ config, pkgs, lib, ... }:
{
fonts = {
enableDefaultFonts = true;
fontDir.enable = true;
fontconfig = {
enable = true;
allowBitmaps = true;
defaultFonts = {
emoji = [
"Twitter Color Emoji"
];
};
};
};
fonts.fonts = with pkgs; [
font-awesome
nerdfonts
vegur
creep
cozette
emacs-all-the-icons-fonts
twitter-color-emoji
];
}

View file

@ -0,0 +1,14 @@
{ config, lib, ... }:
with lib;
{
config = mkIf (any (user: user.programs.fvwm.enable) (attrValues config.home-manager.users)) {
services.xserver.enable = true;
services.xserver.displayManager.startx.enable = true;
services.xserver.windowManager.fvwm = {
enable = true;
gestures = true;
};
};
}

View file

@ -0,0 +1,12 @@
{ config, pkgs, lib, ... }:
{
services.pcscd.enable = true;
services.udev.packages = [ pkgs.yubikey-personalization ];
programs.gnupg.agent = {
enable = true;
enableSSHSupport = true;
pinentryFlavor = "gtk2";
};
}

View file

@ -0,0 +1,46 @@
{ config, lib, pkgs, ... }:
let
c1 = "\\e[22;34m";
c2 = "\\e[1;35m";
nixos = [
" ${c1} ::::. ${c2}'::::: ::::' "
" ${c1} '::::: ${c2}':::::. ::::' "
" ${c1} ::::: ${c2}'::::.::::: "
" ${c1} .......:::::..... ${c2}:::::::: "
" ${c1} ::::::::::::::::::. ${c2}:::::: ${c1}::::. "
" ${c1} ::::::::::::::::::::: ${c2}:::::. ${c1}.::::' "
" ${c2} ..... ::::' ${c1}:::::' "
" ${c2} ::::: '::' ${c1}:::::' "
" ${c2} ........::::: ' ${c1}:::::::::::. "
" ${c2}::::::::::::: ${c1}::::::::::::: "
" ${c2} ::::::::::: ${c1}.. ${c1}::::: "
" ${c2} .::::: ${c1}.::: ${c1}::::: "
" ${c2} .::::: ${c1}::::: ${c1}''''' ${c2}..... "
" ${c2} ::::: ${c1}':::::. ${c2}......:::::::::::::' "
" ${c2} ::: ${c1}::::::. ${c2}':::::::::::::::::' "
" ${c1} .:::::::: ${c2}':::::::::: "
" ${c1} .::::''::::. ${c2}'::::. "
" ${c1} .::::' ::::. ${c2}'::::. "
" ${c1} .:::: :::: ${c2}'::::. "
];
in
{
console = {
font = "Tamzen7x14";
earlySetup = true;
getty = {
greetingPrefix =
''\e[H\e[2J'' + # topleft
''\e[9;10]''; # setterm blank/powersave = 10 minutes
greeting =
"\n" +
lib.concatStringsSep "\n" nixos +
"\n\n" +
''\e[1;32m>>> NixOS ${config.system.nixos.label} (Linux \r) - \l\e[0m'';
};
};
services.getty = {
helpLine = lib.mkForce "";
};
}

View file

@ -0,0 +1,10 @@
{ config, lib, ... }:
with lib;
{
network.firewall = {
public.tcp.ports = [ 6600 32101 ];
private.tcp.ports = [ 6600 32101 ];
};
}

View file

@ -0,0 +1,29 @@
{ config, lib, meta, ... }:
{
boot.supportedFilesystems = [ "nfs" ];
fileSystems."/mnt/kat-nas" = lib.mkIf (config.networking.hostName != "beltane") {
device = "${meta.network.nodes.beltane.network.addresses.private.domain}:/mnt/zraw/media";
fsType = "nfs";
options = [ "x-systemd.automount" "noauto" "nfsvers=4" "soft" "retrans=2" "timeo=60"];
};
/*
fileSystems."/mnt/hex-corn" = {
device = "storah.net.lilwit.ch:/data/cornbox";
fsType = "nfs";
options = [ "x-systemd.automount" "noauto" ];
};
fileSystems."/mnt/hex-tor" = {
device = "storah.net.lilwit.ch:/data/torrents";
fsType = "nfs";
options = [ "x-systemd.automount" "noauto" ];
};
*/
systemd.services.nfs-mountd = {
wants = [ "network-online.target" "yggdrassil.service" ];
};
}

View file

@ -0,0 +1,11 @@
{ pkgs, ... }:
{
nix.extraOptions = ''
plugin-files = ${pkgs.nix-doc}/lib/libnix_doc_plugin.so
'';
environment.systemPackages = with pkgs; [
nix-doc
];
}

View file

@ -0,0 +1,8 @@
{ pkgs, config, lib, ... }:
{
nixpkgs.config = {
allowUnfree = true;
pulseaudio = true;
};
}

View file

@ -0,0 +1,43 @@
{ config, lib, pkgs, ... }:
{
sound = {
enable = true;
extraConfig = ''
defaults.pcm.rate_converter "speexrate_best"
'';
};
environment.systemPackages = with pkgs; [ pulsemixer ];
security.rtkit.enable = true;
services.pipewire = {
enable = true;
config = {
pipewire = {
"context.properties" = {
"log.level" = 2;
"default.clock.min-quantum" =
32; # default; going lower may cause crackles and distorted audio
};
pipewire-pulse = {
"context.modules" = [{
name = "libpipewire-module-protocol-pulse";
args = {
"pulse.min.quantum" = 32; # controls minimum playback quant
"pulse.min.req" = 32; # controls minimum recording quant
"pulse.min.frag" = 32; # controls minimum fragment size
"server.address" =
[ "unix:native" ]; # the default address of the server
};
}];
};
};
};
pulse.enable = true;
alsa.support32Bit = true;
jack.enable = true;
alsa.enable = true;
};
}

View file

@ -0,0 +1,10 @@
{ config, pkgs, lib, ... }:
with lib;
{
programs.sway = {
enable = any (user: user.wayland.windowManager.sway.enable) (attrValues config.home-manager.users);
extraPackages = with pkgs; mkForce [ xwayland swaylock swayidle ];
};
}

View file

@ -0,0 +1,14 @@
{ config, pkgs, ... }:
{
xdg = {
portal = {
enable = true;
extraPortals = with pkgs; [
xdg-desktop-portal-wlr
xdg-desktop-portal-gtk
];
gtkUsePortal = true;
};
};
}

View file

@ -0,0 +1,10 @@
{ config, pkgs, lib, ... }:
with lib;
{
deploy.profile.hardware.amdgpu = true;
boot.initrd.availableKernelModules = [ "amdgpu" ];
hardware.opengl.extraPackages = with pkgs; [ libvdpau-va-gl vaapiVdpau ];
}

View file

@ -0,0 +1,39 @@
let hardwareProfiles = { lib }:
let profiles = with profiles; lib.domainMerge {
folder = ""; # not used in this usage
folderPaths = [
./.
../../trusted/profiles/hardware
];
} // {
ms-7b86 = {
imports = [
ms-7b86-base
ryzen
amdgpu
];
};
rm-310 = {
imports = [
rm-310-base
intel
];
};
v330-14arr = {
imports = [
v330-14arr-base
ryzen
amdgpu
laptop
wifi
];
};
eeepc-1015pem = {
imports = [
eeepc-1015pem-base
intel
laptop
];
};
}; in profiles;
in { __functor = self: hardwareProfiles; isModule = false; }

View file

@ -0,0 +1,19 @@
{ config, ... }:
{
deploy.profile.hardware.eeepc-1015pem = true;
boot = {
initrd = {
availableKernelModules = [ "uhci_hcd" "ehci_pci" "ahci" "usb_storage" "sd_mod" ];
kernelModules = [ ];
};
kernelModules = [ ];
extraModulePackages = [ ];
kernelParams = [
"usbcore.autosuspend=-1"
"acpi_osi=Linux"
"acpi_enforce_resources=lax"
];
};
}

View file

@ -0,0 +1,14 @@
{ config, lib, sources, ... }:
/*
This hardware profile corresponds with the imperatively provisioned hetzner cloud box.
*/
with lib;
{
deploy.profile.hardware.hcloud-imperative = true;
imports = [ (sources.nixpkgs + "/nixos/modules/profiles/qemu-guest.nix") ];
boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "virtio_pci" "sd_mod" "sr_mod" ];
}

View file

@ -0,0 +1,13 @@
{ config, ... }:
/*
This hardware profile corresponds to any machine which has an Intel processor.
*/
{
deploy.profile.hardware.intel = true;
boot = {
kernelModules = [ "kvm-intel" ];
};
}

View file

@ -0,0 +1,9 @@
{ config, ... }:
{
deploy.profile.hardware.laptop = true;
imports = [
./light.nix
];
}

View file

@ -0,0 +1,5 @@
{ config, lib, pkgs, ... }:
{
programs.light.enable = true;
}

View file

@ -0,0 +1,14 @@
{ config, lib, ... }:
/*
This hardware profile corresponds to the MSI B450-A PRO MAX system.
*/
with lib;
{
deploy.profile.hardware.ms-7b86 = true;
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usbhid" "sd_mod" ];
boot.kernelModules = [ "nct6775" ];
}

View file

@ -0,0 +1,14 @@
{ config, ... }:
/*
This hardware profile corresponds with the RM DESKTOP 310 system, which is actually just an Intel DQ67OW motherboard.
*/
{
deploy.profile.hardware.rm-310 = true;
boot.initrd.availableKernelModules = [ "ata_generic" "ehci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
}

View file

@ -0,0 +1,26 @@
{ config, pkgs, ... }:
/*
This hardware profile corresponds to any machine which has an AMD Ryzen processor.
*/
{
deploy.profile.hardware.ryzen = true;
boot = {
kernelModules = [
"msr"
"ryzen_smu"
"kvm-amd"
];
kernelParams = [ "amd_iommu=on" ];
};
hardware.cpu.amd.updateMicrocode = true;
environment.systemPackages = with pkgs; [
lm_sensors
ryzen-smu-monitor_cpu
ryzen-monitor
];
}

View file

@ -0,0 +1,15 @@
{ config, ... }:
/*
This hardware profile corresponds to the Lenovo IdeaPad v330-14ARR.
*/
{
deploy.profile.hardware.v330-14arr = true;
boot.initrd.availableKernelModules =
[ "xhci_pci" "ahci" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
}

View file

@ -0,0 +1,6 @@
{ config, ... }:
{
deploy.profile.hardware.wifi = true;
networking.wireless.enable = true;
}

View file

@ -0,0 +1,68 @@
{ config, pkgs, lib, ... }:
with lib;
let
win10-screenstub = pkgs.writeShellScriptBin "win10-screenstub" ''
${pkgs.screenstub-kat}/bin/screenstub -c "${./screenstub.yml}" x
'';
win10-diskmapper = pkgs.writeShellScriptBin "win10-diskmapper" ''
sudo ${pkgs.disk-mapper}/bin/disk-mapper /dev/disk/by-id/ata-ST2000DM008-2FR102_WK301C3H-part2
'';
in {
deploy.profile.vfio = true;
environment.systemPackages = with pkgs; [
win10-screenstub
win10-vm
win10-diskmapper
ddcutil
];
users.users.kat.extraGroups = [ "vfio" "input" "uinput" ];
users.groups = { uinput = { }; vfio = { }; };
boot = lib.mkMerge [ {
initrd.kernelModules = mkBefore ["vfio" "vfio_iommu_type1" "vfio_pci" "vfio_virqfd"];
kernelModules = [ "i2c-dev" ]; # i2c-dev is required for DDC/CI for screenstub
kernelPatches = with pkgs.kernelPatches; [
(mkIf config.deploy.profile.hardware.acs-override acs-override)
];
} (mkIf (config.deploy.profile.hardware.amdgpu) {
kernelParams = [
"video=efifb:off"
];
extraModulePackages = [
(pkgs.linuxPackagesFor config.boot.kernelPackages.kernel).vendor-reset
];
}) ( mkIf (config.deploy.profile.hardware.acs-override) {
kernelParams = [
"pci=noats"
"pcie_acs_override=downstream,multifunction"
];
}) ];
environment.etc."qemu/bridge.conf".text = "allow br";
security.wrappers = {
qemu-bridge-helper = {
source = "${pkgs.qemu-vfio}/libexec/qemu-bridge-helper";
};
};
services.udev.extraRules = ''
SUBSYSTEM=="i2c-dev", GROUP="vfio", MODE="0660"
SUBSYSTEM=="misc", KERNEL=="uinput", OPTIONS+="static_node=uinput", MODE="0660", GROUP="uinput"
SUBSYSTEM=="vfio", OWNER="root", GROUP="vfio"
'';
security.pam.loginLimits = [{
domain = "@vfio";
type = "-";
item = "memlock";
value = "unlimited";
}];
systemd.extraConfig = "DefaultLimitMEMLOCK=infinity";
}

View file

@ -0,0 +1,85 @@
screens:
- monitor: # fill in with info from `screenstub detect`
manufacturer: BNQ
model: BenQ GW2270
#serial: "..."
guest_source: # Could be automatically detected, but best to fill in if monitor has more than two inputs
name: HDMI-1
#value: 0x0f # can also specify raw VCP value
host_source: # Usually automatically detected
name: HDMI-2
#value: 0x11
ddc:
#minimal_delay: 100ms # minimum time to wait between switching inputs again
#guest: [] # disable input switching
#host: [] # disable input switching
guest: # configure how to switch to the guest
#- guest_wait # wait until guest agent responds, otherwise might get stranded on other input
- ddc # (default) Use ddc-rs
#exec: ["ddcutil", "-g", "BNQ", "setvcp", "0x60", "{}"]
#- exec: [ddccontrol, -r, "0x60", -w, "{}", /dev/i2c-5]
host: # configure how to switch back from the guest
- ddc # (default) Controls DDC from the host GPU - requires no guest agent but many monitors won't support this
#- exec: ["ddcutil", "-g", "BNQ", "setvcp", "0x60", "{}"]
#- guest_exec: ["C:/ddcset.exe", "setvcp", "60", "{:x}"] # or "0x{:x}" for hex input value
#- guest_exec: ["C:/ScreenBright.exe", "-set", "0x60", "{}"] # "{}" is for decimal input value
#- exec: ["ssh", "user@vm", "ddcutil", "setvcp", "0x60", "{}"] # system commands can also be used
qemu:
#routing: qmp # (default) does not require extra configuration or dependencies
#routing: spice # no external requirements # CURRENTLY UNIMPLEMENTED
#routing: input-linux # requires uinput
routing: virtio-host # requires uinput, recommended for performance, requires vioinput drivers in guest
#driver: ps2 # use PS/2 in the guest for all input devices (absolute mouse mode unsupported)
#driver: usb # use USB keyboard/mouse/tablet in the guest
#driver: virtio # Recommended but vioinput drivers must be installed in guest
#keyboard_driver: ps2 # (default) can also be set separately per input type, this should rarely be necessary
#relative_driver: usb # (default)
#absolute_driver: usb # (default)
#driver: virtio
#relative_driver: virtio
qmp_socket: /tmp/vfio-qmp # path to QMP socket
ga_socket: /tmp/vfio-qga # path to Guest Agent socket
key_remap: # Arbitrary keys can be remapped in the guest
# See https://docs.rs/input-linux/*/input_linux/enum.Key.html for a list of key names available (mouse buttons can also be used)
LeftMeta: Reserved # disable the windows key
RightAlt: LeftMeta # remap right alt to trigger the windows key
hotkeys: # Trigger various events on key combinations
#- toggle_grab:y
# xdevice: # CURRENTLY UNIMPLEMENTED
# devices: # Only grab specific devices from Xorg
# - "..."
#- exec: [echo, hi] # Execute an arbitrary system command
#- show_host # switch to the host display
#- show_guest # switch to the guest display
#- toggle_show # switch the current display
#- unstick_guest # causes all held keys to be released in the guest
#- shutdown # safely shuts the guest system down
#- reboot # reboots the guest
#- exit # quits screenstub
- triggers: [G]
modifiers: [LeftMeta]
events:
- toggle_grab:
x:
mouse: false # Confine input/mouse to window
- toggle_grab:
evdev: # evdev grab is useful for playing games that don't work with absolute mouse events
exclusive: false # grab exclusive access from the device(s)
#new_device_name: "unique-grab-name" # create a new uinput device for this grab
xcore_ignore: [absolute] # which events to ignore from the window (key, button, absolute)
evdev_ignore: [button] # which events to ignore from the evdev device
devices: # List of devices to forward to guest
- /dev/input/by-id/usb-Razer_Razer_Naga_Trinity_00000000001A-event-mouse
- unstick_host # force-depress all Xorg keys (prevents keys getting stuck)
- triggers: [T]
modifiers: [LeftMeta]
on_release: false
events:
- toggle_show
exit_events: # Events to trigger on window close / exit
- show_host
#- shutdown

View file

@ -0,0 +1,41 @@
{ config, pkgs, ... }:
{
services.fail2ban = {
enable = true;
packageFirewall = pkgs.nftables;
banaction = "nftables-multiport";
banaction-allports = "nftables-allports";
jails = {
default = ''
bantime = 7d
blocktype = DROP
action = nftables-allports
logpath = /var/log/auth.log
'';
ssh = ''
enabled = true
filter = sshd
maxretry = 4
action = nftables-multiport[name=SSH, port=ssh, protocol=tcp]
'';
sshd-ddos = ''
enabled = true
filter = sshd-ddos
maxretry = 4
action = nftables-multiport[name=ssh, port=ssh, protocol=tcp]
'';
};
};
environment.etc."fail2ban/filter.d/sshd-ddos.conf" = {
enable = true;
text = ''
[Definition]
failregex = sshd(?:\[\d+\])?: Did not receive identification string from <HOST>$
ignoreregex =
'';
};
systemd.services.fail2ban.serviceConfig.LimitSTACK = 128 * 1024;
}

View file

@ -0,0 +1,17 @@
{ config, lib, pkgs, ... }:
{
services.nginx.virtualHosts = {
"files.${config.network.dns.domain}" = {
root = "/var/www/files";
enableACME = true;
forceSSL = true;
};
};
deploy.tf.dns.records.services_filehost = {
tld = config.network.dns.tld;
domain = "files";
cname.target = "${config.networking.hostName}.${config.network.dns.tld}";
};
}

View file

@ -0,0 +1,51 @@
{ config, pkgs, tf, ... }:
{
deploy.tf.dns.records.services_fusionpbx = {
tld = config.network.dns.tld;
domain = "pbx";
cname.target = "${config.network.addresses.private.domain}.";
};
kw.secrets = [
"fusionpbx-username"
"fusionpbx-password"
];
secrets.files.fusionpbx_env = {
text = ''
USER_NAME=${tf.variables.fusionpbx-username.ref}
USER_PASSWORD=${tf.variables.fusionpbx-password.ref}
'';
owner = "fusionpbx";
group = "fusionpbx";
};
security.acme.certs.services_fusionpbx = {
domain = "pbx.${config.network.dns.domain}";
group = "fusionpbx";
dnsProvider = "rfc2136";
credentialsFile = config.secrets.files.dns_creds.path;
postRun = "systemctl restart nginx";
};
services.fusionpbx = {
enable = true;
openFirewall = true;
useLocalPostgreSQL = true;
environmentFile = config.secrets.files.fusionpbx_env.path;
hardphones = true;
useACMEHost = "services_fusionpbx";
domain = "pbx.${config.network.dns.domain}";
package = with pkgs; fusionpbxWithApps [ fusionpbx-apps.sms ];
freeSwitchPackage = with pkgs; freeswitch;
};
services.nginx.virtualHosts."altar.kittywit.ch" = {
locations = {
"app/sms/hook/" = {
proxyPass = "http://pbx.kittywit.ch/app/sms/hook";
};
};
};
}

View file

@ -0,0 +1,90 @@
{ config, lib, pkgs, tf, ... }:
{
kw.secrets = [
"gitea-mail-pass"
];
secrets.files.gitea-mail-passfile = {
text = ''
${tf.variables.gitea-mail-pass.ref};
'';
owner = "gitea";
group = "gitea";
};
services.postgresql = {
enable = true;
ensureDatabases = [ "gitea" ];
ensureUsers = [{
name = "gitea";
ensurePermissions."DATABASE gitea" = "ALL PRIVILEGES";
}];
};
services.gitea = {
enable = true;
disableRegistration = true;
domain = "git.${config.network.dns.domain}";
rootUrl = "https://git.${config.network.dns.domain}";
httpAddress = "127.0.0.1";
appName = "kittywitch git";
ssh = { clonePort = 62954; };
database = {
type = "postgres";
name = "gitea";
user = "gitea";
};
mailerPasswordFile = config.secrets.files.gitea-mail-passfile.path;
settings = {
security = { DISABLE_GIT_HOOKS = false; };
api = { ENABLE_SWAGGER = true; };
mailer = {
ENABLED = true;
SUBJECT = "%(APP_NAME)s";
HOST = "athame.kittywit.ch:465";
USER = "gitea@kittywit.ch";
#SEND_AS_PLAIN_TEXT = true;
USE_SENDMAIL = false;
FROM = "\"kittywitch git\" <gitea@${config.network.dns.domain}>";
};
service = {
NO_REPLY_ADDRESS = "kittywit.ch";
REGISTER_EMAIL_CONFIRM = true;
ENABLE_NOTIFY_MAIL = true;
};
ui = {
THEMES = "pitchblack,gitea,arc-green";
DEFAULT_THEME = "pitchblack";
THEME_COLOR_META_TAG = "#222222";
};
};
};
systemd.services.gitea.serviceConfig.ExecStartPre = [
"${pkgs.coreutils}/bin/ln -sfT ${pkgs.runCommand "gitea-public" {
} ''
${pkgs.coreutils}/bin/mkdir -p $out/{css,img}
${pkgs.coreutils}/bin/cp ${pkgs.fetchFromGitHub {
owner = "iamdoubz";
repo = "Gitea-Pitch-Black";
rev = "38a10947254e46a0a3c1fb90c617d913d6fe63b9";
sha256 = "1zpmjv0h4k9nf52yaj22zyfabhv83n79f6cj6kfm5s685b2s1348";
}}/theme-pitchblack.css $out/css
${pkgs.coreutils}/bin/cp -r ${./public}/* $out/
''} /var/lib/gitea/custom/public"
"${pkgs.coreutils}/bin/ln -sfT ${./templates} /var/lib/gitea/custom/templates"
];
services.nginx.virtualHosts."git.${config.network.dns.domain}" = {
enableACME = true;
forceSSL = true;
locations = { "/".proxyPass = "http://127.0.0.1:3000"; };
};
deploy.tf.dns.records.services_gitea = {
tld = config.network.dns.tld;
domain = "git";
cname.target = "${config.networking.hostName}.${config.network.dns.tld}";
};
}

View file

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 280 171.19"><defs><style>.cls-1,.cls-3{stroke:#000;stroke-miterlimit:10;}.cls-2{fill:#ccc;}.cls-3,.cls-4{fill:#f2f2f2;}</style></defs><path class="cls-1" d="M403.84,463.72s13.51-20.52,9.51-44.75c0,0-22.62-2.63-38,12.5C361.41,402.18,345.28,369,369.47,345c0,0,8,5.45,8.47,12.71,2,31.08-1.18,31.53-1.18,31.53,25.3-3.77,32.51-45.12,9.89-59.77-55.53-26.12-63.59-4.21-71.3,42.35-3.2,27.07-3.83,41.66-8.19,62.74-13.46-15.72-38.59-14.19-38-14-3.4,25,7.88,45.73,9.45,46.09-15,4.55-37.67,6.71-73,11.28,75.87,17.66,199,11.15,274.58-4.94C468.23,470.85,414.31,471.71,403.84,463.72Z" transform="translate(-202.9 -317.79)"/><polygon class="cls-2" points="98.68 121.45 90.04 110.5 90.44 120.65 75.53 115.4 83.03 130.62 71.94 130.38 81.58 144.37 98.68 121.45"/><path class="cls-3" d="M305.65,436.85" transform="translate(-202.9 -317.79)"/><path class="cls-4" d="M301.65,439.44l-.07-.2a85.43,85.43,0,0,0-6.65-5.65l0,6.41s-6,1.47-10.83-1.78c0,0,7.18,11.33,1.07,11.54a40.37,40.37,0,0,0-5.48.5l4.78,11.9C299.42,459.81,301.81,444.69,301.65,439.44Z" transform="translate(-202.9 -317.79)"/><polygon class="cls-2" points="179.03 119.28 187.68 108.33 187.28 118.47 202.19 113.22 194.69 128.44 205.78 128.21 196.13 142.19 179.03 119.28"/><path class="cls-4" d="M381.87,437.27l.07-.2a83.13,83.13,0,0,1,6.65-5.66l0,6.41s6,1.47,10.83-1.78c0,0-7.18,11.33-1.07,11.55a38.39,38.39,0,0,1,5.48.5L399,460C384.1,457.63,381.71,442.51,381.87,437.27Z" transform="translate(-202.9 -317.79)"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

View file

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 280 171.19"><defs><style>.cls-1,.cls-3{stroke:#000;stroke-miterlimit:10;}.cls-2{fill:#ccc;}.cls-3,.cls-4{fill:#f2f2f2;}</style></defs><path class="cls-1" d="M403.84,463.72s13.51-20.52,9.51-44.75c0,0-22.62-2.63-38,12.5C361.41,402.18,345.28,369,369.47,345c0,0,8,5.45,8.47,12.71,2,31.08-1.18,31.53-1.18,31.53,25.3-3.77,32.51-45.12,9.89-59.77-55.53-26.12-63.59-4.21-71.3,42.35-3.2,27.07-3.83,41.66-8.19,62.74-13.46-15.72-38.59-14.19-38-14-3.4,25,7.88,45.73,9.45,46.09-15,4.55-37.67,6.71-73,11.28,75.87,17.66,199,11.15,274.58-4.94C468.23,470.85,414.31,471.71,403.84,463.72Z" transform="translate(-202.9 -317.79)"/><polygon class="cls-2" points="98.68 121.45 90.04 110.5 90.44 120.65 75.53 115.4 83.03 130.62 71.94 130.38 81.58 144.37 98.68 121.45"/><path class="cls-3" d="M305.65,436.85" transform="translate(-202.9 -317.79)"/><path class="cls-4" d="M301.65,439.44l-.07-.2a85.43,85.43,0,0,0-6.65-5.65l0,6.41s-6,1.47-10.83-1.78c0,0,7.18,11.33,1.07,11.54a40.37,40.37,0,0,0-5.48.5l4.78,11.9C299.42,459.81,301.81,444.69,301.65,439.44Z" transform="translate(-202.9 -317.79)"/><polygon class="cls-2" points="179.03 119.28 187.68 108.33 187.28 118.47 202.19 113.22 194.69 128.44 205.78 128.21 196.13 142.19 179.03 119.28"/><path class="cls-4" d="M381.87,437.27l.07-.2a83.13,83.13,0,0,1,6.65-5.66l0,6.41s6,1.47,10.83-1.78c0,0-7.18,11.33-1.07,11.55a38.39,38.39,0,0,1,5.48.5L399,460C384.1,457.63,381.71,442.51,381.87,437.27Z" transform="translate(-202.9 -317.79)"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,29 @@
<div class="ui secondary pointing tabular top attached borderless menu stackable new-menu navbar">
<a class="item {{if .PageIsAdminDashboard}}acty{{end}}" href="{{AppSubUrl}}/admin">
{{.i18n.Tr "admin.dashboard"}}
</a>
<a class="item {{if .PageIsAdminUsers}}acty{{end}}" href="{{AppSubUrl}}/admin/users">
{{.i18n.Tr "admin.users"}}
</a>
<a class="item {{if .PageIsAdminOrganizations}}acty{{end}}" href="{{AppSubUrl}}/admin/orgs">
{{.i18n.Tr "admin.organizations"}}
</a>
<a class="item {{if .PageIsAdminRepositories}}acty{{end}}" href="{{AppSubUrl}}/admin/repos">
{{.i18n.Tr "admin.repositories"}}
</a>
<a class="item {{if .PageIsAdminHooks}}acty{{end}}" href="{{AppSubUrl}}/admin/hooks">
{{.i18n.Tr "admin.hooks"}}
</a>
<a class="item {{if .PageIsAdminAuthentications}}acty{{end}}" href="{{AppSubUrl}}/admin/auths">
{{.i18n.Tr "admin.authentication"}}
</a>
<a class="item {{if .PageIsAdminConfig}}acty{{end}}" href="{{AppSubUrl}}/admin/config">
{{.i18n.Tr "admin.config"}}
</a>
<a class="item {{if .PageIsAdminNotices}}acty{{end}}" href="{{AppSubUrl}}/admin/notices">
{{.i18n.Tr "admin.notices"}}
</a>
<a class="item {{if .PageIsAdminMonitor}}acty{{end}}" href="{{AppSubUrl}}/admin/monitor">
{{.i18n.Tr "admin.monitor"}}
</a>
</div>

View file

@ -0,0 +1,176 @@
<!DOCTYPE html>
<html>
<head data-suburl="{{AppSubUrl}}">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>{{if .Title}}{{.Title}} - {{end}}{{AppName}}</title>
<link rel="manifest" href="{{AppSubUrl}}/manifest.json">
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('{{AppSubUrl}}/serviceworker.js').then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
});
}
</script>
<meta name="theme-color" content="{{ThemeColorMetaTag}}">
<meta name="author" content="{{if .Repository}}{{.Owner.Name}}{{else}}{{MetaAuthor}}{{end}}" />
<meta name="description" content="{{if .Repository}}{{.Repository.Name}}{{if .Repository.Description}} - {{.Repository.Description}}{{end}}{{else}}{{MetaDescription}}{{end}}" />
<meta name="keywords" content="{{MetaKeywords}}">
<meta name="referrer" content="no-referrer" />
<meta name="_csrf" content="{{.CsrfToken}}" />
<meta name="_suburl" content="{{AppSubUrl}}" />
{{if .IsSigned}}
<meta name="_uid" content="{{.SignedUser.ID}}" />
{{end}}
{{if .ContextUser}}
<meta name="_context_uid" content="{{.ContextUser.ID}}" />
{{end}}
{{if .SearchLimit}}
<meta name="_search_limit" content="{{.SearchLimit}}" />
{{end}}
{{if .GoGetImport}}
<meta name="go-import" content="{{.GoGetImport}} git {{.CloneLink.HTTPS}}">
<meta name="go-source" content="{{.GoGetImport}} _ {{.GoDocDirectory}} {{.GoDocFile}}">
{{end}}
<script>
{{SafeJS `/*
@licstart The following is the entire license notice for the
JavaScript code in this page.
Copyright (c) 2016 The Gitea Authors
Copyright (c) 2015 The Gogs Authors
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, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
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.
---
Licensing information for additional javascript libraries can be found at:
{{AppSubUrl}}/vendor/librejs.html
@licend The above is the entire license notice
for the JavaScript code in this page.
*/`}}
</script>
<link rel="shortcut icon" href="{{AppSubUrl}}/img/favicon.png" />
<link rel="mask-icon" href="{{AppSubUrl}}/img/gitea-safari.svg" color="#609926">
<link rel="preload" href="{{AppSubUrl}}/vendor/assets/font-awesome/css/font-awesome.min.css" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="{{AppSubUrl}}/vendor/assets/font-awesome/css/font-awesome.min.css"></noscript>
<link rel="stylesheet" href="{{AppSubUrl}}/vendor/assets/octicons/octicons.min.css">
{{if .RequireSimpleMDE}}
<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/simplemde/simplemde.min.css">
{{end}}
{{if .RequireGitGraph}}
<!-- graph -->
<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/gitgraph/gitgraph.css">
{{end}}
{{if .RequireTribute}}
<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/tribute/tribute.css">
{{end}}
<!-- Stylesheet -->
<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/semantic/semantic.min.css">
<link rel="stylesheet" href="{{AppSubUrl}}/css/index.css?v={{MD5 AppVer}}">
<noscript>
<style>
.dropdown:hover > .menu { display: block; }
.ui.secondary.menu .dropdown.item > .menu { margin-top: 0; }
</style>
</noscript>
{{if .RequireHighlightJS}}
<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/highlight/github.css">
{{end}}
{{if .RequireMinicolors}}
<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/jquery.minicolors/jquery.minicolors.css">
{{end}}
{{if .RequireDatetimepicker}}
<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/jquery.datetimepicker/jquery.datetimepicker.css">
{{end}}
{{if .RequireDropzone}}
<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/dropzone/dropzone.css">
{{end}}
{{if .EnableHeatmap}}
<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/vue-calendar-heatmap/vue-calendar-heatmap.css">
{{end}}
<style class="list-search-style"></style>
<script src="{{AppSubUrl}}/vendor/plugins/cssrelpreload/loadCSS.min.js"></script>
<script src="{{AppSubUrl}}/vendor/plugins/cssrelpreload/cssrelpreload.min.js"></script>
{{if .PageIsUserProfile}}
<meta property="og:title" content="{{.Owner.Name}}" />
<meta property="og:type" content="profile" />
<meta property="og:image" content="{{.Owner.AvatarLink}}" />
<meta property="og:url" content="{{.Owner.HTMLURL}}" />
<meta property="og:site_name" content="{{AppName}}" />
{{else if .Repository}}
<meta property="og:title" content="{{.Repository.Name}}" />
<meta property="og:type" content="object" />
<meta property="og:image" content="{{.Repository.Owner.AvatarLink}}" />
<meta property="og:url" content="{{.Repository.HTMLURL}}" />
{{if .Repository.Description}}
<meta property="og:description" content="{{.Repository.Description}}" />
{{end}}
<meta property="og:site_name" content="{{AppName}}" />
{{else}}
<meta property="og:title" content="{{AppName}}">
<meta property="og:type" content="website" />
<meta property="og:image" content="{{AppSubUrl}}/img/gitea-lg.png" />
<meta property="og:url" content="{{AppUrl}}" />
<meta property="og:description" content="{{MetaDescription}}">
{{end}}
{{if .IsSigned }}
{{ if ne .SignedUser.Theme "gitea" }}
<link rel="stylesheet" href="{{AppSubUrl}}/css/theme-{{.SignedUser.Theme}}.css">
{{end}}
{{else if ne DefaultTheme "gitea"}}
<link rel="stylesheet" href="{{AppSubUrl}}/css/theme-{{DefaultTheme}}.css">
{{end}}
{{template "custom/header" .}}
</head>
<body>
{{template "custom/body_outer_pre" .}}
<div class="full height">
<noscript>{{.i18n.Tr "enable_javascript"}}</noscript>
{{template "custom/body_inner_pre" .}}
{{if not .PageIsInstall}}
<div class="ui top secondary stackable main menu following bar light">
{{template "base/head_navbar" .}}
</div><!-- end bar -->
{{end}}
{{/*
</div>
</body>
</html>
*/}}

View file

@ -0,0 +1,16 @@
<div class="ui secondary pointing tabular top attached borderless stackable menu navbar">
<a class="item {{if .PageIsExploreRepositories}}acty{{end}}" href="{{AppSubUrl}}/explore/repos">
{{svg "octicon-repo" 16}} {{.i18n.Tr "explore.repos"}}
</a>
<a class="item {{if .PageIsExploreUsers}}acty{{end}}" href="{{AppSubUrl}}/explore/users">
{{svg "octicon-person" 16}} {{.i18n.Tr "explore.users"}}
</a>
<a class="item {{if .PageIsExploreOrganizations}}acty{{end}}" href="{{AppSubUrl}}/explore/organizations">
{{svg "octicon-organization" 16}} {{.i18n.Tr "explore.organizations"}}
</a>
{{if .IsRepoIndexerEnabled}}
<a class="item {{if .PageIsExploreCode}}acty{{end}}" href="{{AppSubUrl}}/explore/code">
{{svg "octicon-code" 16}} {{.i18n.Tr "explore.code"}}
</a>
{{end}}
</div>

View file

@ -0,0 +1,18 @@
{{template "base/head" .}}
<div class="home">
<div class="ui stackable middle very relaxed page grid">
<div class="sixteen wide center aligned centered column">
<div>
<img class="logo" src="{{StaticUrlPrefix}}/img/gitea-lg.png" />
</div>
<div class="hero">
<br />
<h1 class="ui icon header title">
{{AppName}}
</h1>
<a href="https://kittywit.ch"><h2>back to home</h2></a>
</div>
</div>
</div>
</div>
{{template "base/footer" .}}

View file

@ -0,0 +1,143 @@
<div class="header-wrapper">
{{with .Repository}}
<div class="ui container">
<div class="repo-header">
<div class="ui huge breadcrumb repo-title">
{{if .RelAvatarLink}}
<img class="ui avatar image" src="{{.RelAvatarLink}}">
{{else if .IsTemplate}}
{{if .IsPrivate}}
{{svg "octicon-repo-template-private" 32}}
{{else}}
{{svg "octicon-repo-template" 32}}
{{end}}
{{else}}
{{if .IsPrivate}}
{{svg "octicon-lock" 32}}
{{else if .IsMirror}}
{{svg "octicon-repo-clone" 32}}
{{else if .IsFork}}
{{svg "octicon-repo-fork" 32}}
{{else}}
{{svg "octicon-repo" 32}}
{{end}}
{{end}}
<a href="{{AppSubUrl}}/{{.Owner.Name}}">{{.Owner.Name}}</a>
<div class="divider"> / </div>
<a href="{{$.RepoLink}}">{{.Name}}</a>
{{if .RelAvatarLink}}
{{if .IsTemplate}}
{{if .IsPrivate}}
{{svg "octicon-repo-template-private" 32}}
{{else}}
{{svg "octicon-repo-template" 32}}
{{end}}
{{else}}
{{if .IsPrivate}}
{{svg "octicon-lock" 32}}
{{else if .IsMirror}}
{{svg "octicon-repo-clone" 32}}
{{else if .IsFork}}
{{svg "octicon-repo-fork" 32}}
{{else}}
{{svg "octicon-repo" 32}}
{{end}}
{{end}}
{{end}}
{{if .IsArchived}}<i class="archive icon archived-icon"></i>{{end}}
{{if .IsMirror}}<div class="fork-flag">{{$.i18n.Tr "repo.mirror_from"}} <a target="_blank" rel="noopener noreferrer" href="{{if .SanitizedOriginalURL}}{{.SanitizedOriginalURL}}{{else}}{{MirrorAddress $.Mirror}}{{end}}">{{if .SanitizedOriginalURL}}{{.SanitizedOriginalURL}}{{else}}{{MirrorAddress $.Mirror}}{{end}}</a></div>{{end}}
{{if .IsFork}}<div class="fork-flag">{{$.i18n.Tr "repo.forked_from"}} <a href="{{.BaseRepo.Link}}">{{SubStr .BaseRepo.RelLink 1 -1}}</a></div>{{end}}
{{if .IsGenerated}}<div class="fork-flag">{{$.i18n.Tr "repo.generated_from"}} <a href="{{.TemplateRepo.Link}}">{{SubStr .TemplateRepo.RelLink 1 -1}}</a></div>{{end}}
</div>
{{if not .IsBeingCreated}}
<div class="repo-buttons">
<div class="ui labeled button" tabindex="0">
<a class="ui compact basic button" href="{{$.RepoLink}}/action/{{if $.IsWatchingRepo}}un{{end}}watch?redirect_to={{$.Link}}">
<i class="icon fa-eye{{if not $.IsWatchingRepo}}-slash{{end}}"></i>{{if $.IsWatchingRepo}}{{$.i18n.Tr "repo.unwatch"}}{{else}}{{$.i18n.Tr "repo.watch"}}{{end}}
</a>
<a class="ui basic label" href="{{.Link}}/watchers">
{{.NumWatches}}
</a>
</div>
<div class="ui labeled button" tabindex="0">
<a class="ui compact basic button" href="{{$.RepoLink}}/action/{{if $.IsStaringRepo}}un{{end}}star?redirect_to={{$.Link}}">
<i class="icon star{{if not $.IsStaringRepo}} outline{{end}}"></i>{{if $.IsStaringRepo}}{{$.i18n.Tr "repo.unstar"}}{{else}}{{$.i18n.Tr "repo.star"}}{{end}}
</a>
<a class="ui basic label" href="{{.Link}}/stars">
{{.NumStars}}
</a>
</div>
{{if and (not .IsEmpty) ($.Permission.CanRead $.UnitTypeCode)}}
<div class="ui labeled button {{if and ($.IsSigned) (not $.CanSignedUserFork)}}disabled-repo-button{{end}}" tabindex="0">
<a class="ui compact basic button {{if or (not $.IsSigned) (not $.CanSignedUserFork)}}poping up{{end}}" {{if $.CanSignedUserFork}}href="{{AppSubUrl}}/repo/fork/{{.ID}}"{{else if $.IsSigned}} data-content="{{$.i18n.Tr "repo.fork_from_self"}}" {{ else }} data-content="{{$.i18n.Tr "repo.fork_guest_user" }}" rel="nofollow" href="{{AppSubUrl}}/user/login?redirect_to={{AppSubUrl}}/repo/fork/{{.ID}}" {{end}} data-position="top center" data-variation="tiny">
{{svg "octicon-repo-forked" 16}}{{$.i18n.Tr "repo.fork"}}
</a>
<a class="ui basic label" href="{{.Link}}/forks">
{{.NumForks}}
</a>
</div>
{{end}}
</div>
{{end}}
</div><!-- end grid -->
</div><!-- end container -->
{{end}}
<div class="ui tabs container">
{{if not .Repository.IsBeingCreated}}
<div class="ui tabular stackable menu navbar">
{{if .Permission.CanRead $.UnitTypeCode}}
<a class="item {{if .PageIsViewCode}}acty{{end}}" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}">
{{svg "octicon-code" 16}} {{.i18n.Tr "repo.code"}}
</a>
{{end}}
{{if .Permission.CanRead $.UnitTypeIssues}}
<a class="item {{if .PageIsIssueList}}acty{{end}}" href="{{.RepoLink}}/issues">
{{svg "octicon-issue-opened" 16}} {{.i18n.Tr "repo.issues"}} <span class="ui {{if not .Repository.NumOpenIssues}}gray{{else}}blue{{end}} small label">{{.Repository.NumOpenIssues}}</span>
</a>
{{end}}
{{if .Permission.CanRead $.UnitTypeExternalTracker}}
<a class="item {{if .PageIsIssueList}}acty{{end}}" href="{{.RepoExternalIssuesLink}}" target="_blank" rel="noopener noreferrer">
{{svg "octicon-link-external" 16}} {{.i18n.Tr "repo.issues"}} </span>
</a>
{{end}}
{{if and .Repository.CanEnablePulls (.Permission.CanRead $.UnitTypePullRequests)}}
<a class="item {{if .PageIsPullList}}acty{{end}}" href="{{.RepoLink}}/pulls">
{{svg "octicon-git-pull-request" 16}} {{.i18n.Tr "repo.pulls"}} <span class="ui {{if not .Repository.NumOpenPulls}}gray{{else}}blue{{end}} small label">{{.Repository.NumOpenPulls}}</span>
</a>
{{end}}
{{if and (.Permission.CanRead $.UnitTypeReleases) (not .IsEmptyRepo) }}
<a class="item {{if .PageIsReleaseList}}acty{{end}}" href="{{.RepoLink}}/releases">
{{svg "octicon-tag" 16}} {{.i18n.Tr "repo.releases"}} <span class="ui {{if not .NumReleases}}gray{{else}}blue{{end}} small label">{{.NumReleases}}</span>
</a>
{{end}}
{{if or (.Permission.CanRead $.UnitTypeWiki) (.Permission.CanRead $.UnitTypeExternalWiki)}}
<a class="item {{if .PageIsWiki}}acty{{end}}" href="{{.RepoLink}}/wiki" {{if (.Permission.CanRead $.UnitTypeExternalWiki)}} target="_blank" rel="noopener noreferrer" {{end}}>
{{svg "octicon-book" 16}} {{.i18n.Tr "repo.wiki"}}
</a>
{{end}}
{{if and (.Permission.CanReadAny $.UnitTypePullRequests $.UnitTypeIssues $.UnitTypeReleases) (not .IsEmptyRepo)}}
<a class="item {{if .PageIsActivity}}acty{{end}}" href="{{.RepoLink}}/activity">
{{svg "octicon-pulse" 16}} {{.i18n.Tr "repo.activity"}}
</a>
{{end}}
{{template "custom/extra_tabs" .}}
{{if .Permission.IsAdmin}}
<div class="right menu">
<a class="item {{if .PageIsSettings}}acty{{end}}" href="{{.RepoLink}}/settings">
{{svg "octicon-tools" 16}} {{.i18n.Tr "repo.settings"}}
</a>
</div>
{{end}}
</div>
{{end}}
</div>
<div class="ui tabs divider"></div>
</div>

View file

@ -0,0 +1,153 @@
{{template "base/head" .}}
<div class="repository file list">
{{template "repo/header" .}}
<div class="ui container">
{{template "base/alert" .}}
<div class="ui repo-description">
<div id="repo-desc">
{{if .Repository.DescriptionHTML}}<span class="description has-emoji">{{.Repository.DescriptionHTML}}</span>{{else if .IsRepositoryAdmin}}<span class="no-description text-italic">{{.i18n.Tr "repo.no_desc"}}</span>{{end}}
<a class="link" href="{{.Repository.Website}}">{{.Repository.Website}}</a>
</div>
{{if .RepoSearchEnabled}}
<div class="ui repo-search">
<form class="ui form ignore-dirty" action="{{.RepoLink}}/search" method="get">
<div class="field">
<div class="ui action input">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "repo.search.search_repo"}}">
<button class="ui icon button" type="submit">
<i class="search icon"></i>
</button>
</div>
</div>
</form>
</div>
{{end}}
</div>
<div class="ui" id="repo-topics">
{{range .Topics}}<a class="ui repo-topic small label topic" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}}
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<a id="manage_topic">{{.i18n.Tr "repo.topic.manage_topics"}}</a>{{end}}
</div>
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}
<div class="ui repo-topic-edit grid form segment error" id="topic_edit" style="display:none">
<div class="fourteen wide column">
<div class="field">
<div class="ui fluid multiple search selection dropdown">
<input type="hidden" name="topics" value="{{range $i, $v := .Topics}}{{.Name}}{{if lt (Add $i 1) (len $.Topics)}},{{end}}{{end}}">
{{range .Topics}}
<div class="ui small label topic transition visible" data-value="{{.Name}}" style="display: inline-block !important; cursor: default;">{{.Name}}<i class="delete icon"></i></div>
{{end}}
<div class="text"></div>
</div>
</div>
</div>
<div class="two wide column">
<a class="ui button primary" href="javascript:;" id="save_topic"
data-link="{{.RepoLink}}/topics">{{.i18n.Tr "repo.topic.done"}}</a>
</div>
</div>
{{end}}
<div class="hide" id="validate_prompt">
<span id="count_prompt">{{.i18n.Tr "repo.topic.count_prompt"}}</span>
<span id="format_prompt">{{.i18n.Tr "repo.topic.format_prompt"}}</span>
</div>
{{if .Repository.IsArchived}}
<div class="ui warning message">
{{.i18n.Tr "repo.archive.title"}}
</div>
{{end}}
{{template "repo/sub_menu" .}}
<div class="ui stackable secondary menu mobile--margin-between-items mobile--no-negative-margins">
{{template "repo/branch_dropdown" .}}
{{ $n := len .TreeNames}}
{{ $l := Subtract $n 1}}
<!-- If home page, show new PR. If not, show breadcrumb -->
{{if eq $n 0}}
{{if and .PullRequestCtx.Allowed .IsViewBranch (not .Repository.IsArchived)}}
<div class="fitted item">
<a href="{{.BaseRepo.Link}}/compare/{{.BaseRepo.DefaultBranch | EscapePound}}...{{if ne .Repository.Owner.Name .BaseRepo.Owner.Name}}{{.Repository.Owner.Name}}:{{end}}{{.BranchName | EscapePound}}">
<button id="new-pull-request" class="ui compact basic button">{{.i18n.Tr "repo.pulls.compare_changes"}}</button>
</a>
</div>
{{end}}
{{else}}
<div class="fitted item"><span class="ui breadcrumb repo-path"><a class="section" href="{{.RepoLink}}/src/{{EscapePound .BranchNameSubURL}}" title="{{.Repository.Name}}">{{EllipsisString .Repository.Name 30}}</a>{{range $i, $v := .TreeNames}}<span class="divider">/</span>{{if eq $i $l}}<span class="active section" title="{{$v}}">{{EllipsisString $v 30}}</span>{{else}}{{ $p := index $.Paths $i}}<span class="section"><a href="{{EscapePound $.BranchLink}}/{{EscapePound $p}}" title="{{$v}}">{{EllipsisString $v 30}}</a></span>{{end}}{{end}}</span></div>
{{end}}
<div class="right fitted item" id="file-buttons">
<div class="ui tiny blue buttons">
{{if .Repository.CanEnableEditor}}
{{if .CanAddFile}}
<a href="{{.RepoLink}}/_new/{{EscapePound .BranchName}}/{{EscapePound .TreePath}}" class="ui button">
{{.i18n.Tr "repo.editor.new_file"}}
</a>
{{end}}
{{if .CanUploadFile}}
<a href="{{.RepoLink}}/_upload/{{EscapePound .BranchName}}/{{EscapePound .TreePath}}" class="ui button">
{{.i18n.Tr "repo.editor.upload_file"}}
</a>
{{end}}
{{end}}
{{if and (ne $n 0) (not .IsViewFile) (not .IsBlame) }}
<a href="{{.RepoLink}}/commits/{{EscapePound .BranchNameSubURL}}/{{EscapePound .TreePath}}" class="ui button">
{{.i18n.Tr "repo.file_history"}}
</a>
{{end}}
</div>
</div>
<div class="fitted item">
{{if eq $n 0}}
{{if .Repository.IsTemplate}}
<div class="ui tiny blue buttons">
<a href="{{AppSubUrl}}/repo/create?template_id={{.Repository.ID}}" class="ui button">
{{.i18n.Tr "repo.use_template"}}
</a>
</div>
{{end}}
{{end}}
</div>
<div class="fitted item">
<!-- Only show clone panel in repository home page -->
{{if eq $n 0}}
<div class="ui action tiny input" id="clone-panel">
{{if not $.DisableHTTP}}
<button class="ui basic clone button" id="repo-clone-https" data-link="{{.CloneLink.HTTPS}}">
{{if UseHTTPS}}HTTPS{{else}}HTTP{{end}}
</button>
{{end}}
{{if and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH)}}
<button class="ui basic clone button" id="repo-clone-ssh" data-link="{{.CloneLink.SSH}}">
SSH
</button>
{{end}}
{{if not $.DisableHTTP}}
<input id="repo-clone-url" value="{{$.CloneLink.HTTPS}}" readonly>
{{else if and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH)}}
<input id="repo-clone-url" value="{{$.CloneLink.SSH}}" readonly>
{{end}}
{{if or (not $.DisableHTTP) (and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH))}}
<button class="ui basic icon button poping up clipboard" id="clipboard-btn" data-original="{{.i18n.Tr "repo.copy_link"}}" data-success="{{.i18n.Tr "repo.copy_link_success"}}" data-error="{{.i18n.Tr "repo.copy_link_error"}}" data-content="{{.i18n.Tr "repo.copy_link"}}" data-variation="inverted tiny" data-clipboard-target="#repo-clone-url">
{{svg "octicon-clippy" 16}}
</button>
{{end}}
<div class="ui basic jump dropdown icon button poping up" data-content="{{.i18n.Tr "repo.download_archive"}}" data-variation="tiny inverted" data-position="top right">
<i class="download icon"></i>
<div class="menu">
<a class="item" href="{{$.RepoLink}}/archive/{{EscapePound $.BranchName}}.zip">{{svg "octicon-file-zip" 16}}&nbsp;ZIP</a>
<a class="item" href="{{$.RepoLink}}/archive/{{EscapePound $.BranchName}}.tar.gz">{{svg "octicon-file-zip" 16}}&nbsp;TAR.GZ</a>
</div>
</div>
</div>
{{end}}
</div>
</div>
{{if .IsViewFile}}
{{template "repo/view_file" .}}
{{else if .IsBlame}}
{{template "repo/blame" .}}
{{else}}
{{template "repo/view_list" .}}
{{end}}
</div>
</div>
{{template "base/footer" .}}

View file

@ -0,0 +1,29 @@
<div class="ui secondary pointing tabular top attached borderless menu stackable new-menu navbar">
<a class="item {{if .PageIsSettingsOptions}}acty{{end}}" href="{{.RepoLink}}/settings">
{{.i18n.Tr "repo.settings.options"}}
</a>
<a class="item {{if .PageIsSettingsCollaboration}}acty{{end}}" href="{{.RepoLink}}/settings/collaboration">
{{.i18n.Tr "repo.settings.collaboration"}}
</a>
{{if not .Repository.IsEmpty}}
<a class="item {{if .PageIsSettingsBranches}}acty{{end}}" href="{{.RepoLink}}/settings/branches">
{{.i18n.Tr "repo.settings.branches"}}
</a>
{{end}}
<a class="item {{if .PageIsSettingsHooks}}acty{{end}}" href="{{.RepoLink}}/settings/hooks">
{{.i18n.Tr "repo.settings.hooks"}}
</a>
{{if .SignedUser.CanEditGitHook}}
<a class="item {{if .PageIsSettingsGitHooks}}acty{{end}}" href="{{.RepoLink}}/settings/hooks/git">
{{.i18n.Tr "repo.settings.githooks"}}
</a>
{{end}}
<a class="item {{if .PageIsSettingsKeys}}acty{{end}}" href="{{.RepoLink}}/settings/keys">
{{.i18n.Tr "repo.settings.deploy_keys"}}
</a>
{{if .LFSStartServer}}
<a class="item {{if .PageIsSettingsLFS}}acty{{end}}" href="{{.RepoLink}}/settings/lfs">
{{.i18n.Tr "repo.settings.lfs"}}
</a>
{{end}}
</div>

View file

@ -0,0 +1,132 @@
{{template "base/head" .}}
<div class="dashboard feeds">
{{template "user/dashboard/navbar" .}}
<div class="ui container">
{{template "base/alert" .}}
<div class="ui mobile reversed stackable grid">
<div class="ui container ten wide column">
{{if .EnableHeatmap}}
<div id="user-heatmap" style="padding-right: 40px">
<activity-heatmap :locale="locale" :suburl="suburl" :user="heatmapUser">
<div slot="loading">
<div class="ui active centered inline indeterminate text loader" id="loading-heatmap">{{.i18n.Tr "user.heatmap.loading"}}</div>
</div>
</activity-heatmap>
<div class="ui divider"></div>
</div>
{{end}}
{{template "user/dashboard/feeds" .}}
</div>
<div id="app" class="six wide column">
<repo-search
:search-limit="searchLimit"
:suburl="suburl"
:uid="uid"
:more-repos-link="'{{.ContextUser.HomeLink}}'"
{{if not .ContextUser.IsOrganization}}
:organizations="[
{{range .ContextUser.Orgs}}
{name: '{{.Name}}', num_repos: '{{.NumRepos}}'},
{{end}}
]"
:is-organization="false"
:organizations-total-count="{{.ContextUser.GetOrganizationCount}}"
:can-create-organization="{{.SignedUser.CanCreateOrganization}}"
{{end}}
inline-template
v-cloak
>
<div>
<div v-if="!isOrganization" class="ui two item tabable menu">
<a :class="{item: true, acty: tab === 'repos'}" @click="changeTab('repos')">{{.i18n.Tr "repository"}}</a>
<a :class="{item: true, acty: tab === 'organizations'}" @click="changeTab('organizations')">{{.i18n.Tr "organization"}}</a>
</div>
<div v-show="tab === 'repos'" class="ui tab active list dashboard-repos">
<h4 class="ui top attached header">
{{.i18n.Tr "home.my_repos"}} <span class="ui grey label">${reposTotalCount}</span>
{{if or (not .ContextUser.IsOrganization) .IsOrganizationOwner}}
<div class="ui right">
<a class="poping up" :href="suburl + '/repo/create{{if .ContextUser.IsOrganization}}?org={{.ContextUser.ID}}{{end}}'" data-content="{{.i18n.Tr "new_repo"}}" data-variation="tiny inverted" data-position="left center">
<i class="plus icon"></i>
<span class="sr-only">{{.i18n.Tr "new_repo"}}</span>
</a>
</div>
{{end}}
</h4>
<div class="ui attached secondary segment repos-search">
<div class="ui fluid icon input" :class="{loading: isLoading}">
<input @input="searchRepos(reposFilter)" v-model="searchQuery" ref="search" placeholder="{{.i18n.Tr "home.search_repos"}}">
<i class="search icon"></i>
</div>
<div class="ui secondary tiny pointing borderless menu center aligned grid repos-filter">
<a class="item" :class="{acty: reposFilter === 'all'}" @click="changeReposFilter('all')">
{{.i18n.Tr "all"}}
<div v-show="reposFilter === 'all'" class="ui circular mini grey label">${repoTypeCount}</div>
</a>
<a class="item" :class="{acty: reposFilter === 'sources'}" @click="changeReposFilter('sources')">
{{.i18n.Tr "sources"}}
<div v-show="reposFilter === 'sources'" class="ui circular mini grey label">${repoTypeCount}</div>
</a>
<a class="item" :class="{acty: reposFilter === 'forks'}" @click="changeReposFilter('forks')">
{{.i18n.Tr "forks"}}
<div v-show="reposFilter === 'forks'" class="ui circular mini grey label">${repoTypeCount}</div>
</a>
<a class="item" :class="{acty: reposFilter === 'mirrors'}" @click="changeReposFilter('mirrors')">
{{.i18n.Tr "mirrors"}}
<div v-show="reposFilter === 'mirrors'" class="ui circular mini grey label">${repoTypeCount}</div>
</a>
<a class="item" :class="{acty: reposFilter === 'collaborative'}" @click="changeReposFilter('collaborative')">
{{.i18n.Tr "collaborative"}}
<div v-show="reposFilter === 'collaborative'" class="ui circular mini grey label">${repoTypeCount}</div>
</a>
</div>
</div>
<div class="ui attached table segment">
<ul class="repo-owner-name-list">
<li v-for="repo in repos" :class="{'private': repo.private}" v-show="showRepo(repo, reposFilter)">
<a :href="suburl + '/' + repo.full_name">
<svg :class="'svg ' + repoClass(repo)" width="16" height="16" aria-hidden="true"><use :xlink:href="staticPrefix + '/img/svg/icons.svg#' + repoClass(repo)" /></svg>
<strong class="text truncate item-name">${repo.full_name}</strong>
<i v-if="repo.archived" class="archive icon archived-icon"></i>
<span class="ui right text light grey">
${repo.stars_count} <span class="rear">{{svg "octicon-star" 16}}</span>
</span>
</a>
</li>
<li v-if="showMoreReposLink">
<a :href="moreReposLink">{{.i18n.Tr "home.show_more_repos"}}</a>
</li>
</ul>
</div>
</div>
<div v-if="!isOrganization" v-show="tab === 'organizations'" class="ui tab active list">
<h4 class="ui top attached header">
{{.i18n.Tr "home.my_orgs"}} <span class="ui grey label">${organizationsTotalCount}</span>
<div v-if="canCreateOrganization" class="ui right">
<a class="poping up" :href="suburl + '/org/create'" data-content="{{.i18n.Tr "new_org"}}" data-variation="tiny inverted" data-position="left center">
<i class="plus icon"></i>
<span class="sr-only">{{.i18n.Tr "new_org"}}</span>
</a>
</div>
</h4>
<div class="ui attached table segment">
<ul class="repo-owner-name-list">
<li v-for="org in organizations">
<a :href="suburl + '/' + org.name">
{{svg "octicon-organization" 16}}
<strong class="text truncate item-name">${org.name}</strong>
<span class="ui right text light grey">
${org.num_repos} <span class="rear">{{svg "octicon-repo" 16}}</span>
</span>
</a>
</li>
</ul>
</div>
</div>
</div>
</repo-search>
</div>
</div>
</div>
</div>
{{template "base/footer" .}}

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