From dcaeca9634fbc8fd01aa63211eef43101199f4a8 Mon Sep 17 00:00:00 2001 From: serviceuser Date: Sun, 19 Jan 2025 11:16:19 +0100 Subject: [PATCH] feat: nixos setup --- nixos.nix | 60 ++++++++ prodsody/default.nix | 23 +++ prodsody/mod_jitsi_rooms.lua | 277 +++++++++++++++++------------------ prodsody/result | 1 + 4 files changed, 222 insertions(+), 139 deletions(-) create mode 100644 nixos.nix create mode 100644 prodsody/default.nix create mode 120000 prodsody/result diff --git a/nixos.nix b/nixos.nix new file mode 100644 index 0000000..4bb062f --- /dev/null +++ b/nixos.nix @@ -0,0 +1,60 @@ +{ + lib, + pkgs, + config, + ... +}: +with lib; +let + cfg = config.services.jitsi-rooms; + backendPort = 8081; + wsPort = 9160; + address = "127.0.0.1"; + backend = (pkgs.callPackage ./backend/default.nix { }); + frontend = (pkgs.callPackage ./frontend/default.nix { }); + prodsodyPackage = (pkgs.callPackage ./prodsody/default.nix { }); +in +{ + options.services.jitsi-rooms = { + enable = mkEnableOption "jitsi-rooms service"; + nginxHostname = mkOption { + type = types.str; + default = "treffen.filefighter.de"; + }; + }; + + config = mkIf cfg.enable { + systemd.services.jitsi-rooms = { + description = "jitsi-rooms"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + ExecStart = "${backend}/bin/jitsi-rooms-exe"; + DynamicUser = true; + User = "jitsi-rooms-backend"; + }; + }; + + services.nginx.virtualHosts.${cfg.nginxHostname} = { + forceSSL = true; + enableACME = true; + + locations = { + "/" = { + root = frontend; + tryFiles = "$uri $uri/ /index.html"; + }; + "/ws" = { + proxyPass = "http://${address}:${toString wsPort}"; + }; + }; + }; + services.prosody = { + extraPluginPaths = [ "${prodsodyPackage}/share" ]; + extraModules = [ "jitsi_rooms" ]; + }; + }; + +} diff --git a/prodsody/default.nix b/prodsody/default.nix new file mode 100644 index 0000000..1f15f09 --- /dev/null +++ b/prodsody/default.nix @@ -0,0 +1,23 @@ +{ + pkgs ? import { }, +}: + +pkgs.stdenv.mkDerivation { + pname = "jitsi-room-prosody"; + version = "1.0.0"; + + src = pkgs.lib.cleanSource ./.; + dontBuild = true; + + installPhase = '' + runHook preInstall + mkdir -p $out/share + mv *.lua $out/share/ + runHook postInstall + ''; + + meta = { + name = "Jitsi Rooms Prosody Plugin"; + description = "Prosody configuration for Jitsi Rooms"; + }; +} diff --git a/prodsody/mod_jitsi_rooms.lua b/prodsody/mod_jitsi_rooms.lua index 2b81a61..470f7f7 100644 --- a/prodsody/mod_jitsi_rooms.lua +++ b/prodsody/mod_jitsi_rooms.lua @@ -1,95 +1,94 @@ ---@diagnostic disable: deprecated -local it = require "util.iterators"; -local json = require "util.json"; -local array = require "util.array"; -local iterators = require "util.iterators"; -local jid = require "util.jid"; -local http = require "util.http"; - -local async_handler_wrapper = module:require "util".async_handler_wrapper; -local get_room_from_jid = module:require "util".get_room_from_jid; -local muc_domain_prefix = module:get_option_string("muc_mapper_domain_prefix", "conference"); -local domain_name = "meet.jitsi" +local it = require("util.iterators") +local json = require("util.json") +local array = require("util.array") +local iterators = require("util.iterators") +local jid = require("util.jid") +local http = require("util.http") +local async_handler_wrapper = module:require("util").async_handler_wrapper +local get_room_from_jid = module:require("util").get_room_from_jid +local muc_domain_prefix = module:get_option_string("muc_mapper_domain_prefix", "conference") +local domain_name = "meet.filefighter.de" function get_participants_for_room(room_name) - local room_address = jid.join(room_name, muc_domain_prefix .. "." .. domain_name); - local decoded_room_address = http.urldecode(room_address); - local room = get_room_from_jid(decoded_room_address); - local occupants_json = array(); - if room then - local occupants = room._occupants; - if occupants then - participant_count = iterators.count(room:each_occupant()); - for _, occupant in room:each_occupant() do - -- filter focus as we keep it as hidden participant - if string.sub(occupant.nick, -string.len("/focus")) ~= "/focus" then - for _, pr in occupant:each_session() do - local nick = pr:get_child_text("nick", "http://jabber.org/protocol/nick") or ""; - local email = pr:get_child_text("email") or ""; - local avatarURL = pr:get_child_text("avatarURL") or "" -- does not work :( - occupants_json:push({ - jid = tostring(occupant.nick), - email = tostring(email), - displayName = tostring(nick), - avatarURL = tostring(avatarURL) - }); - end - end - end - end - log("debug", - "there are %s occupants in room", tostring(participant_count)); - return occupants_json - else - log("debug", "no such room exists"); - end - return occupants_json + local room_address = jid.join(room_name, muc_domain_prefix .. "." .. domain_name) + local decoded_room_address = http.urldecode(room_address) + local room = get_room_from_jid(decoded_room_address) + local occupants_json = array() + if room then + local occupants = room._occupants + if occupants then + participant_count = iterators.count(room:each_occupant()) + for _, occupant in room:each_occupant() do + -- filter focus as we keep it as hidden participant + if string.sub(occupant.nick, -string.len("/focus")) ~= "/focus" then + for _, pr in occupant:each_session() do + local nick = pr:get_child_text("nick", "http://jabber.org/protocol/nick") or "" + local email = pr:get_child_text("email") or "" + local avatarURL = pr:get_child_text("avatarURL") or "" -- does not work :( + occupants_json:push({ + jid = tostring(occupant.nick), + email = tostring(email), + displayName = tostring(nick), + avatarURL = tostring(avatarURL), + }) + end + end + end + end + log("debug", "there are %s occupants in room", tostring(participant_count)) + return occupants_json + else + log("debug", "no such room exists") + end + return occupants_json end function get_all_rooms_with_participants() - local sessions = prosody.full_sessions; + local sessions = prosody.full_sessions - local someTable = (it.join(it.keys(sessions))); - local actual_rooms = someTable[1][2] + local someTable = (it.join(it.keys(sessions))) + local actual_rooms = someTable[1][2] - local roomNames = {} - for _, v in pairs(actual_rooms) do - local roomName = v["jitsi_web_query_room"] - if (roomName ~= nil) then - roomNames[roomName] = true; - end - end + local roomNames = {} + for _, v in pairs(actual_rooms) do + local roomName = v["jitsi_web_query_room"] + if roomName ~= nil then + roomNames[roomName] = true + end + end - local rooms_with_participants = array(); - for room_name, _ in pairs(roomNames) do - local room = { roomName = room_name, participants = get_participants_for_room(room_name) } - rooms_with_participants:push(room) - end - return json.encode(rooms_with_participants) + local rooms_with_participants = array() + for room_name, _ in pairs(roomNames) do + local room = { roomName = room_name, participants = get_participants_for_room(room_name) } + rooms_with_participants:push(room) + end + return json.encode(rooms_with_participants) end --- Handles request for retrieving the room participants details -- @param event the http event, holds the request query -- @return GET response, containing a json with participants details function handle_get_sessions(event) - handle_room_event() - return { status_code = 200, body = get_all_rooms_with_participants() }; + handle_room_event() + return { status_code = 200, body = get_all_rooms_with_participants() } end function module.load() - -- Ensure that mod_http is loaded: - -- - module:depends("http"); + -- Ensure that mod_http is loaded: + -- + module:depends("http") - -- Now publish our HTTP 'app': - module:log("info", "Hello! You can reach me at: %s", module:http_url()); - module:provides("http", { - route = { - ["GET"] = function(event) return async_handler_wrapper(event, handle_get_sessions) end, - - }, - }); + -- Now publish our HTTP 'app': + module:log("info", "Hello! You can reach me at: %s", module:http_url()) + module:provides("http", { + route = { + ["GET"] = function(event) + return async_handler_wrapper(event, handle_get_sessions) + end, + }, + }) end -------- event stuff @@ -103,104 +102,104 @@ end -- -- -local http = require "net.http"; -local is_healthcheck_room = module:require "util".is_healthcheck_room; +local http = require("net.http") +local is_healthcheck_room = module:require("util").is_healthcheck_room --- Start non-blocking HTTP call -- @param url URL to call -- @param options options table as expected by net.http where we provide optional headers, body or method. local function async_http_request(url, options) - local completed = false; - local timed_out = false; + local completed = false + local timed_out = false - local function cb_(response_body, response_code) - if not timed_out then -- request completed before timeout - completed = true; + local function cb_(response_body, response_code) + if not timed_out then -- request completed before timeout + completed = true - module:log("debug", "%s %s returned code %s", options.method, url, response_code); - end - end + module:log("debug", "%s %s returned code %s", options.method, url, response_code) + end + end - http.request(url, options, cb_); + http.request(url, options, cb_) end --- Checks if event is triggered by healthchecks or focus user. function is_system_event(event) - if event == nil or event.room == nil or event.room.jid == nil then - return true; - end + if event == nil or event.room == nil or event.room.jid == nil then + return true + end - if is_healthcheck_room(event.room.jid) then - return true; - end + if is_healthcheck_room(event.room.jid) then + return true + end - if event.occupant and jid.node(event.occupant.jid) == "focus" then - return true; - end + if event.occupant and jid.node(event.occupant.jid) == "focus" then + return true + end - return false; + return false end function handle_room_event(event) - module:log('info', "hallo irgendwas ist passiert"); - if is_system_event(event) then - return; - end + module:log("info", "hallo irgendwas ist passiert") + if is_system_event(event) then + return + end - async_http_request("http://jitsi-rooms:8081/roomdata", - { - method = "POST", - body = get_all_rooms_with_participants() - }) + async_http_request("http://localhost:8081/roomdata", { + method = "POST", + body = get_all_rooms_with_participants(), + }) end --Helper function to wait till a component is loaded before running the given callback function run_when_component_loaded(component_host_name, callback) - local function trigger_callback() - module:log('info', 'Component loaded %s', component_host_name); - callback(module:context(component_host_name), component_host_name); - end + local function trigger_callback() + module:log("info", "Component loaded %s", component_host_name) + callback(module:context(component_host_name), component_host_name) + end - if prosody.hosts[component_host_name] == nil then - module:log('debug', 'Host %s not yet loaded. Will trigger when it is loaded.', component_host_name); - prosody.events.add_handler('host-activated', function(host) - if host == component_host_name then - trigger_callback(); - end - end); - else - trigger_callback(); - end + if prosody.hosts[component_host_name] == nil then + module:log("debug", "Host %s not yet loaded. Will trigger when it is loaded.", component_host_name) + prosody.events.add_handler("host-activated", function(host) + if host == component_host_name then + trigger_callback() + end + end) + else + trigger_callback() + end end -- Helper function to wait till a component's muc module is loaded before running the given callback function run_when_muc_module_loaded(component_host_module, component_host_name, callback) - local function trigger_callback() - module:log('info', 'MUC module loaded for %s', component_host_name); - callback(prosody.hosts[component_host_name].modules.muc, component_host_module); - end + local function trigger_callback() + module:log("info", "MUC module loaded for %s", component_host_name) + callback(prosody.hosts[component_host_name].modules.muc, component_host_module) + end - if prosody.hosts[component_host_name].modules.muc == nil then - module:log('debug', 'MUC module for %s not yet loaded. Will trigger when it is loaded.', component_host_name); - prosody.hosts[component_host_name].events.add_handler('module-loaded', function(event) - if (event.module == 'muc') then - trigger_callback(); - end - end); - else - trigger_callback() - end + if prosody.hosts[component_host_name].modules.muc == nil then + module:log("debug", "MUC module for %s not yet loaded. Will trigger when it is loaded.", component_host_name) + prosody.hosts[component_host_name].events.add_handler("module-loaded", function(event) + if event.module == "muc" then + trigger_callback() + end + end) + else + trigger_callback() + end end local main_muc_component_host = muc_domain_prefix .. "." .. domain_name +-- local main_muc_component_host = module:get_option_string("muc_component") -- Handle events on main muc module run_when_component_loaded(main_muc_component_host, function(host_module, host_name) - run_when_muc_module_loaded(host_module, host_name, function(main_muc, main_module) - main_muc_service = main_muc; -- so it can be accessed from breakout muc event handlers + run_when_muc_module_loaded(host_module, host_name, function(main_muc, main_module) + main_muc_service = main_muc -- so it can be accessed from breakout muc event handlers - -- the following must run after speakerstats (priority -1) - main_module:hook("muc-room-created", handle_room_event, -3); -- must run after handle_main_room_created - main_module:hook("muc-occupant-joined", handle_room_event, -2); - main_module:hook("muc-occupant-left", handle_room_event, -2); -- see also https://issues.prosody.im/1743 - main_module:hook("muc-room-destroyed", handle_room_event, -2); - end); -end); + -- the following must run after speakerstats (priority -1) + main_module:hook("muc-room-created", handle_room_event, -3) -- must run after handle_main_room_created + main_module:hook("muc-occupant-joined", handle_room_event, -2) + main_module:hook("muc-occupant-left", handle_room_event, -2) -- see also https://issues.prosody.im/1743 + main_module:hook("muc-room-destroyed", handle_room_event, -2) + end) +end) diff --git a/prodsody/result b/prodsody/result new file mode 120000 index 0000000..437c8d3 --- /dev/null +++ b/prodsody/result @@ -0,0 +1 @@ +/nix/store/88ak9a0jwp38h95kb3hpd3965pmbhnjx-jitsi-room-prosody-1.0.0 \ No newline at end of file