From c36e0c52d6f300bd979b6a54b2dcac9bf39255e0 Mon Sep 17 00:00:00 2001 From: Haonan Chen Date: Wed, 24 Dec 2025 14:01:35 +0800 Subject: [PATCH] packages/misskey-forwarder: init --- devices/vps6/default.nix | 1 + devices/vps6/secrets.yaml | 8 ++- flake/dev.nix | 7 +++ flake/dns/config/chn.moe.nix | 1 + modules/services/misskey-forwarder.nix | 53 ++++++++++++++++++++ modules/user/default.nix | 1 + packages/default.nix | 1 + packages/misskey-forwarder/.envrc | 1 + packages/misskey-forwarder/CMakeLists.txt | 24 +++++++++ packages/misskey-forwarder/default.nix | 8 +++ packages/misskey-forwarder/src/main.cpp | 60 +++++++++++++++++++++++ 11 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 modules/services/misskey-forwarder.nix create mode 100644 packages/misskey-forwarder/.envrc create mode 100644 packages/misskey-forwarder/CMakeLists.txt create mode 100644 packages/misskey-forwarder/default.nix create mode 100644 packages/misskey-forwarder/src/main.cpp diff --git a/devices/vps6/default.nix b/devices/vps6/default.nix index 1c2f5a6f..5ab38ecb 100644 --- a/devices/vps6/default.nix +++ b/devices/vps6/default.nix @@ -61,6 +61,7 @@ inputs: beesd."/" = {}; coredns.interface = "ens18"; headscale = {}; + misskey-forwarder = {}; }; }; networking.nftables.tables.forward = diff --git a/devices/vps6/secrets.yaml b/devices/vps6/secrets.yaml index ace993c7..00713ef3 100644 --- a/devices/vps6/secrets.yaml +++ b/devices/vps6/secrets.yaml @@ -5,6 +5,10 @@ coturn: tinc: ENC[AES256_GCM,data:E3OrPA67R48x5FJUW0ZbERlclz8Z/XokAaGTeBQLPEHSeqEArHYSZkdJRZejFrBruJPlGZMPNBQzlIBXOfXKwMnlBDaGJIIJHIzPDGG9W7QF4IIRK/BjVZHFwfKvZtbUDGsqLcCSe5+ttmyucBaFGquXhnD/Tu09uyWtRvS10KAJLY0Z2/16CFB1+8egJIcYw2TFXObo+KR92Va0qwiDSepKaJtYLimDGRKk04QGj+BYa5y8PjIG6bz8UG82mmCiV7XM3EPlSMA=,iv:kawsklNGFbRhxKuUwvNL2WyBxuYu2T/uks1cJ4i8NhA=,tag:V+jAaxQX7JCiR5+wIVW4Nw==,type:str] postgresql: headscale: ENC[AES256_GCM,data:z2cyyT1TcIhNJCBeGn072aFI2nAioWZQvpyzoky4tWtMymKlw4ilOtSYAsp+kaNOoqvWSmoAQNJLNzeDk1iTCQ==,iv:hZdS/CAVBO0k/AmX3qw3YwTYgK49Aeu5QI3YCAduiZ0=,tag:2l4GPV/T2GHjAAUDX3LaEA==,type:str] +misskey-forwarder: + secret: ENC[AES256_GCM,data:MknbcYWGDptZ2PynMFyScA9Ka/KrtBPmMTgy/cSfXIw=,iv:fg2x6fjM/KhnC014UkNAYlHKWgMnamd+0bkjIELs04Y=,tag:H8azwIcxm9DMYQMWlWJQZw==,type:str] + telegramBotToken: ENC[AES256_GCM,data:Ba/De5aBmMSCE6Rucqy8uw7XyPSXgy+1c8tb1uJn27//+U0RrzEQnS6TcRy5rQ==,iv:pWIP626x5om98JxR49iHYUCf50IpJ1MKdi0Mml6+gQU=,tag:rpkQFqd7j+3lfAIzuRybMg==,type:str] + telegramChatId: ENC[AES256_GCM,data:HpwzLKbl1WykNj0uB0I=,iv:zA45v4ZNroZNCdoFntTeonwq+ulHpPey25WiuXGCGi4=,tag:yL6P2Vgv/LxTpDWSnZwTLg==,type:str] sops: age: - recipient: age19ax6vm3pv8rph5tq3mmehd9sy9jk823tw8svsd790r0lkslycquqvlwz9m @@ -25,7 +29,7 @@ sops: ZXFTU3ZCaW1pTVh0RUJzdDdGdHlPYTgK2mlgcX2kEc8+2UDdBnhUm6IIuh8V6agW ooxH9OEPXUVI/4JcDo4v8ZUhAyU1ehLH0Ef7PJCChOZe2KZmWSNbhA== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-12-04T12:35:00Z" - mac: ENC[AES256_GCM,data:hiEzLAEtU82Be/+nuMv10/ex/ZacXkNR9LkxdBn/x3kY/0uwHSDI9LGjn0b0/KWIg5zLoxV+zPZEBJQhN4QcRzDT6538zwc0yTv9fkFS0NUF5GchHi8Is6EjjHANbSLe3MEwFumKjx3Lm8AyMjcOXiCckzo4aXV98SHZW9EMVbQ=,iv:Nzzlfsm2aMSVa6NNh4ar67J5dzheWRIVLHSUu5ndjvE=,tag:jO8x2LUpP75g7cXyIYDfQw==,type:str] + lastmodified: "2025-12-25T10:37:16Z" + mac: ENC[AES256_GCM,data:ffiDjDbD/BkzBt6zzly3vDtRbRSfpgGmRIjVarPvTuc/GLUPfUpDzibT27E7LI4aBK8Utt9CCip18zmtRKF4z4h4GfHrp0pb6PipyT43+bXqzmsBrCENDiJhiSYhc6VcpA1Xn18Ce8pMaIHkYrrABqtHxU9ML3NEy6vQgN4jim8=,iv:AnWInBNkjBteWhNPASwawH+6m9yopFnYFIh7dP1jVHo=,tag:6WBLuVOXrj1jOhOHUQ7cYA==,type:str] unencrypted_suffix: _unencrypted version: 3.11.0 diff --git a/flake/dev.nix b/flake/dev.nix index 3224606a..eb4c9df4 100644 --- a/flake/dev.nix +++ b/flake/dev.nix @@ -55,4 +55,11 @@ CMAKE_EXPORT_COMPILE_COMMANDS = "1"; hardeningDisable = [ "all" ]; }; + misskey-forwarder = pkgs.mkShell.override { stdenv = pkgs.clang18Stdenv; } + { + inputsFrom = [ pkgs.localPackages.misskey-forwarder ]; + packages = [ pkgs.llvmPackages_18.clang-tools ]; + CMAKE_EXPORT_COMPILE_COMMANDS = "1"; + hardeningDisable = [ "all" ]; + }; } diff --git a/flake/dns/config/chn.moe.nix b/flake/dns/config/chn.moe.nix index c5dc43a8..0413fc7f 100644 --- a/flake/dns/config/chn.moe.nix +++ b/flake/dns/config/chn.moe.nix @@ -27,6 +27,7 @@ let "git" "grafana" "peertube" "send" "vikunja" "xservernas" "freshrss" "huginn" "nextcloud" "rsshub" "vaultwarden" "webdav" "synapse" "misskey" "api" ]; + "tinc0.pc" = [ "misskey-forwarder" ]; }; a = { diff --git a/modules/services/misskey-forwarder.nix b/modules/services/misskey-forwarder.nix new file mode 100644 index 00000000..734f68f8 --- /dev/null +++ b/modules/services/misskey-forwarder.nix @@ -0,0 +1,53 @@ +inputs: +{ + options.nixos.services.misskey-forwarder = let inherit (inputs.lib) mkOption types; in mkOption + { type = types.nullOr (types.submodule {}); default = null; }; + config = let inherit (inputs.config.nixos.services) misskey-forwarder; in inputs.lib.mkIf (misskey-forwarder != null) + { + users = + { + users.misskey-forwarder = + { uid = inputs.config.nixos.user.uid.misskey-forwarder; group = "misskey-forwarder"; isSystemUser = true; }; + groups.misskey-forwarder.gid = inputs.config.nixos.user.gid.misskey-forwarder; + }; + systemd = + { + services.misskey-forwarder = + { + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = + { + User = inputs.config.users.users.misskey-forwarder.name; + Group = inputs.config.users.users.misskey-forwarder.group; + ExecStart = + let forwarder = inputs.pkgs.localPackages.misskey-forwarder.override + { configFile = inputs.config.nixos.system.sops.templates."misskey-forwarder/config.yml".path; }; + in "${forwarder}/bin/misskey-forwarder"; + }; + }; + }; + nixos = + { + services.nginx.https."misskey-forwarder.chn.moe".location."/".proxy.upstream = "http://127.0.0.1:9173"; + system.sops = + { + templates."misskey-forwarder/config.yml" = + { + owner = "misskey-forwarder"; + content = + let inherit (inputs.config.nixos.system.sops) placeholder; + in builtins.toJSON + { + Secret = placeholder."misskey-forwarder/secret"; + TelegramBotToken = placeholder."misskey-forwarder/telegramBotToken"; + TelegramChatId = placeholder."misskey-forwarder/telegramChatId"; + ServerPort = 9173; + }; + }; + secrets = inputs.lib.genAttrs' [ "secret" "telegramBotToken" "telegramChatId" ] + (n: inputs.lib.nameValuePair "misskey-forwarder/${n}" {}); + }; + }; + }; +} diff --git a/modules/user/default.nix b/modules/user/default.nix index aa0867e3..81e007cb 100644 --- a/modules/user/default.nix +++ b/modules/user/default.nix @@ -56,6 +56,7 @@ inputs: hpcstat = 2011; speedtest = 2012; tailscale = 2013; + misskey-forwarder = 2014; }; }; gid = mkOption diff --git a/packages/default.nix b/packages/default.nix index ec2d63a3..5c40ad97 100644 --- a/packages/default.nix +++ b/packages/default.nix @@ -144,6 +144,7 @@ inputs: rec buildProxy = inputs.pkgs.lib.mkBuildproxy ./pybinding/proxy.nix; }; brokenaxes = inputs.pkgs.python3Packages.callPackage ./brokenaxes.nix { src = inputs.topInputs.brokenaxes; }; + misskey-forwarder = inputs.pkgs.callPackage ./misskey-forwarder { inherit biu; stdenv = inputs.pkgs.clang18Stdenv; }; fromYaml = content: builtins.fromJSON (builtins.readFile (inputs.pkgs.runCommand "toJSON" {} diff --git a/packages/misskey-forwarder/.envrc b/packages/misskey-forwarder/.envrc new file mode 100644 index 00000000..7fbecefe --- /dev/null +++ b/packages/misskey-forwarder/.envrc @@ -0,0 +1 @@ +use flake .#misskey-forwarder diff --git a/packages/misskey-forwarder/CMakeLists.txt b/packages/misskey-forwarder/CMakeLists.txt new file mode 100644 index 00000000..b95a6727 --- /dev/null +++ b/packages/misskey-forwarder/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.14) +project(misskey-forwarder VERSION 0.0.0 LANGUAGES CXX) +enable_testing() +include(GNUInstallDirs) + +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message("Setting build type to 'Release' as none was specified.") + set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") +endif() + +find_package(biu REQUIRED) +find_package(httplib REQUIRED) + +add_executable(misskey-forwarder src/main.cpp) +target_link_libraries(misskey-forwarder PRIVATE biu::biu httplib::httplib) +target_compile_features(misskey-forwarder PRIVATE cxx_std_23) +target_compile_definitions(misskey-forwarder PRIVATE FORWARDER_CONFIG_FILE="${FORWARDER_CONFIG_FILE}") +install(TARGETS misskey-forwarder RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +get_property(ImportedTargets DIRECTORY "${CMAKE_SOURCE_DIR}" PROPERTY IMPORTED_TARGETS) +message("Imported targets: ${ImportedTargets}") +message("List of compile features: ${CMAKE_CXX_COMPILE_FEATURES}") +message("CMake build type: ${CMAKE_BUILD_TYPE}") diff --git a/packages/misskey-forwarder/default.nix b/packages/misskey-forwarder/default.nix new file mode 100644 index 00000000..a15f50d4 --- /dev/null +++ b/packages/misskey-forwarder/default.nix @@ -0,0 +1,8 @@ +{ lib, stdenv, cmake, pkg-config, biu, configFile ? null, httplib }: stdenv.mkDerivation +{ + name = "misskey-forwarder"; + src = ./.; + buildInputs = [ biu httplib ]; + nativeBuildInputs = [ cmake pkg-config ]; + cmakeFlags = lib.optional (configFile != null) [ "-DFORWARDER_CONFIG_FILE=${configFile}" ]; +} diff --git a/packages/misskey-forwarder/src/main.cpp b/packages/misskey-forwarder/src/main.cpp new file mode 100644 index 00000000..00b0c32a --- /dev/null +++ b/packages/misskey-forwarder/src/main.cpp @@ -0,0 +1,60 @@ +# include +# include +# include +# ifndef FORWARDER_CONFIG_FILE +# define FORWARDER_CONFIG_FILE "./config.yml" +# endif + +int main() +{ + using namespace biu::literals; + biu::Logger::Guard log; + + struct Config + { + std::string Secret; + std::string TelegramBotToken; + std::string TelegramChatId; + int ServerPort; + }; + auto config = YAML::LoadFile(FORWARDER_CONFIG_FILE).as(); + + biu::Logger::try_exec([&] + { + httplib::Server svr; + + svr.Post("/", [&](const httplib::Request& req, httplib::Response& res) + { + biu::Logger::try_exec([&] + { + if (req.get_header_value("x-misskey-hook-secret") != config.Secret) + throw std::runtime_error("Invalid secret key."); + + struct Content + { + std::string type, server; + struct { struct + { + std::string text, visibility; + struct Renote { std::string id; }; + std::optional renote; + } note; } body; + }; + auto content = YAML::Load(req.body).as(); + + if (content.type != "note" || content.body.note.visibility != "public") return; + std::string text = content.body.note.text; + if (content.body.note.renote) + text += "\nšŸ” Renote: https://{}/notes/{}"_f(content.server, content.body.note.renote->id); + + TgBot::Bot bot(config.TelegramBotToken); + bot.getApi().sendMessage(config.TelegramChatId, text); + + res.status = 200; + res.body = "OK"; + }); + }); + svr.listen("0.0.0.0", config.ServerPort); + return 0; + }); +}