feat: nixos setup

This commit is contained in:
FileFighter ServiceUser 2025-01-19 11:16:19 +01:00
parent a3461caf81
commit dcaeca9634
4 changed files with 222 additions and 139 deletions

60
nixos.nix Normal file
View file

@ -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" ];
};
};
}

23
prodsody/default.nix Normal file
View file

@ -0,0 +1,23 @@
{
pkgs ? import <nixpkgs> { },
}:
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";
};
}

View file

@ -1,95 +1,94 @@
---@diagnostic disable: deprecated ---@diagnostic disable: deprecated
local it = require "util.iterators"; local it = require("util.iterators")
local json = require "util.json"; local json = require("util.json")
local array = require "util.array"; local array = require("util.array")
local iterators = require "util.iterators"; local iterators = require("util.iterators")
local jid = require "util.jid"; local jid = require("util.jid")
local http = require "util.http"; 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 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) function get_participants_for_room(room_name)
local room_address = jid.join(room_name, muc_domain_prefix .. "." .. domain_name); local room_address = jid.join(room_name, muc_domain_prefix .. "." .. domain_name)
local decoded_room_address = http.urldecode(room_address); local decoded_room_address = http.urldecode(room_address)
local room = get_room_from_jid(decoded_room_address); local room = get_room_from_jid(decoded_room_address)
local occupants_json = array(); local occupants_json = array()
if room then if room then
local occupants = room._occupants; local occupants = room._occupants
if occupants then if occupants then
participant_count = iterators.count(room:each_occupant()); participant_count = iterators.count(room:each_occupant())
for _, occupant in room:each_occupant() do for _, occupant in room:each_occupant() do
-- filter focus as we keep it as hidden participant -- filter focus as we keep it as hidden participant
if string.sub(occupant.nick, -string.len("/focus")) ~= "/focus" then if string.sub(occupant.nick, -string.len("/focus")) ~= "/focus" then
for _, pr in occupant:each_session() do for _, pr in occupant:each_session() do
local nick = pr:get_child_text("nick", "http://jabber.org/protocol/nick") or ""; local nick = pr:get_child_text("nick", "http://jabber.org/protocol/nick") or ""
local email = pr:get_child_text("email") or ""; local email = pr:get_child_text("email") or ""
local avatarURL = pr:get_child_text("avatarURL") or "" -- does not work :( local avatarURL = pr:get_child_text("avatarURL") or "" -- does not work :(
occupants_json:push({ occupants_json:push({
jid = tostring(occupant.nick), jid = tostring(occupant.nick),
email = tostring(email), email = tostring(email),
displayName = tostring(nick), displayName = tostring(nick),
avatarURL = tostring(avatarURL) avatarURL = tostring(avatarURL),
}); })
end end
end end
end end
end end
log("debug", log("debug", "there are %s occupants in room", tostring(participant_count))
"there are %s occupants in room", tostring(participant_count)); return occupants_json
return occupants_json else
else log("debug", "no such room exists")
log("debug", "no such room exists"); end
end return occupants_json
return occupants_json
end end
function get_all_rooms_with_participants() function get_all_rooms_with_participants()
local sessions = prosody.full_sessions; local sessions = prosody.full_sessions
local someTable = (it.join(it.keys(sessions))); local someTable = (it.join(it.keys(sessions)))
local actual_rooms = someTable[1][2] local actual_rooms = someTable[1][2]
local roomNames = {} local roomNames = {}
for _, v in pairs(actual_rooms) do for _, v in pairs(actual_rooms) do
local roomName = v["jitsi_web_query_room"] local roomName = v["jitsi_web_query_room"]
if (roomName ~= nil) then if roomName ~= nil then
roomNames[roomName] = true; roomNames[roomName] = true
end end
end end
local rooms_with_participants = array(); local rooms_with_participants = array()
for room_name, _ in pairs(roomNames) do for room_name, _ in pairs(roomNames) do
local room = { roomName = room_name, participants = get_participants_for_room(room_name) } local room = { roomName = room_name, participants = get_participants_for_room(room_name) }
rooms_with_participants:push(room) rooms_with_participants:push(room)
end end
return json.encode(rooms_with_participants) return json.encode(rooms_with_participants)
end end
--- Handles request for retrieving the room participants details --- Handles request for retrieving the room participants details
-- @param event the http event, holds the request query -- @param event the http event, holds the request query
-- @return GET response, containing a json with participants details -- @return GET response, containing a json with participants details
function handle_get_sessions(event) function handle_get_sessions(event)
handle_room_event() handle_room_event()
return { status_code = 200, body = get_all_rooms_with_participants() }; return { status_code = 200, body = get_all_rooms_with_participants() }
end end
function module.load() function module.load()
-- Ensure that mod_http is loaded: -- Ensure that mod_http is loaded:
-- --
module:depends("http"); module:depends("http")
-- Now publish our HTTP 'app': -- Now publish our HTTP 'app':
module:log("info", "Hello! You can reach me at: %s", module:http_url()); module:log("info", "Hello! You can reach me at: %s", module:http_url())
module:provides("http", { module:provides("http", {
route = { route = {
["GET"] = function(event) return async_handler_wrapper(event, handle_get_sessions) end, ["GET"] = function(event)
return async_handler_wrapper(event, handle_get_sessions)
}, end,
}); },
})
end end
-------- event stuff -------- event stuff
@ -103,104 +102,104 @@ end
-- --
-- --
local http = require "net.http"; local http = require("net.http")
local is_healthcheck_room = module:require "util".is_healthcheck_room; local is_healthcheck_room = module:require("util").is_healthcheck_room
--- Start non-blocking HTTP call --- Start non-blocking HTTP call
-- @param url URL to call -- @param url URL to call
-- @param options options table as expected by net.http where we provide optional headers, body or method. -- @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 function async_http_request(url, options)
local completed = false; local completed = false
local timed_out = false; local timed_out = false
local function cb_(response_body, response_code) local function cb_(response_body, response_code)
if not timed_out then -- request completed before timeout if not timed_out then -- request completed before timeout
completed = true; completed = true
module:log("debug", "%s %s returned code %s", options.method, url, response_code); module:log("debug", "%s %s returned code %s", options.method, url, response_code)
end end
end end
http.request(url, options, cb_); http.request(url, options, cb_)
end end
--- Checks if event is triggered by healthchecks or focus user. --- Checks if event is triggered by healthchecks or focus user.
function is_system_event(event) function is_system_event(event)
if event == nil or event.room == nil or event.room.jid == nil then if event == nil or event.room == nil or event.room.jid == nil then
return true; return true
end end
if is_healthcheck_room(event.room.jid) then if is_healthcheck_room(event.room.jid) then
return true; return true
end end
if event.occupant and jid.node(event.occupant.jid) == "focus" then if event.occupant and jid.node(event.occupant.jid) == "focus" then
return true; return true
end end
return false; return false
end end
function handle_room_event(event) function handle_room_event(event)
module:log('info', "hallo irgendwas ist passiert"); module:log("info", "hallo irgendwas ist passiert")
if is_system_event(event) then if is_system_event(event) then
return; return
end end
async_http_request("http://jitsi-rooms:8081/roomdata", async_http_request("http://localhost:8081/roomdata", {
{ method = "POST",
method = "POST", body = get_all_rooms_with_participants(),
body = get_all_rooms_with_participants() })
})
end end
--Helper function to wait till a component is loaded before running the given callback --Helper function to wait till a component is loaded before running the given callback
function run_when_component_loaded(component_host_name, callback) function run_when_component_loaded(component_host_name, callback)
local function trigger_callback() local function trigger_callback()
module:log('info', 'Component loaded %s', component_host_name); module:log("info", "Component loaded %s", component_host_name)
callback(module:context(component_host_name), component_host_name); callback(module:context(component_host_name), component_host_name)
end end
if prosody.hosts[component_host_name] == nil then 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); 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) prosody.events.add_handler("host-activated", function(host)
if host == component_host_name then if host == component_host_name then
trigger_callback(); trigger_callback()
end end
end); end)
else else
trigger_callback(); trigger_callback()
end end
end end
-- Helper function to wait till a component's muc module is loaded before running the given callback -- 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) function run_when_muc_module_loaded(component_host_module, component_host_name, callback)
local function trigger_callback() local function trigger_callback()
module:log('info', 'MUC module loaded for %s', component_host_name); module:log("info", "MUC module loaded for %s", component_host_name)
callback(prosody.hosts[component_host_name].modules.muc, component_host_module); callback(prosody.hosts[component_host_name].modules.muc, component_host_module)
end end
if prosody.hosts[component_host_name].modules.muc == nil then 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); 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) prosody.hosts[component_host_name].events.add_handler("module-loaded", function(event)
if (event.module == 'muc') then if event.module == "muc" then
trigger_callback(); trigger_callback()
end end
end); end)
else else
trigger_callback() trigger_callback()
end end
end end
local main_muc_component_host = muc_domain_prefix .. "." .. domain_name 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 -- Handle events on main muc module
run_when_component_loaded(main_muc_component_host, function(host_module, host_name) 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) 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 main_muc_service = main_muc -- so it can be accessed from breakout muc event handlers
-- the following must run after speakerstats (priority -1) -- 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-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-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-occupant-left", handle_room_event, -2) -- see also https://issues.prosody.im/1743
main_module:hook("muc-room-destroyed", handle_room_event, -2); main_module:hook("muc-room-destroyed", handle_room_event, -2)
end); end)
end); end)

1
prodsody/result Symbolic link
View file

@ -0,0 +1 @@
/nix/store/88ak9a0jwp38h95kb3hpd3965pmbhnjx-jitsi-room-prosody-1.0.0