Compare commits
No commits in common. "793eeb316167ef73aedb730cfce5c332e1ebe7ea" and "caa6948199f28a9901feee89cc5037b6ae9c38e8" have entirely different histories.
793eeb3161
...
caa6948199
48 changed files with 187 additions and 275 deletions
|
|
@ -1,13 +1,11 @@
|
|||
{ dns, ... }:
|
||||
let
|
||||
{dns, ...}: let
|
||||
domain = "ruject.fun";
|
||||
database = {
|
||||
host = "127.0.0.1";
|
||||
port = 5432;
|
||||
};
|
||||
ipv4 = "94.156.112.0";
|
||||
in
|
||||
{
|
||||
in {
|
||||
services.nginx.enable = true;
|
||||
machine = {
|
||||
gateway = "10.0.0.1";
|
||||
|
|
@ -31,7 +29,7 @@ in
|
|||
"ns2"
|
||||
];
|
||||
|
||||
A = [ ipv4 ];
|
||||
A = [ipv4];
|
||||
|
||||
subdomains = rec {
|
||||
ns1 = host ipv4 null;
|
||||
|
|
@ -54,10 +52,10 @@ in
|
|||
};
|
||||
|
||||
TXT = [
|
||||
(with spf; strict [ "a:mail.ruject.fun" ])
|
||||
(with spf; strict ["a:mail.ruject.fun"])
|
||||
];
|
||||
|
||||
MX = with mx; [ (mx 10 "mail.ruject.fun.") ];
|
||||
MX = with mx; [(mx 10 "mail.ruject.fun.")];
|
||||
|
||||
DMARC = [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@
|
|||
let
|
||||
cfg = config.machine.bind;
|
||||
in
|
||||
with lib;
|
||||
mkIf cfg.enable {
|
||||
with lib; mkIf cfg.enable {
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [ cfg.port ];
|
||||
allowedUDPPorts = [ cfg.port ];
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@
|
|||
let
|
||||
cfg = config.machine.bind;
|
||||
in
|
||||
with lib;
|
||||
mkIf cfg.enable {
|
||||
with lib; mkIf cfg.enable {
|
||||
services.bind = {
|
||||
enable = cfg.enable;
|
||||
listenOnPort = cfg.port;
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@
|
|||
let
|
||||
cfg = config.machine.code-server;
|
||||
in
|
||||
with lib;
|
||||
mkIf cfg.enable {
|
||||
with lib; mkIf cfg.enable {
|
||||
services.nginx.virtualHosts = mkIf (cfg.domain != null) {
|
||||
${cfg.domain} = {
|
||||
enableACME = true;
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@
|
|||
let
|
||||
cfg = config.machine.code-server;
|
||||
in
|
||||
with lib;
|
||||
{
|
||||
with lib; {
|
||||
options.machine.code-server = {
|
||||
enable = mkEnableOption "code-server";
|
||||
port = mkOption {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@
|
|||
let
|
||||
cfg = config.machine.code-server;
|
||||
in
|
||||
with lib;
|
||||
mkIf cfg.enable {
|
||||
with lib; mkIf cfg.enable {
|
||||
services.code-server = {
|
||||
enable = cfg.enable;
|
||||
port = cfg.port;
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@
|
|||
let
|
||||
cfg = config.machine.coturn;
|
||||
in
|
||||
with lib;
|
||||
mkIf cfg.enable {
|
||||
with lib; mkIf cfg.enable {
|
||||
networking.firewall = {
|
||||
interfaces.enp2s0 =
|
||||
let
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ let
|
|||
realm
|
||||
;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
services.coturn = rec {
|
||||
inherit realm enable;
|
||||
no-cli = true;
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ let
|
|||
inherit (config.machine.forgejo) domain;
|
||||
address = "noreply@${domain}";
|
||||
in
|
||||
with lib;
|
||||
mkIf config.machine.mail.enable {
|
||||
with lib; mkIf config.machine.mail.enable {
|
||||
services.forgejo = {
|
||||
secrets = {
|
||||
mailer.PASSWD = sec."mail/servicePassword".path;
|
||||
|
|
|
|||
|
|
@ -10,15 +10,12 @@ let
|
|||
port
|
||||
;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [ port ];
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts =
|
||||
with lib;
|
||||
mkIf (domain != null) {
|
||||
services.nginx.virtualHosts = with lib; mkIf (domain != null) {
|
||||
"${domain}" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ let
|
|||
url = config.services.forgejo.settings.server.ROOT_URL;
|
||||
cfg = config.machine.forgejo;
|
||||
in
|
||||
with lib;
|
||||
mkIf cfg.enableRunner {
|
||||
with lib; mkIf cfg.enableRunner {
|
||||
sops.secrets = {
|
||||
"forgejo/runnerToken" = {
|
||||
sopsFile = ./../../secrets/common.yaml;
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ let
|
|||
cfg = config.machine.forgejo;
|
||||
customDir = config.services.forgejo.customDir;
|
||||
in
|
||||
with lib;
|
||||
mkIf cfg.enable {
|
||||
with lib; mkIf cfg.enable {
|
||||
systemd.tmpfiles.rules = [
|
||||
"d '${customDir}/templates' - forgejo forgejo - -"
|
||||
"d '${customDir}/public' - forgejo forgejo - -"
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@
|
|||
let
|
||||
inherit (config.machine.mail) enable;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
services.rspamd = {
|
||||
enable = true;
|
||||
overrides = {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@
|
|||
let
|
||||
inherit (config.machine.mail) enable;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
systemd.tmpfiles.rules = [
|
||||
"C /var/dkim/default.private 0600 root root - - ${sec.dkim_default_private.path}"
|
||||
];
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ let
|
|||
port
|
||||
;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
port
|
||||
];
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@ let
|
|||
port
|
||||
;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
services.minecraft-server = {
|
||||
inherit enable;
|
||||
dataDir = "/var/lib/minecraft";
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@
|
|||
let
|
||||
cfg = config.machine.mysql;
|
||||
in
|
||||
with lib;
|
||||
mkIf cfg.enable {
|
||||
with lib; mkIf cfg.enable {
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [ cfg.port ];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@ let
|
|||
enable
|
||||
;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
services.mysql = {
|
||||
inherit enable;
|
||||
package = pkgs.mysql84;
|
||||
|
|
|
|||
|
|
@ -11,11 +11,8 @@ let
|
|||
folder
|
||||
;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
services.nginx.virtualHosts =
|
||||
with lib;
|
||||
mkIf (domain != null) {
|
||||
with lib; mkIf enable {
|
||||
services.nginx.virtualHosts = with lib; mkIf (domain != null) {
|
||||
"${domain}" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,8 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
hostname,
|
||||
...
|
||||
}:
|
||||
{ config, lib, hostname, ... }:
|
||||
let
|
||||
inherit (config.machine) ipv4 gateway;
|
||||
in
|
||||
with lib;
|
||||
{
|
||||
with lib; {
|
||||
networking = {
|
||||
hostName = hostname;
|
||||
networkmanager.enable = mkDefault true;
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ let
|
|||
pgsqlEnable = config.machine.postgresql.enable;
|
||||
cfg = config.machine.nextcloud;
|
||||
in
|
||||
with lib;
|
||||
mkIf cfg.enable {
|
||||
with lib; mkIf cfg.enable {
|
||||
services.nextcloud.config =
|
||||
if pgsqlEnable then
|
||||
{
|
||||
|
|
@ -21,9 +20,7 @@ mkIf cfg.enable {
|
|||
dbhost = "localhost";
|
||||
};
|
||||
|
||||
services.postgresql =
|
||||
with lib;
|
||||
mkIf pgsqlEnable {
|
||||
services.postgresql = with lib; mkIf pgsqlEnable {
|
||||
ensureDatabases = [ "nextcloud" ];
|
||||
ensureUsers = [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@ let
|
|||
;
|
||||
address = "noreply@${host}";
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
services.nextcloud = {
|
||||
settings = {
|
||||
mail_smtpmode = "smtp";
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@
|
|||
let
|
||||
cfg = config.machine.nextcloud;
|
||||
in
|
||||
with lib;
|
||||
mkIf cfg.enable {
|
||||
with lib; mkIf cfg.enable {
|
||||
services.nextcloud = {
|
||||
enable = cfg.enable;
|
||||
appstoreEnable = false;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
{ lib, config, ... }:
|
||||
with lib;
|
||||
mkIf (config.services.nginx.enable) {
|
||||
with lib; mkIf (config.services.nginx.enable) {
|
||||
security.acme = {
|
||||
acceptTerms = true;
|
||||
defaults = {
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@
|
|||
let
|
||||
inherit (config.services.nginx) enable;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
80
|
||||
443
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@ let
|
|||
enable
|
||||
;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
services.postgresql = {
|
||||
inherit enable;
|
||||
settings = {
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ let
|
|||
port
|
||||
;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
services.prometheus = {
|
||||
inherit enable port;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@ let
|
|||
enable
|
||||
;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [
|
||||
# HTTP filer
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@ let
|
|||
|
||||
localhost = "http://localhost:5280";
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
security.acme.certs."${domain}".extraDomainNames = [
|
||||
"conference.${domain}"
|
||||
"upload.${domain}"
|
||||
|
|
@ -20,9 +19,7 @@ mkIf enable {
|
|||
users.groups.acme.members = [
|
||||
"prosody"
|
||||
];
|
||||
services.nginx.virtualHosts =
|
||||
with lib;
|
||||
mkIf (domain != null) {
|
||||
services.nginx.virtualHosts = with lib; mkIf (domain != null) {
|
||||
"${domain}".locations = {
|
||||
"= /xmpp-websocket" = {
|
||||
proxyPass = localhost;
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ let
|
|||
|
||||
sslCertDir = config.security.acme.certs."${domain}".directory;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
services.prosody = {
|
||||
inherit enable;
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@ let
|
|||
enable
|
||||
;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
sops.secrets = {
|
||||
"redis/password" = { };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@ let
|
|||
domain
|
||||
;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
services.roundcube = {
|
||||
inherit enable;
|
||||
dicts = with pkgs.aspellDicts; [
|
||||
|
|
|
|||
|
|
@ -7,11 +7,8 @@ let
|
|||
pgsqlEnable = config.machine.postgresql.enable;
|
||||
inherit (config.machine.synapse) enable;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
services.postgresql =
|
||||
with lib;
|
||||
mkIf pgsqlEnable {
|
||||
with lib; mkIf enable {
|
||||
services.postgresql = with lib; mkIf pgsqlEnable {
|
||||
ensureUsers = [
|
||||
{
|
||||
name = "matrix-synapse";
|
||||
|
|
@ -22,9 +19,7 @@ mkIf enable {
|
|||
};
|
||||
services.matrix-synapse.settings.database = {
|
||||
name = if pgsqlEnable then "psycopg2" else "sqlite3";
|
||||
args =
|
||||
with lib;
|
||||
mkIf pgsqlEnable {
|
||||
args = with lib; mkIf pgsqlEnable {
|
||||
host = "/run/postgresql";
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -52,8 +52,7 @@ let
|
|||
};
|
||||
};
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
services.nginx.virtualHosts.${domain} = {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@ let
|
|||
;
|
||||
address = "noreply@${domain}";
|
||||
in
|
||||
with lib;
|
||||
mkIf (enable && config.machine.mail.enable) {
|
||||
with lib; mkIf (enable && config.machine.mail.enable) {
|
||||
services.matrix-synapse = {
|
||||
settings = {
|
||||
admin_contact = address;
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ let
|
|||
redisEnable = config.machine.redis.enable;
|
||||
inherit (config.machine.synapse) enable;
|
||||
in
|
||||
with lib;
|
||||
mkIf (redisEnable && enable) {
|
||||
with lib; mkIf (redisEnable && enable) {
|
||||
services.redis.servers.matrix-synapse = {
|
||||
enable = true;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@ let
|
|||
enable
|
||||
;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
sops.secrets = {
|
||||
"matrix/registrationSharedSecret" = {
|
||||
sopsFile = ./../../secrets/common.yaml;
|
||||
|
|
@ -41,9 +40,7 @@ mkIf enable {
|
|||
smtp_pass: ${config.sops.placeholder."mail/servicePassword"}
|
||||
'';
|
||||
};
|
||||
services.matrix-synapse.extraConfigFiles =
|
||||
with lib;
|
||||
mkIf config.machine.synapse.enable [
|
||||
services.matrix-synapse.extraConfigFiles = with lib; mkIf config.machine.synapse.enable [
|
||||
config.sops.templates.matrix-synapse-config.path
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ let
|
|||
metrics
|
||||
;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
services.matrix-synapse = {
|
||||
inherit enable;
|
||||
enableRegistrationScript = true;
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@
|
|||
let
|
||||
inherit (config.machine.coturn) enable;
|
||||
in
|
||||
with lib;
|
||||
mkIf (enable && config.machine.coturn.enable) {
|
||||
with lib; mkIf (enable && config.machine.coturn.enable) {
|
||||
services.matrix-synapse.settings = with config.services.coturn; {
|
||||
turn_uris = [
|
||||
"turn:${realm}:3478?transport=udp"
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@ let
|
|||
port
|
||||
;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
services.uptime-kuma = {
|
||||
inherit enable;
|
||||
settings = {
|
||||
|
|
@ -19,9 +18,7 @@ mkIf enable {
|
|||
};
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts =
|
||||
with lib;
|
||||
mkIf (domain != null) {
|
||||
services.nginx.virtualHosts = with lib; mkIf (domain != null) {
|
||||
"${domain}" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
|
|
|
|||
|
|
@ -10,15 +10,12 @@ let
|
|||
port
|
||||
;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [ port ];
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts =
|
||||
with lib;
|
||||
mkIf (domain != null) {
|
||||
services.nginx.virtualHosts = with lib; mkIf (domain != null) {
|
||||
"${domain}" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ let
|
|||
port
|
||||
;
|
||||
in
|
||||
with lib;
|
||||
mkIf enable {
|
||||
with lib; mkIf enable {
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
# Web panel
|
||||
port
|
||||
|
|
|
|||
|
|
@ -2,22 +2,17 @@
|
|||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.machine.xray-3x-ui)
|
||||
}: 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) {
|
||||
in {
|
||||
services.nginx.virtualHosts = with lib; mkIf enable {
|
||||
${domain} = with lib; mkIf (domain != null) {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
locations."/" = {
|
||||
|
|
@ -31,9 +26,7 @@ in
|
|||
'';
|
||||
};
|
||||
};
|
||||
${subscriptions.domain} =
|
||||
with lib;
|
||||
mkIf (subscriptions.domain != null) {
|
||||
${subscriptions.domain} = with lib; mkIf (subscriptions.domain != null) {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
locations."/" = {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,6 @@
|
|||
# See https://github.com/sunmeplz/xray-3x-ui
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
|
|
@ -16,7 +11,8 @@ let
|
|||
minGoVersion = "1.26.0";
|
||||
|
||||
xray-3x-ui =
|
||||
assert assertMsg (versionAtLeast pkgs.go.version minGoVersion)
|
||||
assert assertMsg
|
||||
(versionAtLeast pkgs.go.version minGoVersion)
|
||||
"3x-ui requires Go >= ${minGoVersion}, but ${pkgs.go.version} is available";
|
||||
|
||||
pkgs.buildGoModule rec {
|
||||
|
|
@ -32,10 +28,7 @@ let
|
|||
|
||||
vendorHash = "sha256-M8YQTMfF/xZut4hxUcAfF2xGK625vwJNp4JS/zoXUCQ=";
|
||||
|
||||
ldflags = [
|
||||
"-s"
|
||||
"-w"
|
||||
];
|
||||
ldflags = [ "-s" "-w" ];
|
||||
|
||||
meta = with lib; {
|
||||
description = "Xray panel supporting multi-protocol multi-user";
|
||||
|
|
@ -46,8 +39,7 @@ let
|
|||
};
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
in {
|
||||
# Service implementation
|
||||
config = mkIf cfg.enable {
|
||||
# User and group configuration
|
||||
|
|
@ -93,14 +85,8 @@ in
|
|||
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"
|
||||
];
|
||||
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" "CAP_NET_ADMIN" ];
|
||||
CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" "CAP_NET_ADMIN" ];
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
programs.openclaw = {
|
||||
enable = true;
|
||||
installApp = false;
|
||||
systemd.enable = true;
|
||||
|
||||
bundledPlugins = {
|
||||
summarize.enable = true;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue