This commit is contained in:
commit
30ce0dafc2
195 changed files with 8902 additions and 0 deletions
7
services/bind/default.nix
Normal file
7
services/bind/default.nix
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
imports = [
|
||||
./firewall.nix
|
||||
./options.nix
|
||||
./service.nix
|
||||
];
|
||||
}
|
||||
14
services/bind/firewall.nix
Normal file
14
services/bind/firewall.nix
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.machine.bind;
|
||||
in
|
||||
with lib; mkIf cfg.enable {
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [ cfg.port ];
|
||||
allowedUDPPorts = [ cfg.port ];
|
||||
};
|
||||
}
|
||||
25
services/bind/options.nix
Normal file
25
services/bind/options.nix
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
dns,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
with lib;
|
||||
{
|
||||
options.machine.bind = {
|
||||
enable = mkEnableOption "Bind Server";
|
||||
domain = mkOption {
|
||||
type = types.str;
|
||||
description = "Domain name";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 53;
|
||||
description = "Port to listen on.";
|
||||
};
|
||||
zones = mkOption {
|
||||
type = types.attrsOf dns.lib.types.zone;
|
||||
default = { };
|
||||
description = "DNS zones";
|
||||
};
|
||||
};
|
||||
}
|
||||
21
services/bind/service.nix
Normal file
21
services/bind/service.nix
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.machine.bind;
|
||||
in
|
||||
with lib; mkIf cfg.enable {
|
||||
services.bind = {
|
||||
enable = cfg.enable;
|
||||
listenOnPort = cfg.port;
|
||||
zones = {
|
||||
${cfg.domain} = {
|
||||
master = true;
|
||||
file = pkgs.writeText "zone-${cfg.domain}" (builtins.toString cfg.zones.${cfg.domain});
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
7
services/code-server/default.nix
Normal file
7
services/code-server/default.nix
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
imports = [
|
||||
./options.nix
|
||||
./service.nix
|
||||
./nginx.nix
|
||||
];
|
||||
}
|
||||
27
services/code-server/nginx.nix
Normal file
27
services/code-server/nginx.nix
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.machine.code-server;
|
||||
in
|
||||
with lib; mkIf cfg.enable {
|
||||
services.nginx.virtualHosts = mkIf (cfg.domain != null) {
|
||||
${cfg.domain} = {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:${toString cfg.port}";
|
||||
proxyWebsockets = true;
|
||||
extraConfig = ''
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 86400;
|
||||
proxy_send_timeout 86400;
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
33
services/code-server/options.nix
Normal file
33
services/code-server/options.nix
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.machine.code-server;
|
||||
in
|
||||
with lib; {
|
||||
options.machine.code-server = {
|
||||
enable = mkEnableOption "code-server";
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 4444;
|
||||
description = "Port to listen on.";
|
||||
};
|
||||
domain = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "domain for the code-server instance.";
|
||||
};
|
||||
hashedPassword = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Hashed password for code-server";
|
||||
};
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = config.services.code-server.user;
|
||||
description = "The user to run code-server as";
|
||||
};
|
||||
};
|
||||
}
|
||||
49
services/code-server/service.nix
Normal file
49
services/code-server/service.nix
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.machine.code-server;
|
||||
in
|
||||
with lib; mkIf cfg.enable {
|
||||
services.code-server = {
|
||||
enable = cfg.enable;
|
||||
port = cfg.port;
|
||||
hashedPassword = cfg.hashedPassword;
|
||||
user = cfg.user;
|
||||
host = "127.0.0.1";
|
||||
disableTelemetry = true;
|
||||
disableUpdateCheck = true;
|
||||
disableWorkspaceTrust = true;
|
||||
package = pkgs.vscode-with-extensions.override {
|
||||
vscode = pkgs.code-server;
|
||||
vscodeExtensions =
|
||||
(with pkgs.vscode-extensions; [
|
||||
rust-lang.rust-analyzer
|
||||
fill-labs.dependi
|
||||
ms-python.black-formatter
|
||||
ms-python.python
|
||||
ms-python.isort
|
||||
ms-python.mypy-type-checker
|
||||
intellsmi.comment-translate
|
||||
editorconfig.editorconfig
|
||||
dbaeumer.vscode-eslint
|
||||
davidanson.vscode-markdownlint
|
||||
yzhang.markdown-all-in-one
|
||||
jnoortheen.nix-ide
|
||||
ziglang.vscode-zig
|
||||
bbenoist.nix
|
||||
])
|
||||
++ pkgs.vscode-utils.extensionsFromVscodeMarketplace [
|
||||
{
|
||||
name = "purpleVoid";
|
||||
publisher = "Rej";
|
||||
version = "1.0.3";
|
||||
sha256 = "oCZ2N8j2U0xGvechD7DlW64KiL0eSDKYwniYft0kVu4=";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
7
services/coturn/default.nix
Normal file
7
services/coturn/default.nix
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
imports = [
|
||||
./firewall.nix
|
||||
./options.nix
|
||||
./service.nix
|
||||
];
|
||||
}
|
||||
33
services/coturn/firewall.nix
Normal file
33
services/coturn/firewall.nix
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.machine.coturn;
|
||||
in
|
||||
with lib; mkIf cfg.enable {
|
||||
networking.firewall = {
|
||||
interfaces.enp2s0 =
|
||||
let
|
||||
range = with config.services.coturn; [
|
||||
{
|
||||
from = min-port;
|
||||
to = max-port;
|
||||
}
|
||||
];
|
||||
in
|
||||
{
|
||||
allowedUDPPortRanges = range;
|
||||
allowedUDPPorts = [
|
||||
3478
|
||||
5349
|
||||
];
|
||||
allowedTCPPortRanges = [ ];
|
||||
allowedTCPPorts = [
|
||||
3478
|
||||
5349
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
22
services/coturn/options.nix
Normal file
22
services/coturn/options.nix
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{ lib, ... }:
|
||||
with lib;
|
||||
{
|
||||
options.machine.coturn = {
|
||||
enable = mkEnableOption "Coturn";
|
||||
startPort = mkOption {
|
||||
type = types.port;
|
||||
default = 49000;
|
||||
description = "Start port for Coturn.";
|
||||
};
|
||||
endPort = mkOption {
|
||||
type = types.port;
|
||||
default = 50000;
|
||||
description = "End port for Coturn.";
|
||||
};
|
||||
realm = mkOption {
|
||||
type = types.str;
|
||||
default = "turn.example.com";
|
||||
description = "Realm for Coturn.";
|
||||
};
|
||||
};
|
||||
}
|
||||
60
services/coturn/service.nix
Normal file
60
services/coturn/service.nix
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
sec,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.coturn)
|
||||
enable
|
||||
startPort
|
||||
endPort
|
||||
realm
|
||||
;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
services.coturn = rec {
|
||||
inherit realm enable;
|
||||
no-cli = true;
|
||||
no-tcp-relay = true;
|
||||
min-port = startPort;
|
||||
max-port = endPort;
|
||||
use-auth-secret = true;
|
||||
static-auth-secret-file = sec."turn/authSecret".path;
|
||||
cert = "${config.security.acme.certs.${realm}.directory}/full.pem";
|
||||
pkey = "${config.security.acme.certs.${realm}.directory}/key.pem";
|
||||
extraConfig = ''
|
||||
# for debugging
|
||||
verbose
|
||||
# ban private IP ranges
|
||||
no-multicast-peers
|
||||
denied-peer-ip=0.0.0.0-0.255.255.255
|
||||
denied-peer-ip=10.0.0.0-10.255.255.255
|
||||
denied-peer-ip=100.64.0.0-100.127.255.255
|
||||
denied-peer-ip=127.0.0.0-127.255.255.255
|
||||
denied-peer-ip=169.254.0.0-169.254.255.255
|
||||
denied-peer-ip=172.16.0.0-172.31.255.255
|
||||
denied-peer-ip=192.0.0.0-192.0.0.255
|
||||
denied-peer-ip=192.0.2.0-192.0.2.255
|
||||
denied-peer-ip=192.88.99.0-192.88.99.255
|
||||
denied-peer-ip=192.168.0.0-192.168.255.255
|
||||
denied-peer-ip=198.18.0.0-198.19.255.255
|
||||
denied-peer-ip=198.51.100.0-198.51.100.255
|
||||
denied-peer-ip=203.0.113.0-203.0.113.255
|
||||
denied-peer-ip=240.0.0.0-255.255.255.255
|
||||
denied-peer-ip=::1
|
||||
denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
|
||||
denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255
|
||||
denied-peer-ip=100::-100::ffff:ffff:ffff:ffff
|
||||
denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
'';
|
||||
};
|
||||
|
||||
security.acme.certs.${config.services.coturn.realm} = {
|
||||
postRun = "systemctl restart coturn.service";
|
||||
group = "turnserver";
|
||||
};
|
||||
}
|
||||
23
services/default.nix
Normal file
23
services/default.nix
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
imports = [
|
||||
./bind
|
||||
./code-server
|
||||
./coturn
|
||||
./forgejo
|
||||
./mail
|
||||
./minecraft-server
|
||||
./mysql
|
||||
./navidrome
|
||||
./networking
|
||||
./nextcloud
|
||||
./nginx
|
||||
./postgresql
|
||||
./prosody
|
||||
./redis
|
||||
./roundcube
|
||||
./synapse
|
||||
./uptime-kuma
|
||||
./vaultwarden
|
||||
./xray-3x-ui
|
||||
];
|
||||
}
|
||||
10
services/forgejo/default.nix
Normal file
10
services/forgejo/default.nix
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
imports = [
|
||||
./mail.nix
|
||||
./network.nix
|
||||
./options.nix
|
||||
./runners.nix
|
||||
./service.nix
|
||||
./tmpfiles.nix
|
||||
];
|
||||
}
|
||||
37
services/forgejo/mail.nix
Normal file
37
services/forgejo/mail.nix
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
sec,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.forgejo) domain;
|
||||
address = "noreply@${domain}";
|
||||
in
|
||||
with lib; mkIf config.machine.mail.enable {
|
||||
services.forgejo = {
|
||||
secrets = {
|
||||
mailer.PASSWD = sec."mail/servicePassword".path;
|
||||
};
|
||||
settings.mailer = {
|
||||
ENABLED = true;
|
||||
FROM = address;
|
||||
USER = address;
|
||||
SMTP_ADDR = config.machine.mail.fqdn;
|
||||
SMTP_PORT = 465;
|
||||
PROTOCOL = "smtps";
|
||||
SENDMAIL_PATH = "${pkgs.system-sendmail}/bin/sendmail";
|
||||
SEND_AS_PLAIN_TEXT = true;
|
||||
};
|
||||
};
|
||||
|
||||
mailserver = {
|
||||
domains = [ domain ];
|
||||
accounts.${address} = {
|
||||
hashedPasswordFile = sec."mail/serviceHashedPassword".path;
|
||||
aliases = [ ];
|
||||
sendOnly = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
28
services/forgejo/network.nix
Normal file
28
services/forgejo/network.nix
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.forgejo)
|
||||
enable
|
||||
domain
|
||||
port
|
||||
;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [ port ];
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts = with lib; mkIf (domain != null) {
|
||||
"${domain}" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
locations."/" = {
|
||||
proxyPass = "http://[::1]:${toString port}";
|
||||
proxyWebsockets = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
40
services/forgejo/options.nix
Normal file
40
services/forgejo/options.nix
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib)
|
||||
mkEnableOption
|
||||
mkOption
|
||||
types
|
||||
;
|
||||
in
|
||||
{
|
||||
options.machine.forgejo = {
|
||||
enable = mkEnableOption "Forgejo";
|
||||
enableRunner = mkEnableOption "Actions runner";
|
||||
domain = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Domain name. If not set, will be disabled, and use the localhost.";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 3000;
|
||||
description = "Listen port.";
|
||||
};
|
||||
database = {
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
default = "127.0.0.1";
|
||||
description = "PostgreSQL database host address.";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = config.services.postgresql.port;
|
||||
description = "PostgreSQL database host port.";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
41
services/forgejo/runners.nix
Normal file
41
services/forgejo/runners.nix
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
sec,
|
||||
...
|
||||
}:
|
||||
let
|
||||
url = config.services.forgejo.settings.server.ROOT_URL;
|
||||
cfg = config.machine.forgejo;
|
||||
in
|
||||
with lib; mkIf cfg.enableRunner {
|
||||
sops.secrets = {
|
||||
"forgejo/runnerToken" = {
|
||||
sopsFile = ./../../secrets/common.yaml;
|
||||
};
|
||||
};
|
||||
|
||||
services.gitea-actions-runner = {
|
||||
package = pkgs.forgejo-runner;
|
||||
instances.default = {
|
||||
name = "forgejo-runner";
|
||||
enable = cfg.enableRunner;
|
||||
tokenFile = sec."forgejo/runnerToken".path;
|
||||
inherit url;
|
||||
labels = [
|
||||
"ubuntu-latest:docker://ghcr.io/catthehacker/ubuntu:act-latest"
|
||||
"ubuntu-26.04:docker://ghcr.io/catthehacker/ubuntu:act-26.04"
|
||||
"ubuntu-24.04:docker://ghcr.io/catthehacker/ubuntu:act-24.04"
|
||||
"ubuntu-22.04:docker://ghcr.io/catthehacker/ubuntu:act-22.04"
|
||||
"nixos:docker://nixos/nix:latest"
|
||||
"nixos-2.34.4:docker://nixos/nix:2.34.4"
|
||||
];
|
||||
settings = {
|
||||
container = {
|
||||
network = "host";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
64
services/forgejo/service.nix
Normal file
64
services/forgejo/service.nix
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.forgejo)
|
||||
enable
|
||||
database
|
||||
domain
|
||||
port
|
||||
;
|
||||
in
|
||||
{
|
||||
environment.systemPackages = lib.optionals enable [
|
||||
config.services.forgejo.package
|
||||
];
|
||||
|
||||
services.openssh.settings.AllowUsers = [
|
||||
"forgejo"
|
||||
];
|
||||
|
||||
services.forgejo = {
|
||||
inherit enable;
|
||||
database = {
|
||||
type = "postgres";
|
||||
inherit (database) host;
|
||||
inherit (database) port;
|
||||
};
|
||||
lfs.enable = true;
|
||||
settings = {
|
||||
DEFAULT = {
|
||||
APP_NAME = "RuJect Forgejo";
|
||||
};
|
||||
server = {
|
||||
DOMAIN = if (domain != null) then domain else "[::1]";
|
||||
ROOT_URL = if (domain != null) then "https://${domain}/" else "http://[::1]/";
|
||||
HTTP_PORT = port;
|
||||
};
|
||||
service = {
|
||||
SHOW_REGISTRATION_BUTTON = true;
|
||||
REGISTER_EMAIL_CONFIRM = true;
|
||||
ENABLE_NOTIFY_MAIL = true;
|
||||
};
|
||||
"repository.signing" = {
|
||||
DEFAULT_TRUST_MODEL = "committer";
|
||||
};
|
||||
actions = {
|
||||
ENABLED = true;
|
||||
DEFAULT_ACTIONS_URL = "github";
|
||||
};
|
||||
picture = {
|
||||
AVATAR_MAX_FILE_SIZE = 10485760;
|
||||
AVATAR_MAX_WIDTH = 8192;
|
||||
AVATAR_MAX_HEIGHT = 8192;
|
||||
AVATAR_MAX_ORIGIN_SIZE = 5242880;
|
||||
};
|
||||
ui = {
|
||||
DEFAULT_THEME = "catppuccin-mocha";
|
||||
THEMES = "forgejo-auto,forgejo-light,forgejo-dark,catppuccin-mocha";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
873
services/forgejo/themes/theme-catppuccin-mocha-lavender.css
Normal file
873
services/forgejo/themes/theme-catppuccin-mocha-lavender.css
Normal file
|
|
@ -0,0 +1,873 @@
|
|||
:root {
|
||||
color-scheme: dark;
|
||||
--is-dark-theme: true;
|
||||
accent-color: #b4befe;
|
||||
--color-primary: #b4befe;
|
||||
--color-primary-contrast: #11111b;
|
||||
--color-primary-hover: rgb(154.8355263158, 168.1907894737, 253.6644736842);
|
||||
--color-primary-dark-1: rgb(164.9013157895, 176.9144736842, 253.7986842105);
|
||||
--color-primary-dark-2: rgb(149.8026315789, 163.8289473684, 253.5973684211);
|
||||
--color-primary-dark-3: rgb(134.7039473684, 150.7434210526, 253.3960526316);
|
||||
--color-primary-dark-4: rgb(119.6052631579, 137.6578947368, 253.1947368421);
|
||||
--color-primary-dark-5: rgb(104.5065789474, 124.5723684211, 252.9934210526);
|
||||
--color-primary-dark-6: rgb(89.4078947368, 111.4868421053, 252.7921052632);
|
||||
--color-primary-dark-7: rgb(74.3092105263, 98.4013157895, 252.5907894737);
|
||||
--color-primary-light-1: rgb(195.0986842105, 203.0855263158, 254.2013157895);
|
||||
--color-primary-light-2: rgb(210.1973684211, 216.1710526316, 254.4026315789);
|
||||
--color-primary-light-3: rgb(225.2960526316, 229.2565789474, 254.6039473684);
|
||||
--color-primary-light-4: rgb(240.3947368421, 242.3421052632, 254.8052631579);
|
||||
--color-primary-light-5: hsl(231.8918918919, 97.3684210526%, 100.0980392157%);
|
||||
--color-primary-light-6: hsl(231.8918918919, 97.3684210526%, 103.0980392157%);
|
||||
--color-primary-light-7: hsl(231.8918918919, 97.3684210526%, 106.0980392157%);
|
||||
--color-primary-alpha-10: rgba(180, 190, 254, 0.1);
|
||||
--color-primary-alpha-20: rgba(180, 190, 254, 0.2);
|
||||
--color-primary-alpha-30: rgba(180, 190, 254, 0.3);
|
||||
--color-primary-alpha-40: rgba(180, 190, 254, 0.4);
|
||||
--color-primary-alpha-50: rgba(180, 190, 254, 0.5);
|
||||
--color-primary-alpha-60: rgba(180, 190, 254, 0.6);
|
||||
--color-primary-alpha-70: rgba(180, 190, 254, 0.7);
|
||||
--color-primary-alpha-80: rgba(180, 190, 254, 0.8);
|
||||
--color-primary-alpha-90: rgba(180, 190, 254, 0.9);
|
||||
--color-secondary: #45475a;
|
||||
--color-secondary-dark-1: rgb(55.4076923077, 56.5384615385, 76.8923076923);
|
||||
--color-secondary-dark-2: rgb(61.8153846154, 63.0769230769, 85.7846153846);
|
||||
--color-secondary-dark-3: rgb(68.2230769231, 69.6153846154, 94.6769230769);
|
||||
--color-secondary-dark-4: rgb(74.6307692308, 76.1538461538, 103.5692307692);
|
||||
--color-secondary-dark-5: rgb(81.0384615385, 82.6923076923, 112.4615384615);
|
||||
--color-secondary-dark-6: rgb(87.4461538462, 89.2307692308, 121.3538461538);
|
||||
--color-secondary-dark-7: rgb(93.8538461538, 95.7692307692, 130.2461538462);
|
||||
--color-secondary-dark-8: rgb(100.2615384615, 102.3076923077, 139.1384615385);
|
||||
--color-secondary-dark-9: rgb(106.6692307692, 108.8461538462, 148.0307692308);
|
||||
--color-secondary-dark-10: rgb(115.5128205128, 117.5641025641, 154.4871794872);
|
||||
--color-secondary-dark-11: rgb(124.4051282051, 126.3256410256, 160.8948717949);
|
||||
--color-secondary-dark-12: rgb(133.2974358974, 135.0871794872, 167.3025641026);
|
||||
--color-secondary-dark-13: rgb(142.1897435897, 143.8487179487, 173.7102564103);
|
||||
--color-secondary-light-1: rgb(42.5923076923, 43.4615384615, 59.1076923077);
|
||||
--color-secondary-light-2: rgb(36.1846153846, 36.9230769231, 50.2153846154);
|
||||
--color-secondary-light-3: rgb(29.7769230769, 30.3846153846, 41.3230769231);
|
||||
--color-secondary-light-4: rgb(23.3692307692, 23.8461538462, 32.4307692308);
|
||||
--color-secondary-alpha-10: rgba(49, 50, 68, 0.1);
|
||||
--color-secondary-alpha-20: rgba(49, 50, 68, 0.2);
|
||||
--color-secondary-alpha-30: rgba(49, 50, 68, 0.3);
|
||||
--color-secondary-alpha-40: rgba(49, 50, 68, 0.4);
|
||||
--color-secondary-alpha-50: rgba(49, 50, 68, 0.5);
|
||||
--color-secondary-alpha-60: rgba(49, 50, 68, 0.6);
|
||||
--color-secondary-alpha-70: rgba(49, 50, 68, 0.7);
|
||||
--color-secondary-alpha-80: rgba(49, 50, 68, 0.8);
|
||||
--color-secondary-alpha-90: rgba(49, 50, 68, 0.9);
|
||||
/* colors */
|
||||
--color-red: #f38ba8;
|
||||
--color-orange: #fab387;
|
||||
--color-yellow: #f9e2af;
|
||||
--color-olive: #a6e3a1;
|
||||
--color-green: #a6e3a1;
|
||||
--color-teal: #94e2d5;
|
||||
--color-blue: #89b4fa;
|
||||
--color-violet: #b4befe;
|
||||
--color-purple: #cba6f7;
|
||||
--color-pink: #f5c2e7;
|
||||
--color-brown: #f2cdcd;
|
||||
--color-grey: #9399b2;
|
||||
--color-black: #181825;
|
||||
/* light variants - produced via Sass scale-color(color, $lightness: -10%) */
|
||||
--color-red-light: rgb(238.21875, 92.78125, 133.3359375);
|
||||
--color-orange-light: rgb(247.96, 147.992, 86.04);
|
||||
--color-yellow-light: rgb(245.4418604651, 208.8023255814, 127.5581395349);
|
||||
--color-olive-light: rgb(128.7950819672, 215.2950819672, 121.7049180328);
|
||||
--color-green-light: rgb(128.7950819672, 215.2950819672, 121.7049180328);
|
||||
--color-teal-light: rgb(107.875, 215.125, 197.25);
|
||||
--color-blue-light: rgb(88.0731707317, 148.9024390244, 247.9268292683);
|
||||
--color-violet-light: rgb(129.6710526316, 146.3815789474, 253.3289473684);
|
||||
--color-purple-light: rgb(175.6597938144, 119.206185567, 242.793814433);
|
||||
--color-pink-light: rgb(237.8169014085, 150.1830985915, 213.7605633803);
|
||||
--color-brown-light: rgb(231.4761904762, 164.5238095238, 164.5238095238);
|
||||
--color-grey-light: rgb(117.227027027, 124.8810810811, 156.772972973);
|
||||
--color-black-light: rgb(3.9344262295, 3.9344262295, 6.0655737705);
|
||||
/* dark 1 variants - produced via Sass scale-color(color, $lightness: -10%) */
|
||||
--color-red-dark-1: rgb(238.21875, 92.78125, 133.3359375);
|
||||
--color-orange-dark-1: rgb(247.96, 147.992, 86.04);
|
||||
--color-yellow-dark-1: rgb(245.4418604651, 208.8023255814, 127.5581395349);
|
||||
--color-olive-dark-1: rgb(128.7950819672, 215.2950819672, 121.7049180328);
|
||||
--color-green-dark-1: rgb(128.7950819672, 215.2950819672, 121.7049180328);
|
||||
--color-teal-dark-1: rgb(107.875, 215.125, 197.25);
|
||||
--color-blue-dark-1: rgb(88.0731707317, 148.9024390244, 247.9268292683);
|
||||
--color-violet-dark-1: rgb(129.6710526316, 146.3815789474, 253.3289473684);
|
||||
--color-purple-dark-1: rgb(175.6597938144, 119.206185567, 242.793814433);
|
||||
--color-pink-dark-1: rgb(237.8169014085, 150.1830985915, 213.7605633803);
|
||||
--color-brown-dark-1: rgb(231.4761904762, 164.5238095238, 164.5238095238);
|
||||
--color-black-dark-1: rgb(3.9344262295, 3.9344262295, 6.0655737705);
|
||||
/* dark 2 variants - produced via Sass scale-color(color, $lightness: -20%) */
|
||||
--color-red-dark-2: rgb(233.4375, 46.5625, 98.671875);
|
||||
--color-orange-dark-2: rgb(245.92, 116.984, 37.08);
|
||||
--color-yellow-dark-2: rgb(241.8837209302, 191.6046511628, 80.1162790698);
|
||||
--color-olive-dark-2: rgb(91.5901639344, 203.5901639344, 82.4098360656);
|
||||
--color-green-dark-2: rgb(91.5901639344, 203.5901639344, 82.4098360656);
|
||||
--color-teal-dark-2: rgb(67.75, 204.25, 181.5);
|
||||
--color-blue-dark-2: rgb(39.1463414634, 117.8048780488, 245.8536585366);
|
||||
--color-violet-dark-2: rgb(79.3421052632, 102.7631578947, 252.6578947368);
|
||||
--color-purple-dark-2: rgb(148.3195876289, 72.412371134, 238.587628866);
|
||||
--color-pink-dark-2: rgb(230.6338028169, 106.3661971831, 196.5211267606);
|
||||
--color-brown-dark-2: rgb(220.9523809524, 124.0476190476, 124.0476190476);
|
||||
--color-black-dark-2: hsl(240, 21.3114754098%, -8.0392156863%);
|
||||
/* other colors */
|
||||
--color-gold: #f5e0dc;
|
||||
--color-white: #cdd6f4;
|
||||
--color-diff-removed-word-bg: rgba(243, 139, 168, 0.15);
|
||||
--color-diff-added-word-bg: rgba(166, 227, 161, 0.15);
|
||||
--color-diff-removed-row-bg: rgba(243, 139, 168, 0.07);
|
||||
--color-diff-moved-row-bg: rgba(249, 226, 175, 0.07);
|
||||
--color-diff-added-row-bg: rgba(166, 227, 161, 0.07);
|
||||
--color-diff-removed-row-border: rgba(243, 139, 168, 0.07);
|
||||
--color-diff-moved-row-border: rgba(249, 226, 175, 0.07);
|
||||
--color-diff-added-row-border: rgba(166, 227, 161, 0.07);
|
||||
--color-diff-inactive: #9399b2;
|
||||
--color-error-border: #f38ba8;
|
||||
--color-error-bg: #f38ba8;
|
||||
--color-error-bg-active: rgb(240.609375, 115.890625, 150.66796875);
|
||||
--color-error-bg-hover: rgb(238.21875, 92.78125, 133.3359375);
|
||||
--color-error-text: #11111b;
|
||||
--color-success-border: rgb(128.7950819672, 215.2950819672, 121.7049180328);
|
||||
--color-success-bg: #a6e3a1;
|
||||
--color-success-text: #11111b;
|
||||
--color-warning-border: rgb(245.4418604651, 208.8023255814, 127.5581395349);
|
||||
--color-warning-bg: #f9e2af;
|
||||
--color-warning-text: #11111b;
|
||||
--color-info-border: rgb(88.0731707317, 148.9024390244, 247.9268292683);
|
||||
--color-info-bg: #11111b;
|
||||
--color-info-text: #cdd6f4;
|
||||
--color-red-badge: rgb(238.21875, 92.78125, 133.3359375);
|
||||
--color-red-badge-bg: #11111b;
|
||||
--color-red-badge-hover-bg: rgb(240.609375, 115.890625, 150.66796875);
|
||||
--color-green-badge: #a6e3a1;
|
||||
--color-green-badge-bg: #a6e3a1;
|
||||
--color-green-badge-hover-bg: rgb(147.3975409836, 221.1475409836, 141.3524590164);
|
||||
--color-yellow-badge: #f9e2af;
|
||||
--color-yellow-badge-bg: #11111b;
|
||||
--color-yellow-badge-hover-bg: rgb(247.2209302326, 217.4011627907, 151.2790697674);
|
||||
--color-orange-badge: #fab387;
|
||||
--color-orange-badge-bg: #11111b;
|
||||
--color-orange-badge-hover-bg: rgb(248.98, 163.496, 110.52);
|
||||
--color-git: #fab387;
|
||||
--color-highlight-bg: rgba(249, 226, 175, 0.15);
|
||||
/* target-based colors */
|
||||
--color-body: #11111b;
|
||||
--color-box-header: #181825;
|
||||
--color-box-body: #181825;
|
||||
--color-box-body-highlight: #313244;
|
||||
--color-text-dark: #a6adc8;
|
||||
--color-text: #cdd6f4;
|
||||
--color-text-light: #bac2de;
|
||||
--color-text-light-1: #bac2de;
|
||||
--color-text-light-2: #bac2de;
|
||||
--color-text-light-3: #bac2de;
|
||||
--color-footer: #181825;
|
||||
--color-timeline: #313244;
|
||||
--color-input-text: #cdd6f4;
|
||||
--color-input-background: #313244;
|
||||
--color-input-toggle-background: #313244;
|
||||
--color-input-border: #45475a;
|
||||
--color-input-border-hover: #585b70;
|
||||
--color-nav-bg: #181825;
|
||||
--color-nav-hover-bg: var(--color-hover);
|
||||
--color-navbar: #181825;
|
||||
--color-navbar-transparent: rgba(17, 17, 27, 0);
|
||||
--color-light: rgba(88, 91, 112, 0.3);
|
||||
--color-light-mimic-enabled: rgba(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
calc(40 / 255 * 222 / 255 / var(--opacity-disabled))
|
||||
);
|
||||
--color-light-border: #585b70;
|
||||
--color-hover: rgba(108, 112, 134, 0.2);
|
||||
--color-active: rgba(205, 214, 244, 0.1);
|
||||
--color-menu: #313244;
|
||||
--color-card: #313244;
|
||||
--color-markup-table-row: rgba(205, 214, 244, 0.02);
|
||||
--color-markup-code-block: rgba(205, 214, 244, 0.05);
|
||||
--color-markup-code-inline: #313244;
|
||||
--color-button: #313244;
|
||||
--color-code-bg: #1e1e2e;
|
||||
--color-code-sidebar-bg: #313244;
|
||||
--color-shadow: rgba(17, 17, 27, 0.1);
|
||||
--color-tooltip-bg: #313244;
|
||||
--color-tooltip-text: var(--color-text);
|
||||
--color-secondary-bg: #313244;
|
||||
--color-text-focus: #cdd6f4;
|
||||
--color-expand-button: #585b70;
|
||||
--color-placeholder-text: #a6adc8;
|
||||
--color-editor-line-highlight: var(--color-primary-light-5);
|
||||
--color-project-board-bg: var(--color-secondary-light-2);
|
||||
/* gitea source code: */
|
||||
/* should ideally be --color-text-dark, see go-gitea/gitea#15651 */
|
||||
--color-caret: var(--color-text);
|
||||
--color-reaction-bg: rgba(205, 214, 244, 0.07);
|
||||
--color-reaction-active-bg: var(--color-primary-alpha-40);
|
||||
--color-header-wrapper: #181825;
|
||||
--color-header-wrapper-transparent: rgba(24, 24, 37, 0);
|
||||
--color-label-text: #11111b;
|
||||
--color-label-bg: #b4befe;
|
||||
--color-label-hover-bg: rgb(149.8026315789, 163.8289473684, 253.5973684211);
|
||||
--color-label-active-bg: rgb(164.9013157895, 176.9144736842, 253.7986842105);
|
||||
--color-accent: var(--color-primary-light-1);
|
||||
--color-small-accent: var(--color-primary-light-5);
|
||||
--color-active-line: #45475a;
|
||||
}
|
||||
|
||||
/* invert emojis that are hard to read otherwise */
|
||||
.emoji[aria-label="check mark"],
|
||||
.emoji[aria-label="currency exchange"],
|
||||
.emoji[aria-label="TOP arrow"],
|
||||
.emoji[aria-label="END arrow"],
|
||||
.emoji[aria-label="ON! arrow"],
|
||||
.emoji[aria-label="SOON arrow"],
|
||||
.emoji[aria-label="heavy dollar sign"],
|
||||
.emoji[aria-label=copyright],
|
||||
.emoji[aria-label=registered],
|
||||
.emoji[aria-label="trade mark"],
|
||||
.emoji[aria-label=multiply],
|
||||
.emoji[aria-label=plus],
|
||||
.emoji[aria-label=minus],
|
||||
.emoji[aria-label=divide],
|
||||
.emoji[aria-label="curly loop"],
|
||||
.emoji[aria-label="double curly loop"],
|
||||
.emoji[aria-label="wavy dash"],
|
||||
.emoji[aria-label="paw prints"],
|
||||
.emoji[aria-label="musical note"],
|
||||
.emoji[aria-label="musical notes"] {
|
||||
filter: invert(100%) hue-rotate(180deg);
|
||||
}
|
||||
|
||||
.ui.ui.ui.button:not(.inverted, .basic).primary, .ui.ui.ui.button:not(.inverted, .basic).green, .ui.ui.ui.button:not(.inverted, .basic).red, .ui.ui.ui.button:not(.inverted, .basic).teal,
|
||||
.ui.ui.ui.label:not(.inverted, .basic).primary,
|
||||
.ui.ui.ui.label:not(.inverted, .basic).green,
|
||||
.ui.ui.ui.label:not(.inverted, .basic).red,
|
||||
.ui.ui.ui.label:not(.inverted, .basic).teal {
|
||||
color: #11111b;
|
||||
}
|
||||
.ui.ui.ui.button:not(.inverted, .basic).primary:hover, .ui.ui.ui.button:not(.inverted, .basic).green:hover, .ui.ui.ui.button:not(.inverted, .basic).red:hover, .ui.ui.ui.button:not(.inverted, .basic).teal:hover,
|
||||
.ui.ui.ui.label:not(.inverted, .basic).primary:hover,
|
||||
.ui.ui.ui.label:not(.inverted, .basic).green:hover,
|
||||
.ui.ui.ui.label:not(.inverted, .basic).red:hover,
|
||||
.ui.ui.ui.label:not(.inverted, .basic).teal:hover {
|
||||
color: #1e1e2e;
|
||||
}
|
||||
|
||||
.ui.basic.modal {
|
||||
background-color: #1e1e2e;
|
||||
}
|
||||
|
||||
.ui.commit-header-row .svg.gitea-lock ~ a {
|
||||
color: #11111b;
|
||||
}
|
||||
|
||||
.ui.negative.message .header {
|
||||
color: var(--color-error-text);
|
||||
}
|
||||
|
||||
.ui.sha.isSigned.isVerified .shortsha {
|
||||
color: #11111b;
|
||||
}
|
||||
.ui.sha.isSigned.isVerified svg.gitea-lock {
|
||||
fill: #11111b;
|
||||
}
|
||||
|
||||
.ui.basic.modal,
|
||||
.ui.basic.modal > .header,
|
||||
.ui.inverted.button {
|
||||
color: #cdd6f4 !important;
|
||||
}
|
||||
|
||||
#repo-topics,
|
||||
#topic_edit > .ui.selection.dropdown {
|
||||
color: var(--color-label-text) !important;
|
||||
}
|
||||
|
||||
blockquote.attention-tip {
|
||||
border-left-color: var(--color-success-bg);
|
||||
}
|
||||
blockquote.attention-warning {
|
||||
border-left-color: var(--color-warning-bg);
|
||||
}
|
||||
|
||||
svg.attention-tip,
|
||||
strong.attention-tip {
|
||||
color: var(--color-success-bg);
|
||||
}
|
||||
svg.attention-warning,
|
||||
strong.attention-warning {
|
||||
color: var(--color-warning-bg);
|
||||
}
|
||||
|
||||
.inline-code-block {
|
||||
color: #11111b;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: rgba(180, 190, 254, 0.3) !important;
|
||||
}
|
||||
|
||||
/* NameBuiltinPseudo */
|
||||
.chroma .bp {
|
||||
color: #fab387;
|
||||
}
|
||||
|
||||
/* Comment */
|
||||
.chroma .c {
|
||||
color: #6c7086;
|
||||
}
|
||||
|
||||
/* CommentSingle */
|
||||
.chroma .c1 {
|
||||
color: #6c7086;
|
||||
}
|
||||
|
||||
/* CommentHashbang */
|
||||
.chroma .ch {
|
||||
color: #6c7086;
|
||||
}
|
||||
|
||||
/* CommentMultiline */
|
||||
.chroma .cm {
|
||||
color: #6c7086;
|
||||
}
|
||||
|
||||
/* CommentPreproc */
|
||||
.chroma .cp {
|
||||
color: #89b4fa;
|
||||
}
|
||||
|
||||
/* CommentPreprocFile */
|
||||
.chroma .cpf {
|
||||
color: #89b4fa;
|
||||
}
|
||||
|
||||
/* CommentSpecial */
|
||||
.chroma .cs {
|
||||
color: #6c7086;
|
||||
}
|
||||
|
||||
/* LiteralStringDelimiter */
|
||||
.chroma .dl {
|
||||
color: #89b4fa;
|
||||
}
|
||||
|
||||
/* NameFunctionMagic */
|
||||
/* Generic */
|
||||
/* GenericDeleted */
|
||||
.chroma .gd {
|
||||
color: #cdd6f4;
|
||||
background-color: rgba(243, 139, 168, 0.15);
|
||||
}
|
||||
|
||||
/* GenericEmph */
|
||||
.chroma .ge {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* GenericHeading */
|
||||
.chroma .gh {
|
||||
color: #89dceb;
|
||||
}
|
||||
|
||||
/* GenericInserted */
|
||||
.chroma .gi {
|
||||
color: #cdd6f4;
|
||||
background-color: rgba(166, 227, 161, 0.15);
|
||||
}
|
||||
|
||||
/* GenericUnderline */
|
||||
/* GenericOutput */
|
||||
.chroma .go {
|
||||
color: #fab387;
|
||||
}
|
||||
|
||||
/* GenericPrompt */
|
||||
.chroma .gp {
|
||||
color: #6c7086;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* GenericError */
|
||||
.chroma .gr {
|
||||
color: #eba0ac;
|
||||
}
|
||||
|
||||
/* GenericStrong */
|
||||
.chroma .gs {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* GenericTraceback */
|
||||
.chroma .gt {
|
||||
color: #eba0ac;
|
||||
}
|
||||
|
||||
/* GenericSubheading */
|
||||
.chroma .gu {
|
||||
color: #89dceb;
|
||||
}
|
||||
|
||||
/* LiteralNumberIntegerLong */
|
||||
.chroma .il {
|
||||
color: #fab387;
|
||||
}
|
||||
|
||||
/* Keyword */
|
||||
.chroma .k {
|
||||
color: #cba6f7;
|
||||
}
|
||||
|
||||
/* KeywordConstant */
|
||||
.chroma .kc {
|
||||
color: #f9e2af;
|
||||
}
|
||||
|
||||
/* KeywordDeclaration */
|
||||
.chroma .kd {
|
||||
color: #cba6f7;
|
||||
}
|
||||
|
||||
/* KeywordNamespace */
|
||||
.chroma .kn {
|
||||
color: #f9e2af;
|
||||
}
|
||||
|
||||
/* KeywordPseudo */
|
||||
.chroma .kp {
|
||||
color: #cba6f7;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* KeywordReserved */
|
||||
.chroma .kr {
|
||||
color: #cba6f7;
|
||||
}
|
||||
|
||||
/* KeywordType */
|
||||
.chroma .kt {
|
||||
color: #f9e2af;
|
||||
}
|
||||
|
||||
/* Literal */
|
||||
/* LiteralDate */
|
||||
/* LiteralNumber */
|
||||
.chroma .m {
|
||||
color: #fab387;
|
||||
}
|
||||
|
||||
/* LiteralNumberBin */
|
||||
.chroma .mb {
|
||||
color: #fab387;
|
||||
}
|
||||
|
||||
/* LiteralNumberFloat */
|
||||
.chroma .mf {
|
||||
color: #fab387;
|
||||
}
|
||||
|
||||
/* LiteralNumberHex */
|
||||
.chroma .mh {
|
||||
color: #fab387;
|
||||
}
|
||||
|
||||
/* LiteralNumberInteger */
|
||||
.chroma .mi {
|
||||
color: #fab387;
|
||||
}
|
||||
|
||||
/* LiteralNumberOct */
|
||||
.chroma .mo {
|
||||
color: #fab387;
|
||||
}
|
||||
|
||||
/* Name */
|
||||
.chroma .n {
|
||||
color: #b4befe;
|
||||
}
|
||||
|
||||
/* NameAttribute */
|
||||
.chroma .na {
|
||||
color: #f9e2af;
|
||||
}
|
||||
|
||||
/* NameBuiltin */
|
||||
.chroma .nb {
|
||||
color: #fab387;
|
||||
}
|
||||
|
||||
/* NameClass */
|
||||
.chroma .nc {
|
||||
color: #f9e2af;
|
||||
}
|
||||
|
||||
/* NameDecorator */
|
||||
.chroma .nd {
|
||||
color: #f5c2e7;
|
||||
}
|
||||
|
||||
/* NameException */
|
||||
.chroma .ne {
|
||||
color: #eba0ac;
|
||||
}
|
||||
|
||||
/* NameFunction */
|
||||
.chroma .nf {
|
||||
color: #89b4fa;
|
||||
}
|
||||
|
||||
/* NameEntity */
|
||||
.chroma .ni {
|
||||
color: #f5c2e7;
|
||||
}
|
||||
|
||||
/* NameLabel */
|
||||
.chroma .nl {
|
||||
color: #f9e2af;
|
||||
}
|
||||
|
||||
/* NameNamespace */
|
||||
.chroma .nn {
|
||||
color: #f9e2af;
|
||||
}
|
||||
|
||||
/* NameConstant */
|
||||
.chroma .no {
|
||||
color: #f9e2af;
|
||||
}
|
||||
|
||||
/* NameTag */
|
||||
.chroma .nt {
|
||||
color: #cba6f7;
|
||||
}
|
||||
|
||||
/* NameVariable */
|
||||
.chroma .nv {
|
||||
color: #fab387;
|
||||
}
|
||||
|
||||
/* NameOther */
|
||||
.chroma .nx {
|
||||
color: #fab387;
|
||||
}
|
||||
|
||||
/* Operator */
|
||||
.chroma .o {
|
||||
color: #89dceb;
|
||||
}
|
||||
|
||||
/* OperatorWord */
|
||||
.chroma .ow {
|
||||
color: #89dceb;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Punctuation */
|
||||
.chroma .p {
|
||||
color: #9399b2;
|
||||
}
|
||||
|
||||
/* NameProperty */
|
||||
/* LiteralString */
|
||||
.chroma .s {
|
||||
color: #a6e3a1;
|
||||
}
|
||||
|
||||
/* LiteralStringSingle */
|
||||
.chroma .s1 {
|
||||
color: #a6e3a1;
|
||||
}
|
||||
|
||||
/* LiteralStringDouble */
|
||||
.chroma .s2 {
|
||||
color: #a6e3a1;
|
||||
}
|
||||
|
||||
/* LiteralStringAffix */
|
||||
.chroma .sa {
|
||||
color: #a6e3a1;
|
||||
}
|
||||
|
||||
/* LiteralStringBacktick */
|
||||
.chroma .sb {
|
||||
color: #a6e3a1;
|
||||
}
|
||||
|
||||
/* LiteralStringChar */
|
||||
.chroma .sc {
|
||||
color: #a6e3a1;
|
||||
}
|
||||
|
||||
/* LiteralStringDoc */
|
||||
.chroma .sd {
|
||||
color: #a6e3a1;
|
||||
}
|
||||
|
||||
/* LiteralStringEscape */
|
||||
.chroma .se {
|
||||
color: #89b4fa;
|
||||
}
|
||||
|
||||
/* LiteralStringHeredoc */
|
||||
.chroma .sh {
|
||||
color: #a6e3a1;
|
||||
}
|
||||
|
||||
/* LiteralStringInterpol */
|
||||
.chroma .si {
|
||||
color: #a6e3a1;
|
||||
}
|
||||
|
||||
/* LiteralStringRegex */
|
||||
.chroma .sr {
|
||||
color: #89b4fa;
|
||||
}
|
||||
|
||||
/* LiteralStringSymbol */
|
||||
.chroma .ss {
|
||||
color: #a6e3a1;
|
||||
}
|
||||
|
||||
/* LiteralStringOther */
|
||||
.chroma .sx {
|
||||
color: #a6e3a1;
|
||||
}
|
||||
|
||||
/* NameVariableClass */
|
||||
.chroma .vc {
|
||||
color: #f9e2af;
|
||||
}
|
||||
|
||||
/* NameVariableGlobal */
|
||||
.chroma .vg {
|
||||
color: #fab387;
|
||||
}
|
||||
|
||||
/* NameVariableInstance */
|
||||
.chroma .vi {
|
||||
color: #f9e2af;
|
||||
}
|
||||
|
||||
/* NameVariableMagic */
|
||||
/* TextWhitespace */
|
||||
.chroma .w {
|
||||
color: #313244;
|
||||
}
|
||||
|
||||
.CodeMirror .cm-property,
|
||||
.CodeMirror.cm-s-default .cm-property,
|
||||
.CodeMirror.cm-s-paper .cm-property {
|
||||
color: #cdd6f4;
|
||||
}
|
||||
.CodeMirror .cm-header,
|
||||
.CodeMirror.cm-s-default .cm-header,
|
||||
.CodeMirror.cm-s-paper .cm-header {
|
||||
color: #cdd6f4;
|
||||
}
|
||||
.CodeMirror .cm-quote,
|
||||
.CodeMirror.cm-s-default .cm-quote,
|
||||
.CodeMirror.cm-s-paper .cm-quote {
|
||||
color: #a6e3a1;
|
||||
}
|
||||
.CodeMirror .cm-keyword,
|
||||
.CodeMirror.cm-s-default .cm-keyword,
|
||||
.CodeMirror.cm-s-paper .cm-keyword {
|
||||
color: #cba6f7;
|
||||
}
|
||||
.CodeMirror .cm-atom,
|
||||
.CodeMirror.cm-s-default .cm-atom,
|
||||
.CodeMirror.cm-s-paper .cm-atom {
|
||||
color: #f38ba8;
|
||||
}
|
||||
.CodeMirror .cm-number,
|
||||
.CodeMirror.cm-s-default .cm-number,
|
||||
.CodeMirror.cm-s-paper .cm-number {
|
||||
color: #fab387;
|
||||
}
|
||||
.CodeMirror .cm-def,
|
||||
.CodeMirror.cm-s-default .cm-def,
|
||||
.CodeMirror.cm-s-paper .cm-def {
|
||||
color: #cdd6f4;
|
||||
}
|
||||
.CodeMirror .cm-variable-2,
|
||||
.CodeMirror.cm-s-default .cm-variable-2,
|
||||
.CodeMirror.cm-s-paper .cm-variable-2 {
|
||||
color: #89dceb;
|
||||
}
|
||||
.CodeMirror .cm-variable-3,
|
||||
.CodeMirror.cm-s-default .cm-variable-3,
|
||||
.CodeMirror.cm-s-paper .cm-variable-3 {
|
||||
color: #94e2d5;
|
||||
}
|
||||
.CodeMirror .cm-comment,
|
||||
.CodeMirror.cm-s-default .cm-comment,
|
||||
.CodeMirror.cm-s-paper .cm-comment {
|
||||
color: #585b70;
|
||||
}
|
||||
.CodeMirror .cm-string,
|
||||
.CodeMirror.cm-s-default .cm-string,
|
||||
.CodeMirror.cm-s-paper .cm-string {
|
||||
color: #a6e3a1;
|
||||
}
|
||||
.CodeMirror .cm-string-2,
|
||||
.CodeMirror.cm-s-default .cm-string-2,
|
||||
.CodeMirror.cm-s-paper .cm-string-2 {
|
||||
color: #a6e3a1;
|
||||
}
|
||||
.CodeMirror .cm-meta,
|
||||
.CodeMirror.cm-s-default .cm-meta,
|
||||
.CodeMirror.cm-s-paper .cm-meta {
|
||||
color: #fab387;
|
||||
}
|
||||
.CodeMirror .cm-qualifier,
|
||||
.CodeMirror.cm-s-default .cm-qualifier,
|
||||
.CodeMirror.cm-s-paper .cm-qualifier {
|
||||
color: #fab387;
|
||||
}
|
||||
.CodeMirror .cm-builtin,
|
||||
.CodeMirror.cm-s-default .cm-builtin,
|
||||
.CodeMirror.cm-s-paper .cm-builtin {
|
||||
color: #fab387;
|
||||
}
|
||||
.CodeMirror .cm-bracket,
|
||||
.CodeMirror.cm-s-default .cm-bracket,
|
||||
.CodeMirror.cm-s-paper .cm-bracket {
|
||||
color: #cdd6f4;
|
||||
}
|
||||
.CodeMirror .cm-tag,
|
||||
.CodeMirror.cm-s-default .cm-tag,
|
||||
.CodeMirror.cm-s-paper .cm-tag {
|
||||
color: #f9e2af;
|
||||
}
|
||||
.CodeMirror .cm-attribute,
|
||||
.CodeMirror.cm-s-default .cm-attribute,
|
||||
.CodeMirror.cm-s-paper .cm-attribute {
|
||||
color: #f9e2af;
|
||||
}
|
||||
.CodeMirror .cm-hr,
|
||||
.CodeMirror.cm-s-default .cm-hr,
|
||||
.CodeMirror.cm-s-paper .cm-hr {
|
||||
color: #9399b2;
|
||||
}
|
||||
.CodeMirror .cm-url,
|
||||
.CodeMirror.cm-s-default .cm-url,
|
||||
.CodeMirror.cm-s-paper .cm-url {
|
||||
color: #89b4fa;
|
||||
}
|
||||
.CodeMirror .cm-link,
|
||||
.CodeMirror.cm-s-default .cm-link,
|
||||
.CodeMirror.cm-s-paper .cm-link {
|
||||
color: #89b4fa;
|
||||
}
|
||||
.CodeMirror .cm-error,
|
||||
.CodeMirror.cm-s-default .cm-error,
|
||||
.CodeMirror.cm-s-paper .cm-error {
|
||||
color: #f38ba8;
|
||||
}
|
||||
|
||||
.monaco-editor .selected-text {
|
||||
background-color: #313244 !important;
|
||||
}
|
||||
.monaco-editor .margin-view-overlays .line-numbers {
|
||||
color: #a6adc8 !important;
|
||||
}
|
||||
.monaco-editor .line-numbers.active-line-number {
|
||||
color: #b4befe !important;
|
||||
}
|
||||
.monaco-editor .view-overlays .current-line,
|
||||
.monaco-editor .margin-view-overlays .current-line-margin {
|
||||
background-color: rgb(42.16, 42.8, 60.08) !important;
|
||||
}
|
||||
.monaco-editor .mtk1 {
|
||||
color: #cdd6f4 !important;
|
||||
}
|
||||
.monaco-editor .mtk2 {
|
||||
color: #ff69b4 !important;
|
||||
}
|
||||
.monaco-editor .mtk3 {
|
||||
color: #fab387 !important;
|
||||
}
|
||||
.monaco-editor .mtk4 {
|
||||
color: #94e2d5 !important;
|
||||
}
|
||||
.monaco-editor .mtk5 {
|
||||
color: #cdd6f4 !important;
|
||||
}
|
||||
.monaco-editor .mtk6 {
|
||||
color: #cba6f7 !important;
|
||||
}
|
||||
.monaco-editor .mtk7 {
|
||||
color: #fab387 !important;
|
||||
}
|
||||
.monaco-editor .mtk8 {
|
||||
color: #9399b2 !important;
|
||||
}
|
||||
.monaco-editor .mtk9 {
|
||||
color: #cba6f7 !important;
|
||||
}
|
||||
.monaco-editor .mtk10 {
|
||||
color: #a6adc8 !important;
|
||||
}
|
||||
.monaco-editor .mtk11 {
|
||||
color: #94e2d5 !important;
|
||||
}
|
||||
.monaco-editor .mtk12 {
|
||||
color: #94e2d5 !important;
|
||||
}
|
||||
.monaco-editor .mtk13 {
|
||||
color: #ff69b4 !important;
|
||||
}
|
||||
.monaco-editor .mtk14 {
|
||||
color: #ff69b4 !important;
|
||||
}
|
||||
.monaco-editor .mtk15 {
|
||||
color: #cba6f7 !important;
|
||||
}
|
||||
.monaco-editor .mtk16 {
|
||||
color: #9399b2 !important;
|
||||
}
|
||||
.monaco-editor .mtk17 {
|
||||
color: #ff69b4 !important;
|
||||
}
|
||||
.monaco-editor .mtk18 {
|
||||
color: #ff69b4 !important;
|
||||
}
|
||||
.monaco-editor .mtk19 {
|
||||
color: #94e2d5 !important;
|
||||
}
|
||||
.monaco-editor .mtk20 {
|
||||
color: #ff69b4 !important;
|
||||
}
|
||||
.monaco-editor .mtk21 {
|
||||
color: #a6e3a1 !important;
|
||||
}
|
||||
.monaco-editor .mtk22 {
|
||||
color: #ff69b4 !important;
|
||||
}
|
||||
.monaco-editor .mtk23 {
|
||||
color: #89b4fa !important;
|
||||
}
|
||||
.monaco-editor .mtk24 {
|
||||
color: #fab387 !important;
|
||||
}
|
||||
.monaco-editor .mtk25 {
|
||||
color: #f5c2e7 !important;
|
||||
}
|
||||
.monaco-editor .bracket-highlighting-0 {
|
||||
color: rgb(227.8, 169, 198.4) !important;
|
||||
}
|
||||
.monaco-editor .bracket-highlighting-1 {
|
||||
color: rgb(232, 193, 178.6) !important;
|
||||
}
|
||||
.monaco-editor .bracket-highlighting-2 {
|
||||
color: rgb(231.4, 221.2, 202.6) !important;
|
||||
}
|
||||
.monaco-editor .bracket-highlighting-3 {
|
||||
color: rgb(181.6, 221.8, 194.2) !important;
|
||||
}
|
||||
.monaco-editor .bracket-highlighting-4 {
|
||||
color: rgb(164.2, 193.6, 247.6) !important;
|
||||
}
|
||||
.monaco-editor .bracket-highlighting-5 {
|
||||
color: rgb(203.8, 185.2, 245.8) !important;
|
||||
}
|
||||
18
services/forgejo/tmpfiles.nix
Normal file
18
services/forgejo/tmpfiles.nix
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.machine.forgejo;
|
||||
customDir = config.services.forgejo.customDir;
|
||||
in
|
||||
with lib; mkIf cfg.enable {
|
||||
systemd.tmpfiles.rules = [
|
||||
"d '${customDir}/templates' - forgejo forgejo - -"
|
||||
"d '${customDir}/public' - forgejo forgejo - -"
|
||||
"d '${customDir}/public/assets' - forgejo forgejo - -"
|
||||
"d '${customDir}/public/assets/css' - forgejo forgejo - -"
|
||||
"C+ '${customDir}/public/assets/css/theme-catppuccin-mocha.css' - forgejo forgejo - ${./themes/theme-catppuccin-mocha-lavender.css}"
|
||||
];
|
||||
}
|
||||
9
services/mail/default.nix
Normal file
9
services/mail/default.nix
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
imports = [
|
||||
./options.nix
|
||||
./rspamd.nix
|
||||
./secrets.nix
|
||||
./service.nix
|
||||
./tmpfiles.nix
|
||||
];
|
||||
}
|
||||
23
services/mail/options.nix
Normal file
23
services/mail/options.nix
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
mkEnableOption
|
||||
mkOption
|
||||
types
|
||||
;
|
||||
in
|
||||
{
|
||||
options.machine.mail = {
|
||||
enable = mkEnableOption "Mail";
|
||||
domain = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Domain name";
|
||||
};
|
||||
fqdn = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "fqdn";
|
||||
};
|
||||
};
|
||||
}
|
||||
20
services/mail/rspamd.nix
Normal file
20
services/mail/rspamd.nix
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.mail) enable;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
services.rspamd = {
|
||||
enable = true;
|
||||
overrides = {
|
||||
"local.d/greylist.conf" = {
|
||||
text = ''
|
||||
enabled = false;
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
25
services/mail/secrets.nix
Normal file
25
services/mail/secrets.nix
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
sops.secrets = {
|
||||
dkim_default_private = {
|
||||
key = "dkim_default_private";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
mode = "0600";
|
||||
};
|
||||
"rus07tam/hashedPassword" = {
|
||||
sopsFile = ./../../secrets/rus07tam.yaml;
|
||||
};
|
||||
"mail/serviceHashedPassword" = {
|
||||
sopsFile = ./../../secrets/common.yaml;
|
||||
};
|
||||
"mail/servicePassword" = {
|
||||
sopsFile = ./../../secrets/common.yaml;
|
||||
};
|
||||
"mail/NikitaHapanulStaff228HashedPassword" = {
|
||||
sopsFile = ./../../secrets/common.yaml;
|
||||
};
|
||||
"mail/nikitapocox6prohype" = {
|
||||
sopsFile = ./../../secrets/common.yaml;
|
||||
};
|
||||
};
|
||||
}
|
||||
66
services/mail/service.nix
Normal file
66
services/mail/service.nix
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
{ config, lib, ... }:
|
||||
let
|
||||
sec = config.sops.secrets;
|
||||
inherit (config.machine.mail)
|
||||
enable
|
||||
domain
|
||||
fqdn
|
||||
;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(builtins.fetchTarball {
|
||||
url = "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/master/nixos-mailserver-master.tar.gz";
|
||||
sha256 = "0vnczps1ns4d7b3l5m4qwp2fp567pr6b38w40h1x48qfw70x8mf0";
|
||||
})
|
||||
];
|
||||
|
||||
mailserver = {
|
||||
inherit enable;
|
||||
inherit fqdn;
|
||||
stateVersion = 5;
|
||||
domains = [ domain ];
|
||||
messageSizeLimit = 52428800; # 50MB
|
||||
enableManageSieve = true;
|
||||
|
||||
localDnsResolver = false;
|
||||
|
||||
accounts = {
|
||||
"admin@${domain}" = {
|
||||
hashedPasswordFile = sec."mail/serviceHashedPassword".path;
|
||||
aliases = [
|
||||
"postmaster@${domain}"
|
||||
"system@${domain}"
|
||||
"contact@${domain}"
|
||||
"sales@${domain}"
|
||||
];
|
||||
};
|
||||
"rus07tam@${domain}" = {
|
||||
hashedPasswordFile = sec."rus07tam/hashedPassword".path;
|
||||
aliases = [ ];
|
||||
};
|
||||
"nikitahapanulstaff228@${domain}" = {
|
||||
hashedPasswordFile = sec."mail/NikitaHapanulStaff228HashedPassword".path;
|
||||
aliases = [
|
||||
"mamutraxal@${domain}"
|
||||
"sava.uwu@${domain}"
|
||||
];
|
||||
};
|
||||
"nikitapocox6prohype@${domain}" = {
|
||||
hashedPasswordFile = sec."mail/nikitapocox6prohype".path;
|
||||
aliases = [
|
||||
"pomoemuhype@${domain}"
|
||||
"nikita.uwu@${domain}"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
x509.useACMEHost = config.mailserver.fqdn;
|
||||
|
||||
dkim = {
|
||||
enable = true;
|
||||
keyDirectory = "/var/dkim";
|
||||
defaults.selector = "default";
|
||||
};
|
||||
};
|
||||
}
|
||||
14
services/mail/tmpfiles.nix
Normal file
14
services/mail/tmpfiles.nix
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
sec,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.mail) enable;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
systemd.tmpfiles.rules = [
|
||||
"C /var/dkim/default.private 0600 root root - - ${sec.dkim_default_private.path}"
|
||||
];
|
||||
}
|
||||
7
services/minecraft-server/default.nix
Normal file
7
services/minecraft-server/default.nix
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
imports = [
|
||||
./options.nix
|
||||
./firewall.nix
|
||||
./service.nix
|
||||
];
|
||||
}
|
||||
16
services/minecraft-server/firewall.nix
Normal file
16
services/minecraft-server/firewall.nix
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.minecraft-server)
|
||||
enable
|
||||
port
|
||||
;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
port
|
||||
];
|
||||
}
|
||||
12
services/minecraft-server/options.nix
Normal file
12
services/minecraft-server/options.nix
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{ lib, ... }:
|
||||
with lib;
|
||||
{
|
||||
options.machine.minecraft-server = {
|
||||
enable = mkEnableOption "Minecraft Server";
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 25565;
|
||||
description = "Listen port.";
|
||||
};
|
||||
};
|
||||
}
|
||||
46
services/minecraft-server/service.nix
Normal file
46
services/minecraft-server/service.nix
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.minecraft-server)
|
||||
enable
|
||||
port
|
||||
;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
services.minecraft-server = {
|
||||
inherit enable;
|
||||
dataDir = "/var/lib/minecraft";
|
||||
declarative = true;
|
||||
eula = true;
|
||||
jvmOpts = "-Xms2046M -Xmx2046M -XX:+UseZGC";
|
||||
package = pkgs.papermcServers.papermc-1_21;
|
||||
serverProperties = {
|
||||
server-port = port;
|
||||
query-port = port;
|
||||
allow-flight = false;
|
||||
broadcast-console-to-ops = false;
|
||||
broadcast-rcon-to-ops = false;
|
||||
difficulty = "hard";
|
||||
enable-query = true;
|
||||
enable-rcon = false;
|
||||
enable-status = true;
|
||||
enforce-whitelist = false;
|
||||
force-gamemode = true;
|
||||
gamemode = "survival";
|
||||
log-ips = false;
|
||||
max-players = 20;
|
||||
max-tick-time = -1;
|
||||
motd = "RuJect's Minecraft Server";
|
||||
online-mode = false;
|
||||
op-permission-level = 4;
|
||||
simulation-distance = 16;
|
||||
view-distance = 16;
|
||||
spawn-protection = 0;
|
||||
white-list = false;
|
||||
};
|
||||
};
|
||||
}
|
||||
7
services/mysql/default.nix
Normal file
7
services/mysql/default.nix
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
imports = [
|
||||
./firewall.nix
|
||||
./options.nix
|
||||
./service.nix
|
||||
];
|
||||
}
|
||||
13
services/mysql/firewall.nix
Normal file
13
services/mysql/firewall.nix
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.machine.mysql;
|
||||
in
|
||||
with lib; mkIf cfg.enable {
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [ cfg.port ];
|
||||
};
|
||||
}
|
||||
12
services/mysql/options.nix
Normal file
12
services/mysql/options.nix
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{ lib, ... }:
|
||||
with lib;
|
||||
{
|
||||
options.machine.mysql = {
|
||||
enable = mkEnableOption "Postgresql";
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 3306;
|
||||
description = "The port on which listens.";
|
||||
};
|
||||
};
|
||||
}
|
||||
22
services/mysql/service.nix
Normal file
22
services/mysql/service.nix
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.mysql)
|
||||
port
|
||||
enable
|
||||
;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
services.mysql = {
|
||||
inherit enable;
|
||||
package = pkgs.mysql84;
|
||||
ensureDatabases = [ ];
|
||||
settings.mysqld = {
|
||||
inherit port;
|
||||
};
|
||||
};
|
||||
}
|
||||
6
services/navidrome/default.nix
Normal file
6
services/navidrome/default.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
imports = [
|
||||
./options.nix
|
||||
./service.nix
|
||||
];
|
||||
}
|
||||
22
services/navidrome/options.nix
Normal file
22
services/navidrome/options.nix
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{ lib, ... }:
|
||||
with lib;
|
||||
{
|
||||
options.machine.navidrome = {
|
||||
enable = mkEnableOption "Navidrome";
|
||||
domain = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Domain name. If not set, will be disabled, and use the localhost.";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 4533;
|
||||
description = "Listen port.";
|
||||
};
|
||||
folder = mkOption {
|
||||
type = types.str;
|
||||
default = "/mnt/music";
|
||||
description = "Navidrome music folder.";
|
||||
};
|
||||
};
|
||||
}
|
||||
36
services/navidrome/service.nix
Normal file
36
services/navidrome/service.nix
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.navidrome)
|
||||
enable
|
||||
domain
|
||||
port
|
||||
folder
|
||||
;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
services.nginx.virtualHosts = with lib; mkIf (domain != null) {
|
||||
"${domain}" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
locations."/" = {
|
||||
proxyPass = "http://[::1]:${toString port}";
|
||||
proxyWebsockets = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.navidrome = {
|
||||
inherit enable;
|
||||
settings = {
|
||||
Address = "0.0.0.0";
|
||||
Port = port;
|
||||
MusicFolder = folder;
|
||||
DefaultTheme = "AMusic";
|
||||
EnableSharing = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
6
services/networking/default.nix
Normal file
6
services/networking/default.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
imports = [
|
||||
./options.nix
|
||||
./service.nix
|
||||
];
|
||||
}
|
||||
17
services/networking/options.nix
Normal file
17
services/networking/options.nix
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{ lib, ... }:
|
||||
with lib;
|
||||
{
|
||||
options.machine = {
|
||||
gateway = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Default network gateway";
|
||||
};
|
||||
|
||||
ipv4 = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Main IPv4 address";
|
||||
};
|
||||
};
|
||||
}
|
||||
32
services/networking/service.nix
Normal file
32
services/networking/service.nix
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{ config, lib, hostname, ... }:
|
||||
let
|
||||
inherit (config.machine) ipv4 gateway;
|
||||
in
|
||||
with lib; {
|
||||
networking = {
|
||||
hostName = hostname;
|
||||
networkmanager.enable = mkDefault true;
|
||||
firewall = {
|
||||
enable = true;
|
||||
allowPing = true;
|
||||
};
|
||||
nameservers = [
|
||||
"1.1.1.1"
|
||||
"1.0.0.1"
|
||||
"9.9.9.9"
|
||||
"8.8.8.8"
|
||||
];
|
||||
interfaces.ens3 = mkIf (ipv4 != null) {
|
||||
ipv4.addresses = [
|
||||
{
|
||||
address = ipv4;
|
||||
prefixLength = 32;
|
||||
}
|
||||
];
|
||||
};
|
||||
defaultGateway = mkIf (gateway != null) {
|
||||
address = gateway;
|
||||
interface = "ens3";
|
||||
};
|
||||
};
|
||||
}
|
||||
32
services/nextcloud/database.nix
Normal file
32
services/nextcloud/database.nix
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
pgsqlEnable = config.machine.postgresql.enable;
|
||||
cfg = config.machine.nextcloud;
|
||||
in
|
||||
with lib; mkIf cfg.enable {
|
||||
services.nextcloud.config =
|
||||
if pgsqlEnable then
|
||||
{
|
||||
dbtype = "pgsql";
|
||||
dbhost = "localhost:${toString config.machine.postgresql.port}";
|
||||
}
|
||||
else
|
||||
{
|
||||
dbtype = "sqlite";
|
||||
dbhost = "localhost";
|
||||
};
|
||||
|
||||
services.postgresql = with lib; mkIf pgsqlEnable {
|
||||
ensureDatabases = [ "nextcloud" ];
|
||||
ensureUsers = [
|
||||
{
|
||||
name = "nextcloud";
|
||||
ensureDBOwnership = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
8
services/nextcloud/default.nix
Normal file
8
services/nextcloud/default.nix
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
imports = [
|
||||
./database.nix
|
||||
./mail.nix
|
||||
./options.nix
|
||||
./service.nix
|
||||
];
|
||||
}
|
||||
40
services/nextcloud/mail.nix
Normal file
40
services/nextcloud/mail.nix
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
sec,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.nextcloud)
|
||||
enable
|
||||
host
|
||||
;
|
||||
address = "noreply@${host}";
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
services.nextcloud = {
|
||||
settings = {
|
||||
mail_smtpmode = "smtp";
|
||||
mail_sendmailmode = "smtp";
|
||||
mail_smtpsecure = "ssl";
|
||||
mail_domain = host;
|
||||
mail_from_address = "noreply";
|
||||
mail_smtpname = address;
|
||||
mail_smtphost = config.machine.mail.fqdn;
|
||||
mail_smtpport = 465;
|
||||
mail_smtpauth = true;
|
||||
};
|
||||
secrets = {
|
||||
mail_smtppassword = sec."mail/servicePassword".path;
|
||||
};
|
||||
};
|
||||
|
||||
mailserver = {
|
||||
domains = [ host ];
|
||||
accounts.${address} = {
|
||||
hashedPasswordFile = sec."mail/serviceHashedPassword".path;
|
||||
aliases = [ ];
|
||||
sendOnly = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
12
services/nextcloud/options.nix
Normal file
12
services/nextcloud/options.nix
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{ lib, ... }:
|
||||
with lib;
|
||||
{
|
||||
options.machine.nextcloud = {
|
||||
enable = mkEnableOption "Nextcloud";
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
default = "localhost";
|
||||
description = "FQDN for the nextcloud instance.";
|
||||
};
|
||||
};
|
||||
}
|
||||
44
services/nextcloud/service.nix
Normal file
44
services/nextcloud/service.nix
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
sec,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.machine.nextcloud;
|
||||
in
|
||||
with lib; mkIf cfg.enable {
|
||||
services.nextcloud = {
|
||||
inherit enable;
|
||||
appstoreEnable = false;
|
||||
autoUpdateApps.enable = false;
|
||||
config.adminpassFile = sec."nextcloud/adminPassword".path;
|
||||
hostName = cfg.host;
|
||||
package = pkgs.nextcloud33;
|
||||
https = if cfg.host == "localhost" then false else true;
|
||||
settings = {
|
||||
default_phone_region = "RU";
|
||||
log_type = "file";
|
||||
loglevel = 1;
|
||||
};
|
||||
extraAppsEnable = true;
|
||||
extraApps = with pkgs.nextcloud33Packages.apps; {
|
||||
inherit
|
||||
mail
|
||||
contacts
|
||||
collectives
|
||||
impersonate
|
||||
;
|
||||
};
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts.${config.services.nextcloud.hostName} = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
};
|
||||
|
||||
sops.secrets = {
|
||||
"nextcloud/adminPassword" = { };
|
||||
};
|
||||
}
|
||||
10
services/nginx/acme.nix
Normal file
10
services/nginx/acme.nix
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{ lib, config, ... }:
|
||||
with lib; mkIf (config.nginx.enable) {
|
||||
security.acme = {
|
||||
acceptTerms = true;
|
||||
defaults = {
|
||||
email = "admin@ruject.fun";
|
||||
webroot = "/var/lib/acme/acme-challenge/";
|
||||
};
|
||||
};
|
||||
}
|
||||
7
services/nginx/default.nix
Normal file
7
services/nginx/default.nix
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
imports = [
|
||||
./acme.nix
|
||||
./firewall.nix
|
||||
./service.nix
|
||||
];
|
||||
}
|
||||
14
services/nginx/firewall.nix
Normal file
14
services/nginx/firewall.nix
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.services.nginx) enable;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
80
|
||||
443
|
||||
];
|
||||
}
|
||||
12
services/nginx/service.nix
Normal file
12
services/nginx/service.nix
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
users.groups.acme.members = [
|
||||
"nginx"
|
||||
];
|
||||
|
||||
services.nginx = {
|
||||
recommendedProxySettings = true;
|
||||
recommendedTlsSettings = true;
|
||||
recommendedOptimisation = true;
|
||||
recommendedGzipSettings = true;
|
||||
};
|
||||
}
|
||||
6
services/postgresql/default.nix
Normal file
6
services/postgresql/default.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
imports = [
|
||||
./options.nix
|
||||
./service.nix
|
||||
];
|
||||
}
|
||||
14
services/postgresql/options.nix
Normal file
14
services/postgresql/options.nix
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{ lib, ... }:
|
||||
with lib;
|
||||
{
|
||||
options.machine.postgresql = {
|
||||
enable = mkEnableOption "Postgresql";
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 5432;
|
||||
description = ''
|
||||
The port on which PostgreSQL listens.
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
29
services/postgresql/service.nix
Normal file
29
services/postgresql/service.nix
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.postgresql)
|
||||
port
|
||||
enable
|
||||
;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
services.postgresql = {
|
||||
inherit enable;
|
||||
settings = {
|
||||
inherit port;
|
||||
};
|
||||
ensureDatabases = [ ];
|
||||
enableTCPIP = true;
|
||||
authentication = mkOverride 10 ''
|
||||
#type database DBuser origin-address auth-method
|
||||
local all all trust
|
||||
host all all 127.0.0.1/32 trust
|
||||
host all all ::1/128 trust
|
||||
host all all 0.0.0.0/0 md5
|
||||
'';
|
||||
};
|
||||
}
|
||||
6
services/prometheus/default.nix
Normal file
6
services/prometheus/default.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
imports = [
|
||||
./options.nix
|
||||
./service.nix
|
||||
];
|
||||
}
|
||||
15
services/prometheus/options.nix
Normal file
15
services/prometheus/options.nix
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
options.machine.prometheus = {
|
||||
enable = mkEnableOption "Prometheus";
|
||||
domain = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Domain name. If not set, will be disabled, and use the localhost.";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = cfg.port;
|
||||
description = "Listen port.";
|
||||
};
|
||||
};
|
||||
}
|
||||
16
services/prometheus/service.nix
Normal file
16
services/prometheus/service.nix
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.prometheus)
|
||||
enable
|
||||
port
|
||||
;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
services.prometheus = {
|
||||
inherit enable port;
|
||||
};
|
||||
}
|
||||
8
services/prosody/default.nix
Normal file
8
services/prosody/default.nix
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
imports = [
|
||||
./firewall.nix
|
||||
./nginx.nix
|
||||
./options.nix
|
||||
./service.nix
|
||||
];
|
||||
}
|
||||
38
services/prosody/firewall.nix
Normal file
38
services/prosody/firewall.nix
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.prosody)
|
||||
enable
|
||||
;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [
|
||||
# HTTP filer
|
||||
80
|
||||
443
|
||||
|
||||
# C2S
|
||||
5222
|
||||
5223
|
||||
|
||||
# S2S
|
||||
5269
|
||||
5270
|
||||
|
||||
# WebSockets / BOSH
|
||||
5280
|
||||
5281
|
||||
]
|
||||
++ concatLists (
|
||||
with config.services.prosody;
|
||||
[
|
||||
httpPorts
|
||||
httpsPorts
|
||||
]
|
||||
);
|
||||
};
|
||||
}
|
||||
54
services/prosody/nginx.nix
Normal file
54
services/prosody/nginx.nix
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.prosody)
|
||||
enable
|
||||
domain
|
||||
;
|
||||
|
||||
localhost = "http://localhost:5280";
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
security.acme.certs."${domain}".extraDomainNames = [
|
||||
"conference.${domain}"
|
||||
"upload.${domain}"
|
||||
];
|
||||
users.groups.acme.members = [
|
||||
"prosody"
|
||||
];
|
||||
services.nginx.virtualHosts = with lib; mkIf (domain != null) {
|
||||
"${domain}".locations = {
|
||||
"= /xmpp-websocket" = {
|
||||
proxyPass = localhost;
|
||||
proxyWebsockets = true;
|
||||
};
|
||||
"= /http-bind".proxyPass = localhost;
|
||||
"/push".proxyPass = localhost;
|
||||
"= /.well-known/host-meta".proxyPass = localhost;
|
||||
"= /.well-known/host-meta.json".proxyPass = localhost;
|
||||
};
|
||||
"conference.${domain}" = {
|
||||
http3 = true;
|
||||
quic = true;
|
||||
forceSSL = true;
|
||||
kTLS = true;
|
||||
useACMEHost = domain;
|
||||
sslCertificate = "${config.security.acme.certs.${domain}.directory}/fullchain.pem";
|
||||
sslCertificateKey = "${config.security.acme.certs.${domain}.directory}/key.pem";
|
||||
locations."/".proxyPass = localhost;
|
||||
};
|
||||
"upload.${domain}" = {
|
||||
http3 = true;
|
||||
quic = true;
|
||||
forceSSL = true;
|
||||
kTLS = true;
|
||||
useACMEHost = domain;
|
||||
sslCertificate = "${config.security.acme.certs.${domain}.directory}/fullchain.pem";
|
||||
sslCertificateKey = "${config.security.acme.certs.${domain}.directory}/key.pem";
|
||||
locations."/".proxyPass = localhost;
|
||||
};
|
||||
};
|
||||
}
|
||||
17
services/prosody/options.nix
Normal file
17
services/prosody/options.nix
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{ lib, ... }:
|
||||
with lib;
|
||||
{
|
||||
options.machine.prosody = {
|
||||
enable = mkEnableOption "Prosody";
|
||||
domain = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Domain name. If not set, will be disabled, and use the localhost.";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 4000;
|
||||
description = "Listen port.";
|
||||
};
|
||||
};
|
||||
}
|
||||
104
services/prosody/service.nix
Normal file
104
services/prosody/service.nix
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.prosody)
|
||||
enable
|
||||
domain
|
||||
;
|
||||
|
||||
sslCertDir = config.security.acme.certs."${domain}".directory;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
services.prosody = {
|
||||
inherit enable;
|
||||
|
||||
package = pkgs.prosody.override {
|
||||
withCommunityModules = [
|
||||
"sasl2"
|
||||
"sasl2_bind2"
|
||||
"sasl_ssdp"
|
||||
"sasl2_fast"
|
||||
"sasl_ssdp"
|
||||
"csi_battery_saver"
|
||||
"muc_notifications"
|
||||
];
|
||||
};
|
||||
|
||||
admins = [
|
||||
"admin@${domain}"
|
||||
];
|
||||
allowRegistration = true;
|
||||
s2sSecureAuth = true;
|
||||
c2sRequireEncryption = true;
|
||||
modules = {
|
||||
http_files = true;
|
||||
limits = true;
|
||||
server_contact_info = true;
|
||||
bosh = true;
|
||||
motd = true;
|
||||
announce = true;
|
||||
welcome = true;
|
||||
admin_adhoc = true;
|
||||
websocket = true;
|
||||
watchregistrations = true;
|
||||
};
|
||||
extraModules = [
|
||||
"turn_external"
|
||||
];
|
||||
xmppComplianceSuite = true;
|
||||
checkConfig = false;
|
||||
ssl = {
|
||||
cert = "${sslCertDir}/fullchain.pem";
|
||||
key = "${sslCertDir}/key.pem";
|
||||
};
|
||||
virtualHosts.${domain} = {
|
||||
inherit domain;
|
||||
enabled = enable;
|
||||
ssl = {
|
||||
cert = "${sslCertDir}/fullchain.pem";
|
||||
key = "${sslCertDir}/key.pem";
|
||||
};
|
||||
};
|
||||
muc = [
|
||||
{
|
||||
domain = "conference.${domain}";
|
||||
restrictRoomCreation = "local";
|
||||
}
|
||||
];
|
||||
httpFileShare = {
|
||||
domain = "upload.${domain}";
|
||||
http_host = domain;
|
||||
expires_after = "never";
|
||||
size_limit = 32 * 1024 * 1024;
|
||||
};
|
||||
extraConfig = ''
|
||||
storage = "sql"
|
||||
sql = {
|
||||
driver = "SQLite3";
|
||||
database = "prosody.sqlite";
|
||||
}
|
||||
|
||||
-- Keep messages
|
||||
archive_expires_after = "never"
|
||||
muc_log_presences = true
|
||||
muc_log_expires_after = "never"
|
||||
|
||||
-- Recommended by Monal dev
|
||||
smacks_max_queue_size = 4000
|
||||
|
||||
c2s_direct_tls_ports = { 5223 };
|
||||
s2s_direct_tls_ports = { 5270 };
|
||||
|
||||
trusted_proxies = { "127.0.0.1", "::1" };
|
||||
http_external_url = "https://${domain}/"
|
||||
consider_bosh_secure = true;
|
||||
consider_websocket_secure = true;
|
||||
statistics = "internal";
|
||||
statistics_interval = "manual";
|
||||
'';
|
||||
};
|
||||
}
|
||||
6
services/redis/default.nix
Normal file
6
services/redis/default.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
imports = [
|
||||
./options.nix
|
||||
./service.nix
|
||||
];
|
||||
}
|
||||
12
services/redis/options.nix
Normal file
12
services/redis/options.nix
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{ lib, ... }:
|
||||
with lib;
|
||||
{
|
||||
options.machine.redis = {
|
||||
enable = mkEnableOption "Redis";
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 6379;
|
||||
description = "Port to listen on.";
|
||||
};
|
||||
};
|
||||
}
|
||||
28
services/redis/service.nix
Normal file
28
services/redis/service.nix
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
sec,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.redis)
|
||||
port
|
||||
enable
|
||||
;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
sops.secrets = {
|
||||
"redis/password" = { };
|
||||
};
|
||||
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [ port ];
|
||||
};
|
||||
|
||||
services.redis.servers."default" = {
|
||||
inherit enable;
|
||||
inherit port;
|
||||
bind = null;
|
||||
requirePassFile = sec."redis/password".path;
|
||||
};
|
||||
}
|
||||
6
services/roundcube/default.nix
Normal file
6
services/roundcube/default.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
imports = [
|
||||
./options.nix
|
||||
./service.nix
|
||||
];
|
||||
}
|
||||
12
services/roundcube/options.nix
Normal file
12
services/roundcube/options.nix
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{ lib, ... }:
|
||||
with lib;
|
||||
{
|
||||
options.machine.roundcube = {
|
||||
enable = mkEnableOption "Roundcube";
|
||||
domain = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Domain name. If not set, will be disabled, and use the localhost.";
|
||||
};
|
||||
};
|
||||
}
|
||||
45
services/roundcube/service.nix
Normal file
45
services/roundcube/service.nix
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.roundcube)
|
||||
enable
|
||||
domain
|
||||
;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
services.roundcube = {
|
||||
inherit enable;
|
||||
dicts = with pkgs.aspellDicts; [
|
||||
ru
|
||||
en
|
||||
fr
|
||||
];
|
||||
hostName = domain;
|
||||
configureNginx = true;
|
||||
extraConfig = ''
|
||||
// IMAP
|
||||
$config['imap_host'] = 'ssl://${config.mailserver.fqdn}';
|
||||
$config['imap_conn_options'] = array(
|
||||
'ssl' => array(
|
||||
'verify_peer' => false,
|
||||
'verify_peer_name' => false,
|
||||
),
|
||||
);
|
||||
|
||||
// SMTP
|
||||
$config['smtp_host'] = 'ssl://${config.mailserver.fqdn}:465';
|
||||
$config['smtp_conn_options'] = array(
|
||||
'ssl' => array(
|
||||
'verify_peer' => false,
|
||||
'verify_peer_name' => false,
|
||||
),
|
||||
);
|
||||
$config['smtp_user'] = '%u';
|
||||
$config['smtp_pass'] = '%p';
|
||||
'';
|
||||
};
|
||||
}
|
||||
26
services/synapse/database.nix
Normal file
26
services/synapse/database.nix
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
pgsqlEnable = config.machine.postgresql.enable;
|
||||
inherit (config.machine.synapse) enable;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
services.postgresql = with lib; mkIf pgsqlEnable {
|
||||
ensureUsers = [
|
||||
{
|
||||
name = "matrix-synapse";
|
||||
ensureDBOwnership = true;
|
||||
}
|
||||
];
|
||||
ensureDatabases = [ "matrix-synapse" ];
|
||||
};
|
||||
services.matrix-synapse.settings.database = {
|
||||
name = if pgsqlEnable then "psycopg2" else "sqlite3";
|
||||
args = with lib; mkIf pgsqlEnable {
|
||||
host = "/run/postgresql";
|
||||
};
|
||||
};
|
||||
}
|
||||
13
services/synapse/default.nix
Normal file
13
services/synapse/default.nix
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
imports = [
|
||||
./database.nix
|
||||
./element.nix
|
||||
./mail.nix
|
||||
./nginx.nix
|
||||
./options.nix
|
||||
./redis.nix
|
||||
./secrets.nix
|
||||
./synapse.nix
|
||||
./turn.nix
|
||||
];
|
||||
}
|
||||
64
services/synapse/element.nix
Normal file
64
services/synapse/element.nix
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.synapse.element) domain enable;
|
||||
elementConfig = {
|
||||
default_server_config = {
|
||||
"m.homeserver" = with config.services.matrix-synapse.settings; {
|
||||
base_url = public_baseurl;
|
||||
inherit server_name;
|
||||
};
|
||||
"m.identity_server" = {
|
||||
base_url = "";
|
||||
};
|
||||
};
|
||||
disable_custom_urls = false;
|
||||
disable_guests = true;
|
||||
disable_login_language_selector = false;
|
||||
disable_3pid_login = false;
|
||||
brand = "Element";
|
||||
default_country_code = "US";
|
||||
show_labs_settings = true;
|
||||
features = {
|
||||
feature_video_rooms = true;
|
||||
feature_group_calls = true;
|
||||
feature_element_call_video_rooms = true;
|
||||
feature_new_device_manager = true;
|
||||
feature_wysiwyg_composer = true;
|
||||
};
|
||||
default_federate = true;
|
||||
room_directory = with config.services.matrix-synapse.settings; {
|
||||
servers = [
|
||||
server_name
|
||||
"matrix.org"
|
||||
];
|
||||
};
|
||||
setting_defaults = {
|
||||
breadcrumbs = true;
|
||||
"MessageComposerInput.autoReplaceEmoji" = true;
|
||||
sendTypingNotifications = true;
|
||||
showTypingNotifications = true;
|
||||
showReadReceipts = true;
|
||||
showJoinLeaves = false;
|
||||
urlPreviewsEnabled = true;
|
||||
};
|
||||
sso_redirect_options = {
|
||||
immediate = false;
|
||||
on_welcome_page = true;
|
||||
};
|
||||
};
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
services.nginx.virtualHosts.${domain} = {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
|
||||
root = pkgs.element-web.override (_old: {
|
||||
conf = elementConfig;
|
||||
});
|
||||
};
|
||||
}
|
||||
39
services/synapse/mail.nix
Normal file
39
services/synapse/mail.nix
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
sec,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.synapse)
|
||||
enable
|
||||
domain
|
||||
;
|
||||
address = "noreply@${domain}";
|
||||
in
|
||||
with lib; mkIf (enable && config.machine.mail.enable) {
|
||||
services.matrix-synapse = {
|
||||
settings = {
|
||||
admin_contact = address;
|
||||
registrations_require_3pid = [ "email" ];
|
||||
email = {
|
||||
smtp_host = config.machine.mail.fqdn;
|
||||
smtp_port = 465;
|
||||
smtp_user = address;
|
||||
require_transport_security = true;
|
||||
enable_tls = true;
|
||||
notif_from = "RuJect Matrix <${address}>";
|
||||
app_name = "RuJect Matrix";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
mailserver = {
|
||||
domains = [ domain ];
|
||||
accounts.${address} = {
|
||||
hashedPasswordFile = sec."mail/serviceHashedPassword".path;
|
||||
aliases = [ ];
|
||||
sendOnly = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
98
services/synapse/nginx.nix
Normal file
98
services/synapse/nginx.nix
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
{ config, ... }:
|
||||
let
|
||||
inherit (config.machine.synapse) domain port;
|
||||
maxUploadSize = config.services.matrix-synapse.settings.max_upload_size;
|
||||
in
|
||||
{
|
||||
systemd.services.nginx.serviceConfig.SupplementaryGroups = [ "matrix-synapse" ];
|
||||
|
||||
services.nginx = {
|
||||
appendHttpConfig = ''
|
||||
limit_req_zone $binary_remote_addr zone=matrix_login:10m rate=1r/s;
|
||||
limit_req_zone $binary_remote_addr zone=matrix_register:10m rate=1r/m;
|
||||
limit_req_zone $binary_remote_addr zone=matrix_api:10m rate=20r/s;
|
||||
limit_req_zone $binary_remote_addr zone=matrix_media:10m rate=10r/s;
|
||||
limit_req_zone $binary_remote_addr zone=matrix_federation:10m rate=50r/s;
|
||||
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
|
||||
'';
|
||||
upstreams."matrix-synapse".servers = {
|
||||
"unix:/run/matrix-synapse/matrix-synapse.sock" = { };
|
||||
};
|
||||
virtualHosts.${domain} = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
|
||||
locations = {
|
||||
# Synapse client API
|
||||
"/_matrix/client" = {
|
||||
proxyPass = "http://127.0.0.1:${toString port}";
|
||||
extraConfig = ''
|
||||
limit_req zone=matrix_api burst=50 nodelay;
|
||||
client_max_body_size ${maxUploadSize};
|
||||
'';
|
||||
};
|
||||
|
||||
# Login endpoint with stricter rate limiting
|
||||
"~ ^/_matrix/client/(r0|v3)/login$" = {
|
||||
proxyPass = "http://127.0.0.1:${toString port}";
|
||||
extraConfig = ''
|
||||
limit_req zone=matrix_login burst=3 nodelay;
|
||||
'';
|
||||
};
|
||||
|
||||
# Registration with very strict rate limiting
|
||||
"~ ^/_matrix/client/(r0|v3)/register" = {
|
||||
proxyPass = "http://127.0.0.1:${toString port}";
|
||||
extraConfig = ''
|
||||
limit_req zone=matrix_register burst=2 nodelay;
|
||||
'';
|
||||
};
|
||||
|
||||
# Sync endpoint with longer timeout
|
||||
"~ ^/_matrix/client/(r0|v3|unstable)/sync" = {
|
||||
proxyPass = "http://127.0.0.1:${toString port}";
|
||||
extraConfig = ''
|
||||
limit_req zone=matrix_api burst=50 nodelay;
|
||||
proxy_read_timeout 600s;
|
||||
'';
|
||||
};
|
||||
|
||||
# Media
|
||||
"/_matrix/media" = {
|
||||
proxyPass = "http://127.0.0.1:${toString port}";
|
||||
extraConfig = ''
|
||||
limit_req zone=matrix_media burst=100 nodelay;
|
||||
client_max_body_size ${maxUploadSize};
|
||||
'';
|
||||
};
|
||||
|
||||
# Federation
|
||||
"/_matrix/federation" = {
|
||||
proxyPass = "http://127.0.0.1:${toString port}";
|
||||
extraConfig = ''
|
||||
limit_req zone=matrix_federation burst=100 nodelay;
|
||||
client_max_body_size 50M;
|
||||
'';
|
||||
};
|
||||
|
||||
"/_matrix/key" = {
|
||||
proxyPass = "http://127.0.0.1:${toString port}";
|
||||
extraConfig = ''
|
||||
limit_req zone=matrix_federation burst=50 nodelay;
|
||||
'';
|
||||
};
|
||||
|
||||
# Health check
|
||||
"= /health" = {
|
||||
proxyPass = "http://127.0.0.1:${toString port}";
|
||||
extraConfig = ''
|
||||
access_log off;
|
||||
'';
|
||||
};
|
||||
|
||||
# Block admin API from public
|
||||
"/_synapse/admin".return = "404";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
37
services/synapse/options.nix
Normal file
37
services/synapse/options.nix
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
with lib;
|
||||
{
|
||||
options.machine.synapse = {
|
||||
enable = mkEnableOption "Synapse";
|
||||
element = {
|
||||
enable = mkEnableOption "Elemenet web";
|
||||
domain = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = "chat.${config.machine.synapse.domain}";
|
||||
description = "Domain name.";
|
||||
};
|
||||
};
|
||||
domain = mkOption {
|
||||
type = types.str;
|
||||
default = "example.com";
|
||||
description = "Domain name.";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 8008;
|
||||
description = "Listen port.";
|
||||
};
|
||||
metrics = {
|
||||
enable = mkEnableOption "Synapse metrics";
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 9000;
|
||||
description = "Listen port.";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
19
services/synapse/redis.nix
Normal file
19
services/synapse/redis.nix
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
redisEnable = config.machine.redis.enable;
|
||||
inherit (config.machine.synapse) enable;
|
||||
in
|
||||
with lib; mkIf (redisEnable && enable) {
|
||||
services.redis.servers.matrix-synapse = {
|
||||
enable = true;
|
||||
};
|
||||
systemd.services.matrix-synapse.serviceConfig.SupplementaryGroups = [ "redis-matrix-synapse" ];
|
||||
services.matrix-synapse.settings.redis = {
|
||||
enabled = true;
|
||||
path = config.services.redis.servers.matrix-synapse.unixSocket;
|
||||
};
|
||||
}
|
||||
46
services/synapse/secrets.nix
Normal file
46
services/synapse/secrets.nix
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.synapse)
|
||||
enable
|
||||
;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
sops.secrets = {
|
||||
"matrix/registrationSharedSecret" = {
|
||||
sopsFile = ./../../secrets/common.yaml;
|
||||
owner = config.users.users.matrix-synapse.name;
|
||||
inherit (config.users.users.matrix-synapse) group;
|
||||
restartUnits = [ "matrix-synapse.service" ];
|
||||
};
|
||||
"matrix/signingKey" = {
|
||||
sopsFile = ./../../secrets/common.yaml;
|
||||
owner = config.users.users.matrix-synapse.name;
|
||||
inherit (config.users.users.matrix-synapse) group;
|
||||
restartUnits = [ "matrix-synapse.service" ];
|
||||
};
|
||||
"turn/authSecret" = {
|
||||
sopsFile = ./../../secrets/common.yaml;
|
||||
owner = config.users.users.turnserver.name;
|
||||
inherit (config.users.users.turnserver) group;
|
||||
restartUnits = [ "coturn.service" ];
|
||||
};
|
||||
};
|
||||
sops.templates.matrix-synapse-config = {
|
||||
owner = config.users.users.matrix-synapse.name;
|
||||
inherit (config.users.users.matrix-synapse) group;
|
||||
restartUnits = [ "matrix-synapse.service" ];
|
||||
content = ''
|
||||
registration_shared_secret: ${config.sops.placeholder."matrix/registrationSharedSecret"}
|
||||
turn_shared_secret: ${config.sops.placeholder."turn/authSecret"}
|
||||
mail:
|
||||
smtp_pass: ${config.sops.placeholder."mail/servicePassword"}
|
||||
'';
|
||||
};
|
||||
services.matrix-synapse.extraConfigFiles = with lib; mkIf config.machine.synapse.enable [
|
||||
config.sops.templates.matrix-synapse-config.path
|
||||
];
|
||||
}
|
||||
94
services/synapse/synapse.nix
Normal file
94
services/synapse/synapse.nix
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
sec,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.synapse)
|
||||
domain
|
||||
enable
|
||||
port
|
||||
metrics
|
||||
;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
services.matrix-synapse = {
|
||||
inherit enable;
|
||||
enableRegistrationScript = true;
|
||||
settings = {
|
||||
server_name = domain;
|
||||
public_baseurl = "https://${domain}";
|
||||
signing_key_path = sec."matrix/signingKey".path;
|
||||
listeners = [
|
||||
{
|
||||
inherit port;
|
||||
bind_addresses = [ "127.0.0.1" ];
|
||||
type = "http";
|
||||
tls = false;
|
||||
x_forwarded = true;
|
||||
resources = [
|
||||
{
|
||||
compress = true;
|
||||
names = [
|
||||
"client"
|
||||
"federation"
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
]
|
||||
++ (optionals metrics.enable [
|
||||
{
|
||||
inherit (metrics) port;
|
||||
bind_addresses = [ "127.0.0.1" ];
|
||||
type = "metrics";
|
||||
tls = false;
|
||||
resources = [
|
||||
{
|
||||
names = [ "metrics" ];
|
||||
}
|
||||
];
|
||||
}
|
||||
]);
|
||||
|
||||
enable_metrics = metrics.enable;
|
||||
|
||||
enable_registration = true;
|
||||
enable_registration_without_verification = false;
|
||||
|
||||
allow_public_rooms_over_federation = true;
|
||||
federation_domain_whitelist = [ ];
|
||||
|
||||
allow_public_rooms_without_auth = true;
|
||||
|
||||
url_preview_enabled = true;
|
||||
url_preview_ip_range_blacklist = [
|
||||
"127.0.0.0/8"
|
||||
"10.0.0.0/8"
|
||||
"172.16.0.0/12"
|
||||
"192.168.0.0/16"
|
||||
"100.64.0.0/10"
|
||||
"169.254.0.0/16"
|
||||
"::1/128"
|
||||
"fe80::/10"
|
||||
"fc00::/7"
|
||||
];
|
||||
|
||||
dynamic_thumbnails = true;
|
||||
max_upload_size = "50M";
|
||||
media_retention = {
|
||||
local_media_lifetime = "90d";
|
||||
remote_media_lifetime = "14d";
|
||||
};
|
||||
|
||||
retention = {
|
||||
enabled = true;
|
||||
default_policy.max_lifetime = "180d";
|
||||
purge_jobs = [
|
||||
{ interval = "1d"; }
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
18
services/synapse/turn.nix
Normal file
18
services/synapse/turn.nix
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.coturn) enable;
|
||||
in
|
||||
with lib; mkIf (enable && config.machine.coturn.enable) {
|
||||
services.matrix-synapse.settings = with config.services.coturn; {
|
||||
turn_uris = [
|
||||
"turn:${realm}:3478?transport=udp"
|
||||
"turn:${realm}:3478?transport=tcp"
|
||||
];
|
||||
turn_user_lifetime = "1h";
|
||||
turn_allow_guests = false;
|
||||
};
|
||||
}
|
||||
6
services/uptime-kuma/default.nix
Normal file
6
services/uptime-kuma/default.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
imports = [
|
||||
./options.nix
|
||||
./service.nix
|
||||
];
|
||||
}
|
||||
17
services/uptime-kuma/options.nix
Normal file
17
services/uptime-kuma/options.nix
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{ lib, ... }:
|
||||
with lib;
|
||||
{
|
||||
options.machine.uptime-kuma = {
|
||||
enable = mkEnableOption "Uptime Kuma";
|
||||
domain = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Domain name. If not set, will be disabled, and use the localhost.";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 4000;
|
||||
description = "Listen port.";
|
||||
};
|
||||
};
|
||||
}
|
||||
35
services/uptime-kuma/service.nix
Normal file
35
services/uptime-kuma/service.nix
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.uptime-kuma)
|
||||
domain
|
||||
enable
|
||||
port
|
||||
;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
services.uptime-kuma = {
|
||||
inherit enable;
|
||||
settings = {
|
||||
PORT = toString port;
|
||||
};
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts = with lib; mkIf (domain != null) {
|
||||
"${domain}" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:${toString port}";
|
||||
proxyWebsockets = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [ port ];
|
||||
};
|
||||
}
|
||||
6
services/vaultwarden/default.nix
Normal file
6
services/vaultwarden/default.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
imports = [
|
||||
./options.nix
|
||||
./service.nix
|
||||
];
|
||||
}
|
||||
17
services/vaultwarden/options.nix
Normal file
17
services/vaultwarden/options.nix
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{ lib, ... }:
|
||||
with lib;
|
||||
{
|
||||
options.machine.vaultwarden = {
|
||||
enable = mkEnableOption "Vaultwarden";
|
||||
domain = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Domain name. If not set, will be disabled, and use the localhost.";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 4534;
|
||||
description = "Listen port.";
|
||||
};
|
||||
};
|
||||
}
|
||||
55
services/vaultwarden/service.nix
Normal file
55
services/vaultwarden/service.nix
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.vaultwarden)
|
||||
enable
|
||||
domain
|
||||
port
|
||||
;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [ port ];
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts = with lib; mkIf (domain != null) {
|
||||
"${domain}" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:${toString port}";
|
||||
proxyWebsockets = true;
|
||||
extraConfig = ''
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = ''
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header Referrer-Policy same-origin;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
services.vaultwarden = {
|
||||
inherit enable;
|
||||
backupDir = "/var/local/vaultwarden/backup";
|
||||
environmentFile = "/var/lib/vaultwarden/vaultwarden.env";
|
||||
config = {
|
||||
DOMAIN = "https://${domain}";
|
||||
SIGNUPS_ALLOWED = true;
|
||||
|
||||
ROCKET_ADDRESS = "127.0.0.1";
|
||||
ROCKET_PORT = port;
|
||||
ROCKET_LOG = "critical";
|
||||
};
|
||||
};
|
||||
}
|
||||
8
services/xray-3x-ui/default.nix
Normal file
8
services/xray-3x-ui/default.nix
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
imports = [
|
||||
./options.nix
|
||||
./firewall.nix
|
||||
./nginx.nix
|
||||
./service.nix
|
||||
];
|
||||
}
|
||||
24
services/xray-3x-ui/firewall.nix
Normal file
24
services/xray-3x-ui/firewall.nix
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.xray-3x-ui)
|
||||
enable
|
||||
port
|
||||
;
|
||||
in
|
||||
with lib; mkIf enable {
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
# Web panel
|
||||
port
|
||||
|
||||
# SSL & HTTP
|
||||
80
|
||||
443
|
||||
|
||||
# Inbounds
|
||||
1082
|
||||
];
|
||||
}
|
||||
44
services/xray-3x-ui/nginx.nix
Normal file
44
services/xray-3x-ui/nginx.nix
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}: let
|
||||
inherit
|
||||
(config.machine.xray-3x-ui)
|
||||
enable
|
||||
port
|
||||
domain
|
||||
subscriptions
|
||||
;
|
||||
in {
|
||||
services.nginx.virtualHosts = with lib; mkIf enable {
|
||||
${domain} = with lib; mkIf (domain != null) {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:${toString port}";
|
||||
proxyWebsockets = true;
|
||||
extraConfig = ''
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_redirect off;
|
||||
'';
|
||||
};
|
||||
};
|
||||
${subscriptions.domain} = with lib; mkIf (subscriptions.domain != null) {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:2096";
|
||||
proxyWebsockets = true;
|
||||
extraConfig = ''
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_redirect off;
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
33
services/xray-3x-ui/options.nix
Normal file
33
services/xray-3x-ui/options.nix
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
{ lib, config, ... }:
|
||||
with lib;
|
||||
{
|
||||
options.machine.xray-3x-ui = {
|
||||
enable = mkEnableOption "3x-ui Xray panel";
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 2053;
|
||||
description = "Port for the web interface.";
|
||||
};
|
||||
|
||||
domain = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "domain for the web interface.";
|
||||
};
|
||||
|
||||
subscriptions = {
|
||||
domain = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "domain for the web interface.";
|
||||
};
|
||||
};
|
||||
|
||||
dataDir = mkOption {
|
||||
type = types.path;
|
||||
default = "/var/lib/3x-ui";
|
||||
description = "Directory to store 3x-ui data.";
|
||||
};
|
||||
};
|
||||
}
|
||||
96
services/xray-3x-ui/service.nix
Normal file
96
services/xray-3x-ui/service.nix
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
# See https://github.com/sunmeplz/xray-3x-ui
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.machine.xray-3x-ui;
|
||||
|
||||
# Minimum Go version required for building
|
||||
minGoVersion = "1.26.0";
|
||||
|
||||
xray-3x-ui =
|
||||
assert assertMsg
|
||||
(versionAtLeast pkgs.go.version minGoVersion)
|
||||
"3x-ui requires Go >= ${minGoVersion}, but ${pkgs.go.version} is available";
|
||||
|
||||
pkgs.buildGoModule rec {
|
||||
pname = "3x-ui";
|
||||
version = "2.8.11";
|
||||
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "MHSanaei";
|
||||
repo = "3x-ui";
|
||||
rev = "v${version}";
|
||||
hash = "sha256-2I6t3caf2t7nKSFwxI/dVIobpBzuptrgauuXfFw8ltg=";
|
||||
};
|
||||
|
||||
vendorHash = "sha256-M8YQTMfF/xZut4hxUcAfF2xGK625vwJNp4JS/zoXUCQ=";
|
||||
|
||||
ldflags = [ "-s" "-w" ];
|
||||
|
||||
meta = with lib; {
|
||||
description = "Xray panel supporting multi-protocol multi-user";
|
||||
homepage = "https://github.com/MHSanaei/3x-ui";
|
||||
license = licenses.gpl3Only;
|
||||
platforms = platforms.linux;
|
||||
maintainers = [ ];
|
||||
};
|
||||
};
|
||||
|
||||
in {
|
||||
# Service implementation
|
||||
config = mkIf cfg.enable {
|
||||
# User and group configuration
|
||||
users.users.xray-3x-ui = {
|
||||
isSystemUser = true;
|
||||
group = "xray-3x-ui";
|
||||
description = "3x-ui service user";
|
||||
};
|
||||
|
||||
users.groups.xray-3x-ui = { };
|
||||
|
||||
# Directory structure
|
||||
systemd.tmpfiles.rules = [
|
||||
"d ${cfg.dataDir} 0755 xray-3x-ui xray-3x-ui -"
|
||||
"d ${cfg.dataDir}/bin 0755 xray-3x-ui xray-3x-ui -"
|
||||
"d ${cfg.dataDir}/logs 0755 xray-3x-ui xray-3x-ui -"
|
||||
];
|
||||
|
||||
# Systemd service
|
||||
systemd.services.xray-3x-ui = {
|
||||
description = "3x-ui Xray Panel";
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
environment = {
|
||||
XUI_DB_FOLDER = cfg.dataDir;
|
||||
XUI_BIN_FOLDER = "${cfg.dataDir}/bin";
|
||||
XUI_LOG_FOLDER = "${cfg.dataDir}/logs";
|
||||
};
|
||||
|
||||
preStart = ''
|
||||
# Symlink xray-core binary to expected location
|
||||
ln -sf ${pkgs.xray}/bin/xray ${cfg.dataDir}/bin/xray-linux-amd64
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
ExecStart = "${xray-3x-ui}/bin/3x-ui";
|
||||
WorkingDirectory = cfg.dataDir;
|
||||
Restart = "on-failure";
|
||||
RestartSec = "10s";
|
||||
User = "xray-3x-ui";
|
||||
Group = "xray-3x-ui";
|
||||
StateDirectory = "3x-ui 3x-ui/bin 3x-ui/logs";
|
||||
StateDirectoryMode = "0755";
|
||||
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" "CAP_NET_ADMIN" ];
|
||||
CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" "CAP_NET_ADMIN" ];
|
||||
};
|
||||
};
|
||||
|
||||
# Add to system packages for CLI access
|
||||
environment.systemPackages = [ xray-3x-ui ];
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue