Compare commits
5 Commits
26068a9dc5
...
998ebc1164
| Author | SHA1 | Date | |
|---|---|---|---|
| 998ebc1164 | |||
| 9acb378e5f | |||
| dca434c0ba | |||
| c22ff38874 | |||
| 0591868be3 |
9
home/apps/affine.nix
Normal file
9
home/apps/affine.nix
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}: {
|
||||
home.packages = lib.optionals pkgs.stdenv.isLinux [
|
||||
pkgs.affine
|
||||
];
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
}:
|
||||
lib.optionalAttrs device.hasGui {
|
||||
imports = [
|
||||
# ./audacity.nix
|
||||
./audacity.nix
|
||||
# ./bottles.nix
|
||||
# ./cursor.nix
|
||||
# ./gimp.nix
|
||||
@@ -22,6 +22,7 @@ lib.optionalAttrs device.hasGui {
|
||||
# ./vial.nix
|
||||
# ./vscode.nix
|
||||
|
||||
./affine.nix
|
||||
./blueman.nix
|
||||
./chromium.nix
|
||||
./discord.nix
|
||||
|
||||
@@ -8,20 +8,20 @@
|
||||
loop-playlist = "inf";
|
||||
};
|
||||
profiles = {
|
||||
hdr = {
|
||||
vo = "gpu-next";
|
||||
gpu-api = "vulkan";
|
||||
hdr-compute-peak = "yes";
|
||||
hdr-peak-detect = "yes";
|
||||
target-peak = 400;
|
||||
target-prim = "bt.2020";
|
||||
target-trc = "pq";
|
||||
inverse-tone-mapping = "yes";
|
||||
tone-mapping = "spline";
|
||||
tone-mapping-mode = "auto";
|
||||
target-colorspace-hint = "auto";
|
||||
gamut-mapping = "desaturate";
|
||||
};
|
||||
# hdr = {
|
||||
# vo = "gpu-next";
|
||||
# gpu-api = "vulkan";
|
||||
# hdr-compute-peak = "yes";
|
||||
# hdr-peak-detect = "yes";
|
||||
# target-peak = 400;
|
||||
# target-prim = "bt.2020";
|
||||
# target-trc = "pq";
|
||||
# inverse-tone-mapping = "yes";
|
||||
# tone-mapping = "spline";
|
||||
# tone-mapping-mode = "auto";
|
||||
# target-colorspace-hint = "auto";
|
||||
# gamut-mapping = "desaturate";
|
||||
# };
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
};
|
||||
};
|
||||
home.packages = with pkgs; [
|
||||
pulseaudio
|
||||
# pulseaudio
|
||||
playerctl
|
||||
];
|
||||
}
|
||||
|
||||
@@ -80,5 +80,6 @@
|
||||
./zoxide.nix
|
||||
./attic.nix
|
||||
./cfcli.nix
|
||||
./jujutsu.nix
|
||||
];
|
||||
}
|
||||
|
||||
1
home/programs/jujutsu.nix
Normal file
1
home/programs/jujutsu.nix
Normal file
@@ -0,0 +1 @@
|
||||
{pkgs, ...}: {home.packages = [pkgs.jujutsu];}
|
||||
@@ -6,5 +6,20 @@
|
||||
lib.optionalAttrs (device.is "ryu" || device.is "kuro") {
|
||||
programs.opencode = {
|
||||
enable = true;
|
||||
settings.provider = {
|
||||
ollama = {
|
||||
models = {
|
||||
"glm-4.7-flash" = {
|
||||
# "_launch" = true;
|
||||
name = "glm-4.7-flash";
|
||||
};
|
||||
};
|
||||
name = "Ollama (local)";
|
||||
npm = "@ai-sdk/openai-compatible";
|
||||
options = {
|
||||
baseURL = "https://ollama.darksailor.dev/v1";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
166
modules/nixos/affine.nix
Normal file
166
modules/nixos/affine.nix
Normal file
@@ -0,0 +1,166 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
with lib; let
|
||||
cfg = config.services.affine;
|
||||
dbName = "affine";
|
||||
dbUser = "affine";
|
||||
in {
|
||||
options.services.affine = {
|
||||
enable = mkEnableOption "AFFiNE self-hosted workspace";
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 3010;
|
||||
description = "Port for the AFFiNE server to listen on";
|
||||
};
|
||||
|
||||
domain = mkOption {
|
||||
type = types.str;
|
||||
description = "Public domain for AFFiNE (e.g. notes.darksailor.dev)";
|
||||
};
|
||||
|
||||
imageTag = mkOption {
|
||||
type = types.str;
|
||||
default = "stable";
|
||||
description = "Docker image tag for AFFiNE (stable, beta, canary)";
|
||||
};
|
||||
|
||||
dataDir = mkOption {
|
||||
type = types.str;
|
||||
default = "/var/lib/affine";
|
||||
description = "Base data directory for AFFiNE storage";
|
||||
};
|
||||
|
||||
environmentFiles = mkOption {
|
||||
type = types.listOf types.path;
|
||||
default = [];
|
||||
description = "Environment files containing secrets (DB password, etc.)";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
# Create data directories
|
||||
systemd.tmpfiles.rules = [
|
||||
"d ${cfg.dataDir} 0755 root root -"
|
||||
"d ${cfg.dataDir}/storage 0755 root root -"
|
||||
"d ${cfg.dataDir}/config 0755 root root -"
|
||||
"d ${cfg.dataDir}/postgres 0700 root root -"
|
||||
"d ${cfg.dataDir}/redis 0755 root root -"
|
||||
];
|
||||
|
||||
virtualisation.oci-containers = {
|
||||
backend = "docker";
|
||||
containers = {
|
||||
affine-postgres = {
|
||||
image = "pgvector/pgvector:pg16";
|
||||
volumes = [
|
||||
"${cfg.dataDir}/postgres:/var/lib/postgresql/data"
|
||||
];
|
||||
environment = {
|
||||
POSTGRES_USER = dbUser;
|
||||
POSTGRES_DB = dbName;
|
||||
POSTGRES_INITDB_ARGS = "--data-checksums";
|
||||
POSTGRES_HOST_AUTH_METHOD = "trust";
|
||||
};
|
||||
environmentFiles = cfg.environmentFiles;
|
||||
extraOptions = [
|
||||
"--network=affine-net"
|
||||
"--health-cmd=pg_isready -U ${dbUser} -d ${dbName}"
|
||||
"--health-interval=10s"
|
||||
"--health-timeout=5s"
|
||||
"--health-retries=5"
|
||||
];
|
||||
};
|
||||
|
||||
affine-redis = {
|
||||
image = "redis:7";
|
||||
volumes = [
|
||||
"${cfg.dataDir}/redis:/data"
|
||||
];
|
||||
extraOptions = [
|
||||
"--network=affine-net"
|
||||
"--health-cmd=redis-cli --raw incr ping"
|
||||
"--health-interval=10s"
|
||||
"--health-timeout=5s"
|
||||
"--health-retries=5"
|
||||
];
|
||||
};
|
||||
|
||||
affine = {
|
||||
image = "ghcr.io/toeverything/affine:${cfg.imageTag}";
|
||||
ports = ["127.0.0.1:${toString cfg.port}:3010"];
|
||||
dependsOn = [
|
||||
"affine-postgres"
|
||||
"affine-redis"
|
||||
"affine-migration"
|
||||
];
|
||||
volumes = [
|
||||
"${cfg.dataDir}/storage:/root/.affine/storage"
|
||||
"${cfg.dataDir}/config:/root/.affine/config"
|
||||
];
|
||||
environment = {
|
||||
AFFINE_SERVER_PORT = "3010";
|
||||
AFFINE_SERVER_HOST = cfg.domain;
|
||||
AFFINE_SERVER_HTTPS = "true";
|
||||
AFFINE_SERVER_EXTERNAL_URL = "https://${cfg.domain}";
|
||||
REDIS_SERVER_HOST = "affine-redis";
|
||||
DATABASE_URL = "postgresql://${dbUser}:$${AFFINE_DB_PASSWORD:-affine}@affine-postgres:5432/${dbName}";
|
||||
AFFINE_INDEXER_ENABLED = "false";
|
||||
};
|
||||
environmentFiles = cfg.environmentFiles;
|
||||
extraOptions = [
|
||||
"--network=affine-net"
|
||||
];
|
||||
};
|
||||
|
||||
affine-migration = {
|
||||
image = "ghcr.io/toeverything/affine:${cfg.imageTag}";
|
||||
dependsOn = [
|
||||
"affine-postgres"
|
||||
"affine-redis"
|
||||
];
|
||||
volumes = [
|
||||
"${cfg.dataDir}/storage:/root/.affine/storage"
|
||||
"${cfg.dataDir}/config:/root/.affine/config"
|
||||
];
|
||||
cmd = ["sh" "-c" "node ./scripts/self-host-predeploy.js"];
|
||||
environment = {
|
||||
REDIS_SERVER_HOST = "affine-redis";
|
||||
DATABASE_URL = "postgresql://${dbUser}:$${AFFINE_DB_PASSWORD:-affine}@affine-postgres:5432/${dbName}";
|
||||
AFFINE_INDEXER_ENABLED = "false";
|
||||
};
|
||||
environmentFiles = cfg.environmentFiles;
|
||||
extraOptions = [
|
||||
"--network=affine-net"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Create the Docker network
|
||||
systemd.services.affine-network = {
|
||||
description = "Create AFFiNE Docker network";
|
||||
after = ["docker.service"];
|
||||
wantedBy = ["multi-user.target"];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStart = "${config.virtualisation.docker.package}/bin/docker network create affine-net";
|
||||
ExecStop = "${config.virtualisation.docker.package}/bin/docker network remove affine-net";
|
||||
};
|
||||
};
|
||||
|
||||
# Ensure containers start after the network is created
|
||||
systemd.services.docker-affine.after = ["affine-network.service"];
|
||||
systemd.services.docker-affine.requires = ["affine-network.service"];
|
||||
systemd.services.docker-affine-postgres.after = ["affine-network.service"];
|
||||
systemd.services.docker-affine-postgres.requires = ["affine-network.service"];
|
||||
systemd.services.docker-affine-redis.after = ["affine-network.service"];
|
||||
systemd.services.docker-affine-redis.requires = ["affine-network.service"];
|
||||
systemd.services.docker-affine-migration.after = ["affine-network.service"];
|
||||
systemd.services.docker-affine-migration.requires = ["affine-network.service"];
|
||||
};
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
pkgs,
|
||||
lib,
|
||||
device,
|
||||
config,
|
||||
...
|
||||
}: {
|
||||
imports = [
|
||||
@@ -47,6 +48,7 @@
|
||||
auto-optimise-store = true;
|
||||
extra-experimental-features = "nix-command flakes auto-allocate-uids";
|
||||
trusted-users = [device.user];
|
||||
extra-sandbox-paths = [config.programs.ccache.cacheDir];
|
||||
};
|
||||
extraOptions = ''
|
||||
build-users-group = nixbld
|
||||
|
||||
6
nixos/ryu/programs/ccache.nix
Normal file
6
nixos/ryu/programs/ccache.nix
Normal file
@@ -0,0 +1,6 @@
|
||||
{...}: {
|
||||
programs.ccache = {
|
||||
enable = true;
|
||||
packageNames = ["ollama" "orca-slicer" "opencv" "onnxruntime" "obs-studio" "llama-cpp"];
|
||||
};
|
||||
}
|
||||
89
nixos/tako/services/affine.nix
Normal file
89
nixos/tako/services/affine.nix
Normal file
@@ -0,0 +1,89 @@
|
||||
{config, ...}: let
|
||||
domain = "notes.darksailor.dev";
|
||||
in {
|
||||
imports = [
|
||||
../../../modules/nixos/affine.nix
|
||||
];
|
||||
|
||||
# SOPS secrets
|
||||
sops = {
|
||||
secrets = {
|
||||
"affine/db_password" = {};
|
||||
"authelia/oidc/affine/client_id" = {
|
||||
owner = config.systemd.services.authelia-darksailor.serviceConfig.User;
|
||||
mode = "0440";
|
||||
restartUnits = ["authelia-darksailor.service"];
|
||||
};
|
||||
"authelia/oidc/affine/client_secret" = {
|
||||
owner = config.systemd.services.authelia-darksailor.serviceConfig.User;
|
||||
mode = "0440";
|
||||
restartUnits = ["authelia-darksailor.service"];
|
||||
};
|
||||
};
|
||||
templates."affine.env".content = ''
|
||||
AFFINE_DB_PASSWORD=${config.sops.placeholder."affine/db_password"}
|
||||
POSTGRES_PASSWORD=${config.sops.placeholder."affine/db_password"}
|
||||
AFFINE_SERVER_EXTERNAL_URL=https://${domain}
|
||||
'';
|
||||
};
|
||||
|
||||
# Enable AFFiNE service
|
||||
services.affine = {
|
||||
enable = true;
|
||||
inherit domain;
|
||||
environmentFiles = [
|
||||
config.sops.templates."affine.env".path
|
||||
];
|
||||
};
|
||||
|
||||
# Caddy reverse proxy with SSO forward auth
|
||||
services.caddy.virtualHosts."${domain}".extraConfig = ''
|
||||
reverse_proxy localhost:${toString config.services.affine.port}
|
||||
'';
|
||||
|
||||
# Authelia access control rules
|
||||
services.authelia.instances.darksailor.settings = {
|
||||
access_control.rules = [
|
||||
{
|
||||
inherit domain;
|
||||
policy = "bypass";
|
||||
resources = [
|
||||
"^/api/(sync|awareness)([/?].*)?$"
|
||||
"^/socket\\.io([/?].*)?$"
|
||||
];
|
||||
}
|
||||
{
|
||||
inherit domain;
|
||||
policy = "one_factor";
|
||||
}
|
||||
];
|
||||
# OIDC client for AFFiNE
|
||||
identity_providers.oidc.clients = [
|
||||
{
|
||||
client_name = "AFFiNE: Darksailor";
|
||||
client_id = ''{{ secret "${config.sops.secrets."authelia/oidc/affine/client_id".path}" }}'';
|
||||
client_secret = ''{{ secret "${config.sops.secrets."authelia/oidc/affine/client_secret".path}" }}'';
|
||||
public = false;
|
||||
authorization_policy = "one_factor";
|
||||
require_pkce = false;
|
||||
redirect_uris = [
|
||||
"https://${domain}/oauth/callback"
|
||||
];
|
||||
scopes = [
|
||||
"openid"
|
||||
"email"
|
||||
"profile"
|
||||
];
|
||||
response_types = ["code"];
|
||||
grant_types = ["authorization_code"];
|
||||
userinfo_signed_response_alg = "none";
|
||||
token_endpoint_auth_method = "client_secret_post";
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
# Ensure containers start after secrets are available
|
||||
systemd.services.docker-affine.after = ["sops-install-secrets.service"];
|
||||
systemd.services.docker-affine-migration.after = ["sops-install-secrets.service"];
|
||||
systemd.services.docker-affine-postgres.after = ["sops-install-secrets.service"];
|
||||
}
|
||||
@@ -1,31 +1,33 @@
|
||||
{...}: {
|
||||
imports = [
|
||||
./attic.nix
|
||||
./atuin.nix
|
||||
./authelia.nix
|
||||
./caddy.nix
|
||||
./excalidraw.nix
|
||||
./fail2ban.nix
|
||||
./flaresolverr.nix
|
||||
./games
|
||||
./gitea.nix
|
||||
./homepage.nix
|
||||
./immich.nix
|
||||
./kellnr.nix
|
||||
./lldap.nix
|
||||
./llms.nix
|
||||
./matrix
|
||||
./monitoring.nix
|
||||
./navidrome.nix
|
||||
./nextcloud.nix
|
||||
./openssh.nix
|
||||
./prowlarr.nix
|
||||
./resolved.nix
|
||||
./searxng.nix
|
||||
./shitpost.nix
|
||||
./tailscale.nix
|
||||
./gitea.nix
|
||||
|
||||
./affine.nix
|
||||
./attic.nix
|
||||
./excalidraw.nix
|
||||
./flaresolverr.nix
|
||||
# ./games
|
||||
# ./headscale.nix
|
||||
./immich.nix
|
||||
./kellnr.nix
|
||||
# ./llms.nix
|
||||
./matrix
|
||||
# ./monitoring.nix
|
||||
# ./paperless.nix
|
||||
./prowlarr.nix
|
||||
# ./searxng.nix
|
||||
# ./shitpost.nix
|
||||
];
|
||||
services = {
|
||||
nix-serve = {
|
||||
|
||||
@@ -34,12 +34,12 @@
|
||||
};
|
||||
};
|
||||
};
|
||||
# headplane = {
|
||||
# enable = true;
|
||||
# settings = {
|
||||
# server.port = 42562;
|
||||
# };
|
||||
# };
|
||||
headplane = {
|
||||
enable = true;
|
||||
settings = {
|
||||
server.port = 42562;
|
||||
};
|
||||
};
|
||||
caddy = {
|
||||
virtualHosts."headscale.darksailor.dev".extraConfig = ''
|
||||
reverse_proxy localhost:${toString config.services.headplane.settings.server.port}
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings.PasswordAuthentication = false;
|
||||
settings.PermitRootLogin = "prohibit-password";
|
||||
settings.PermitRootLogin = "no";
|
||||
};
|
||||
}
|
||||
|
||||
@@ -53,6 +53,9 @@ authelia:
|
||||
excalidraw:
|
||||
client_id: ENC[AES256_GCM,data:ANaCFTiPnR/bP51lSMfiTRX7ZGZ2pmX3Guamsyj7KRzD34G18E+UUgXi0YdbDfmFxcEj+nvoerf7wWhtvIzO1Q==,iv:CyNiLA0PH0p1Zwdf8B6/Ysb6GODClnXkPctbtZnoddw=,tag:X3kAkBKD407QFg/Se33Flg==,type:str]
|
||||
client_secret: ENC[AES256_GCM,data:VHIbKjHWXfQCUp3wh2dsMpMaDdCabmVlLMHcMnTCXPr5ZNIS1zpyGD6keapoOYywwvDFenICf73vpHun5aFhLw==,iv:HjRTwREC2jMsW1VrVYe4iywGc9apWZWLwh5aHOjvde0=,tag:Jl9kDI8C9VjSm6SiePk7Ow==,type:str]
|
||||
affine:
|
||||
client_id: ENC[AES256_GCM,data:riwC/9GuHamkV7U80+qCXP/F2wP0volVmRXtkVOQu7NfBb9OlSnamMvVh1pRNHj4c3AZWHpYIIHpkhuNKUuD+A==,iv:gi70CRZqRYfvHq7JhY+N3rX3Trb7eLqgmUlFqLk7p+4=,tag:M6MzyKRTJpZsznXzvxQINQ==,type:str]
|
||||
client_secret: ENC[AES256_GCM,data:/4+606H4s50+E25LOaVRi5Vt0rpzg9mAHSonYaLASPW/jEwAxwTNunWGvOg4ydjF+D9VBUbYcp1krZnlrhDp9sRR81vf+1G9aA69mWVPF7aMjBx5wA44tBszjLc/KKx804qbopG1m4y828aPrFvknx2d7Rox/YkvhLoq5jPjyVU=,iv:9s4OQNTtPBcN+7kvtH9u9kkPadFG7MG0LdlHAhMR5sw=,tag:BV0G1f/5qGAfrdRMshHzYw==,type:str]
|
||||
lldap:
|
||||
jwt: ENC[AES256_GCM,data:61dwC1ElOOGaf0CmalzXZnxImEyufKjUUWcNaEcOuv3TEODhQyHK7g==,iv:CVEJVuaCc2gDmSYWHS3fPL8FjbvblF6IladAzGoGb0o=,tag:OMm/OdKjliHjsGqJripLbg==,type:str]
|
||||
seed: ENC[AES256_GCM,data:jJPutPkhFVFxLbbQNZznHHiilP/cN2r+/vT4ArQVRQSqPMnkkwgc3LNk4sUTrT9V,iv:LD1IJ1CgtDfYf1gSyyaU+hir0InuDEq0u7ppMmwGJRY=,tag:cK4l4Evr7V9WEUEL7V9jtQ==,type:str]
|
||||
@@ -101,6 +104,8 @@ livekit:
|
||||
key_secret: ENC[AES256_GCM,data:n0SH6SLYXOwdjJ5nNku0Pg6/DZxmYG7iLfNNDbKlvF9DiCDxfn2ag1fToxWGz0qrAvqV6b3PdD123BURDzJ+gQ==,iv:1Pix12Pxr/kX/SqRWHOaSdccIZTQsSoFVGXYNyx2Rfo=,tag:QG1qDJPBUUtlih+TcXXsRQ==,type:str]
|
||||
coturn:
|
||||
static_auth_secret: ENC[AES256_GCM,data:osEBYgWGZl+SnqVV1G9IxMys/qDm6WTtj4nILYVw0klDjiB6vd21yA0ik/rLv9E6Y539uMCk3oB0NS7I72U1hQ==,iv:jruS3vfe0fVHY67qNhEgaCEp/9cR57UIu8a/LhdTC1o=,tag:vhxXhh9u4bOSu/lxINjvew==,type:str]
|
||||
affine:
|
||||
db_password: ENC[AES256_GCM,data:AbpoEbmeihtVIoRaWxVL8+v3oCk5iiia9qZLKgyy98qTuNZruiaV3kQN6clYvWgHbzJta5/H9e+xocrEtw8C/A==,iv:2vPeDAJuVujPgM+kr6AFAvat2MCJnsblebx23Ey7YNA=,tag:ukuK30aZ//MKabhSRtLNXw==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- recipient: age1pw7kluxp7872c63ne4jecq75glj060jkmqwzkk6esatuyck9egfswufdpk
|
||||
@@ -112,7 +117,7 @@ sops:
|
||||
VGZKdHpVeFRpQUxtSEkyaEhLMlBJcGsKLb0DvPNZosPBUuiX6qz1s5IO5INQh8CK
|
||||
ZtXTVClwMSmaUYhdSB2gKFrKVZHXTJZ4oAL5t/BpC0pOHyr+o96T3Q==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-02-12T13:19:01Z"
|
||||
mac: ENC[AES256_GCM,data:IVU1PbDwH1JKG3qPOtmfMZr7BJ7zR/UGQ167Tyf62w5V1gaiVoeqjD8/MR6OSvhMDNYxjJXRKg9E9N8q4JRxok34v5zOfqWchnXEP9wIS39kgsYJ1Hra7hOryd5n49/Xkwyen6f1VSe1nkKtldWS9XHwDBZRrSE+kaXcZTQmKIY=,iv:WLRVIEzR0MFsY6EAgyXZCHQz/xD4cSaeikA8nZqHy38=,tag:+TyOvwWkHa4fHYonKBfxyg==,type:str]
|
||||
lastmodified: "2026-02-18T10:39:02Z"
|
||||
mac: ENC[AES256_GCM,data:fz+15A9C6G4x3OUzoKY3yvUt75dx2aql2GEmnuJcPM1YC9KN083PodKQR1axr23w7C9S2/iTXEnYJhx3X+dfoJbTRWKVvraGHWQ9w5jbjVMVfU+97JxrtykdwXKmwlzTbF4lakHd4dWRv5e9aR7vN2JX1NUd9EuazQ0/xPeIVOQ=,iv:l4/r/poWY5IdKrM0IxbdWfg6JB7r+tssh9LIZDSNr8w=,tag:HE3C7LzWrzCKE91VdZnqXg==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.11.0
|
||||
|
||||
Reference in New Issue
Block a user