Compare commits

..

No commits in common. "main" and "refactor/formatting" have entirely different histories.

62 changed files with 502 additions and 2710 deletions

View file

@ -18,7 +18,6 @@ trigger:
event:
include:
- tag
- push
volumes:
- name: drone-shared

View file

@ -3,8 +3,7 @@
## Development
`stack build`
`stack run`
`stack stack run`
## Debug
@ -13,44 +12,3 @@ Using websocat
`websocat ws://127.0.0.1:9160`
`curl --data 'body data' localhost:8081`
```
curl -X POST localhost:8081/roomdata \
-H "Content-Type: application/json" \
-d '[
{
"roomName": "ConferenceRoom1",
"participants": [
{
"jid": "participant1@example.com",
"email": "participant1@example.com",
"displayName": "Alice",
"avatarURL": "https://example.com/avatars/alice.png"
},
{
"jid": "participant2@example.com",
"email": "participant2@example.com",
"displayName": "Bob",
"avatarURL": "https://example.com/avatars/bob.png"
}
]
},
{
"roomName": "ConferenceRoom2",
"participants": [
{
"jid": "participant3@example.com",
"email": "participant3@example.com",
"displayName": "Charlie",
"avatarURL": "https://example.com/avatars/charlie.png"
},
{
"jid": "participant4@example.com",
"email": "participant4@example.com",
"displayName": "Dana",
"avatarURL": "https://example.com/avatars/dana.png"
}
]
}
]'
```

View file

@ -1,27 +1,7 @@
module Main (main) where
import ClassyPrelude
import GHC.IO.Encoding (setLocaleEncoding)
import GHC.IO.Encoding.UTF8 (utf8)
import Lib (runBothServers)
import Options.Applicative
import Types.Config (ServerOptions, serverOptionsParser)
main :: IO ()
main = do
setLocaleEncoding utf8
hSetBuffering stdout LineBuffering
hSetBuffering stderr LineBuffering
opts <- execParser serverOptions
runBothServers opts
serverOptions :: ParserInfo ServerOptions
serverOptions =
info
(serverOptionsParser <**> helper)
( fullDesc
<> progDesc "Run the server with specified options"
<> header "Haskell Server - A configurable server application"
)
main = runBothServers

View file

@ -1,20 +1,17 @@
{
mkDerivation,
aeson,
base,
bytestring,
classy-prelude,
http-types,
lib,
lifted-base,
mtl,
text,
uuid,
wai,
wai-extra,
warp,
websockets,
optparse-applicative,
{ mkDerivation
, aeson
, base
, bytestring
, classy-prelude
, http-types
, lib
, lifted-base
, mtl
, text
, uuid
, wai
, warp
, websockets
}:
mkDerivation {
pname = "jitsi-rooms";
@ -33,10 +30,8 @@ mkDerivation {
text
uuid
wai
wai-extra
warp
websockets
optparse-applicative
];
executableHaskellDepends = [
aeson
@ -49,10 +44,8 @@ mkDerivation {
text
uuid
wai
wai-extra
warp
websockets
optparse-applicative
];
homepage = "https://github.com/githubuser/jitsi-rooms#readme";
license = lib.licenses.bsd3;

View file

@ -1,4 +1,34 @@
{ pkgs ? import <nixpkgs> { } }:
let
config = {
packageOverrides = pkgs: rec {
haskellPackages = pkgs.haskellPackages.override {
overrides = haskellPackagesNew: haskellPackagesOld: rec {
jitsi-rooms =
haskellPackagesNew.callPackage ./converted.nix { };
};
};
};
};
pkgs = import <nixpkgs> { inherit config; };
pkgs.haskellPackages.callPackage ./converted.nix { }
in
pkgs.dockerTools.buildImage {
name = "jitsi-rooms";
tag = "latest";
copyToRoot = pkgs.buildEnv {
name = "image-root";
paths = [
# pkgs.bash
# pkgs.coreutils
];
pathsToLink = [ "/bin" ];
};
config = {
Cmd = [ "${pkgs.haskellPackages.jitsi-rooms}/bin/jitsi-rooms-exe" ];
ExposedPorts = {
"9160/tcp" = { };
"8081/tcp" = { };
};
};
}

View file

@ -1,37 +0,0 @@
let
config = {
packageOverrides = pkgs: rec {
haskellPackages = pkgs.haskellPackages.override {
overrides = haskellPackagesNew: haskellPackagesOld: rec {
jitsi-rooms =
haskellPackagesNew.callPackage ./converted.nix { };
};
};
};
};
pkgs = import <nixpkgs> { inherit config; };
in
pkgs.dockerTools.buildImage {
name = "jitsi-rooms";
tag = "latest";
copyToRoot = pkgs.buildEnv {
name = "image-root";
paths = [
# pkgs.bash
# pkgs.coreutils
];
pathsToLink = [ "/bin" ];
};
config = {
Cmd = [ "${pkgs.haskellPackages.jitsi-rooms}/bin/jitsi-rooms-exe" ];
ExposedPorts = {
"9160/tcp" = { };
"8081/tcp" = { };
};
Env = [
"LANG=en_US.UTF-8"
];
};
}

View file

@ -1,6 +1,6 @@
cabal-version: 1.12
-- This file has been generated from package.yaml by hpack version 0.37.0.
-- This file has been generated from package.yaml by hpack version 0.35.0.
--
-- see: https://github.com/sol/hpack
@ -29,14 +29,12 @@ library
Lib
RoomDataHandler
State.ConnectedClientsState
State.GenericTVarState
State.RoomDataState
State.RoomsState
Types.AppTypes
Types.Config
Types.ConnectionState
Types.Participant
Types.RoomData
Types.RoomsState
Types.User
Types.UsersData
Types.WebEnv
@ -63,13 +61,10 @@ library
, http-types
, lifted-base
, mtl
, optparse-applicative
, process
, text
, time
, uuid
, wai
, wai-extra
, warp
, websockets
default-language: Haskell2010
@ -92,13 +87,10 @@ executable jitsi-rooms-exe
, jitsi-rooms
, lifted-base
, mtl
, optparse-applicative
, process
, text
, time
, uuid
, wai
, wai-extra
, warp
, websockets
default-language: Haskell2010

View file

@ -1,14 +0,0 @@
{
"nixpkgs": {
"branch": "nixpkgs-unstable",
"description": "Nix Packages collection",
"homepage": "https://github.com/NixOS/nixpkgs",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "abfd31179174133ab8131139d650297bf4da63b7",
"sha256": "1jmkz6l7sj876wzyn5niyfaxshbmw9fp3g8r41k1wbjvmm5xrnsn",
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/abfd31179174133ab8131139d650297bf4da63b7.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
}
}

View file

@ -1,194 +0,0 @@
# This file has been generated by Niv.
let
#
# The fetchers. fetch_<type> fetches specs of type <type>.
#
fetch_file = pkgs: name: spec:
let
name' = sanitizeName name + "-src";
in
if spec.builtin or true then
builtins_fetchurl { inherit (spec) url sha256; name = name'; }
else
pkgs.fetchurl { inherit (spec) url sha256; name = name'; };
fetch_tarball = pkgs: name: spec:
let
name' = sanitizeName name + "-src";
in
if spec.builtin or true then
builtins_fetchTarball { name = name'; inherit (spec) url sha256; }
else
pkgs.fetchzip { name = name'; inherit (spec) url sha256; };
fetch_git = name: spec:
let
ref =
if spec ? ref then spec.ref else
if spec ? branch then "refs/heads/${spec.branch}" else
if spec ? tag then "refs/tags/${spec.tag}" else
abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!";
submodules = if spec ? submodules then spec.submodules else false;
submoduleArg =
let
nixSupportsSubmodules = builtins.compareVersions builtins.nixVersion "2.4" >= 0;
emptyArgWithWarning =
if submodules == true
then
builtins.trace
(
"The niv input \"${name}\" uses submodules "
+ "but your nix's (${builtins.nixVersion}) builtins.fetchGit "
+ "does not support them"
)
{}
else {};
in
if nixSupportsSubmodules
then { inherit submodules; }
else emptyArgWithWarning;
in
builtins.fetchGit
({ url = spec.repo; inherit (spec) rev; inherit ref; } // submoduleArg);
fetch_local = spec: spec.path;
fetch_builtin-tarball = name: throw
''[${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`.
$ niv modify ${name} -a type=tarball -a builtin=true'';
fetch_builtin-url = name: throw
''[${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`.
$ niv modify ${name} -a type=file -a builtin=true'';
#
# Various helpers
#
# https://github.com/NixOS/nixpkgs/pull/83241/files#diff-c6f540a4f3bfa4b0e8b6bafd4cd54e8bR695
sanitizeName = name:
(
concatMapStrings (s: if builtins.isList s then "-" else s)
(
builtins.split "[^[:alnum:]+._?=-]+"
((x: builtins.elemAt (builtins.match "\\.*(.*)" x) 0) name)
)
);
# The set of packages used when specs are fetched using non-builtins.
mkPkgs = sources: system:
let
sourcesNixpkgs =
import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; };
hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath;
hasThisAsNixpkgsPath = <nixpkgs> == ./.;
in
if builtins.hasAttr "nixpkgs" sources
then sourcesNixpkgs
else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then
import <nixpkgs> {}
else
abort
''
Please specify either <nixpkgs> (through -I or NIX_PATH=nixpkgs=...) or
add a package called "nixpkgs" to your sources.json.
'';
# The actual fetching function.
fetch = pkgs: name: spec:
if ! builtins.hasAttr "type" spec then
abort "ERROR: niv spec ${name} does not have a 'type' attribute"
else if spec.type == "file" then fetch_file pkgs name spec
else if spec.type == "tarball" then fetch_tarball pkgs name spec
else if spec.type == "git" then fetch_git name spec
else if spec.type == "local" then fetch_local spec
else if spec.type == "builtin-tarball" then fetch_builtin-tarball name
else if spec.type == "builtin-url" then fetch_builtin-url name
else
abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}";
# If the environment variable NIV_OVERRIDE_${name} is set, then use
# the path directly as opposed to the fetched source.
replace = name: drv:
let
saneName = stringAsChars (c: if isNull (builtins.match "[a-zA-Z0-9]" c) then "_" else c) name;
ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}";
in
if ersatz == "" then drv else
# this turns the string into an actual Nix path (for both absolute and
# relative paths)
if builtins.substring 0 1 ersatz == "/" then /. + ersatz else /. + builtins.getEnv "PWD" + "/${ersatz}";
# Ports of functions for older nix versions
# a Nix version of mapAttrs if the built-in doesn't exist
mapAttrs = builtins.mapAttrs or (
f: set: with builtins;
listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set))
);
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295
range = first: last: if first > last then [] else builtins.genList (n: first + n) (last - first + 1);
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257
stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1));
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
concatMapStrings = f: list: concatStrings (map f list);
concatStrings = builtins.concatStringsSep "";
# https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331
optionalAttrs = cond: as: if cond then as else {};
# fetchTarball version that is compatible between all the versions of Nix
builtins_fetchTarball = { url, name ? null, sha256 }@attrs:
let
inherit (builtins) lessThan nixVersion fetchTarball;
in
if lessThan nixVersion "1.12" then
fetchTarball ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; }))
else
fetchTarball attrs;
# fetchurl version that is compatible between all the versions of Nix
builtins_fetchurl = { url, name ? null, sha256 }@attrs:
let
inherit (builtins) lessThan nixVersion fetchurl;
in
if lessThan nixVersion "1.12" then
fetchurl ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; }))
else
fetchurl attrs;
# Create the final "sources" from the config
mkSources = config:
mapAttrs (
name: spec:
if builtins.hasAttr "outPath" spec
then abort
"The values in sources.json should not have an 'outPath' attribute"
else
spec // { outPath = replace name (fetch config.pkgs name spec); }
) config.sources;
# The "config" used by the fetchers
mkConfig =
{ sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null
, sources ? if isNull sourcesFile then {} else builtins.fromJSON (builtins.readFile sourcesFile)
, system ? builtins.currentSystem
, pkgs ? mkPkgs sources system
}: rec {
# The sources, i.e. the attribute set of spec name to spec
inherit sources;
# The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers
inherit pkgs;
};
in
mkSources (mkConfig {}) // { __functor = _: settings: mkSources (mkConfig settings); }

View file

@ -1,16 +0,0 @@
let
sources = import ./sources.nix;
pkgs = import sources.nixpkgs { };
in
# See https://docs.haskellstack.org/en/stable/nix_integration/#using-a-custom-shellnix-file
{ ghc }:
pkgs.haskell.lib.buildStackProject {
inherit ghc;
name = "haskell-stack-nix";
# System dependencies needed at compilation time
buildInputs = [
pkgs.zlib
];
}

View file

@ -33,9 +33,6 @@ dependencies:
- lifted-base
- mtl
- time
- wai-extra
- process
- optparse-applicative
ghc-options:
- -Wall

View file

@ -1,12 +0,0 @@
{ pkgs ? import <nixpkgs> { } }:
pkgs.mkShell {
buildInputs = with pkgs; [
haskell-language-server
stack
stylish-haskell
libnotify
];
}

View file

@ -8,9 +8,9 @@ where
import ClassyPrelude
import Data.Aeson (encode)
import Network.WebSockets qualified as WS
import State.ConnectedClientsState (ConnectedClients, MonadConnectedClientsRead (getConnctedClients))
import State.ConnectedClientsState (MonadConnectedClientsRead (getConnctedClients))
import State.RoomDataState (MonadRoomDataStateRead (getRoomDataState))
import Types.ConnectionState (Client (..))
import Types.ConnectionState (Client (..), ConnectedClients)
import Types.User (User, clientToUser)
import Types.UsersData (UsersData (..))
@ -41,10 +41,10 @@ broadCastToClientsGeneric ::
Text ->
m ()
broadCastToClientsGeneric message = do
connectedClients <- getConnctedClients
liftIO (broadcast message connectedClients)
state <- getConnctedClients
liftIO $ broadcast message state
broadcast :: Text -> ConnectedClients -> IO ()
broadcast message clients = do
putStrLn $ "Broadcasting: " ++ message
putStrLn message
forM_ clients $ \client -> WS.sendTextData (conn client) message

View file

@ -6,15 +6,14 @@ module Lib
where
import ClassyPrelude
import State.ConnectedClientsState (initConnectionsState)
import State.RoomsState (initRoomsState)
import Types.AppTypes
import Types.Config (ServerOptions)
import Types.ConnectionState (initConnectionsState)
import Types.RoomsState (initRoomsState)
import WebServer (runWebServer)
import WebSocket.Server (runWebSocketServer)
runBothServers :: ServerOptions -> IO ()
runBothServers serverOptions = do
runBothServers :: IO ()
runBothServers = do
connectedClientsState <- initConnectionsState
roomsState <- initRoomsState
@ -22,8 +21,7 @@ runBothServers serverOptions = do
Env
{ connectedClientsState = connectedClientsState,
profile = Dev,
roomsState = roomsState,
config = serverOptions
roomsState = roomsState
}
_ <- concurrently (unApp runWebSocketServer env) (unApp runWebServer env)

View file

@ -10,26 +10,23 @@ import ClassyPrelude
import Control.Monad.Except (MonadError, throwError)
import Data.Aeson (eitherDecodeStrict)
import Data.Aeson.Types (FromJSON)
import GHC.IO.Exception (ExitCode (ExitSuccess))
import Network.HTTP.Types (status200, status500)
import Network.Wai
( ResponseReceived,
consumeRequestBodyStrict,
responseLBS,
)
import State.ConnectedClientsState (MonadConnectedClientsRead)
import State.RoomDataState
( MonadRoomDataStateModify (setRoomDataState),
MonadRoomDataStateRead,
)
import State.RoomsState
( roomStateDiffInOpenRooms,
import Types.AppTypes (HasConnectedClientState)
import Types.RoomsState
( HasRoomsState,
roomStateDiffers,
updateRoomState,
)
import System.Process
import Text.Printf (printf)
import Types.AppTypes (HasConfig (getConfig))
import Types.Config (ServerOptions (..))
import Types.RoomData (RoomData, prettyPrintOpenedRoom)
import Types.WebEnv
( HasWebEnv (getRequest),
getRespond,
@ -42,19 +39,15 @@ roomDataHandler ::
MonadError ResponseReceived m,
MonadRoomDataStateRead m,
MonadRoomDataStateModify m,
MonadBroadcast m,
HasConfig env
MonadBroadcast m
) =>
m ResponseReceived
roomDataHandler = do
newRoomData <- parseBodyOrBadRequest
liftIO $ putStrLn "Got triggered from prosody"
(openedRooms, closedRooms) <- setRoomDataState newRoomData
mapM_ notifyRoomOpend openedRooms
mapM_ notifyRoomClosed closedRooms
broadcastUserData
liftIO $ putStrLn "Got triggered form prosody"
whenM (roomStateDiffers newRoomData) $ do
setRoomDataState newRoomData
broadcastUserData
success
parseBodyOrBadRequest ::
@ -66,7 +59,6 @@ parseBodyOrBadRequest ::
) =>
m a
parseBodyOrBadRequest = do
liftIO $ putStrLn "Parsing body"
body <- getRequestBody
case eitherDecodeStrict body of
Left errorMessage -> do
@ -115,22 +107,3 @@ success = do
status200
[("Content-Type", "text/plain")]
""
notifyRoomOpend :: (MonadIO m, HasConfig a0, MonadReader a0 m) => RoomData -> m ()
notifyRoomOpend room = do
config' <- getConfig <$> ask
let ServerOptions {notifyExecutable = notifyExecutable'} = config'
let (name, user) = prettyPrintOpenedRoom room
liftIO $ printf "Room %s opened by %s\n" name user
let command = printf "%s open '%s' '%s'" notifyExecutable' name user
exitCode <- liftIO $ system command
when (exitCode /= ExitSuccess) $ liftIO $ printf "Failed to notify room %s opened by %s running command %s\n" name user command
notifyRoomClosed :: (MonadIO m, HasConfig a0, MonadReader a0 m) => RoomData -> m ()
notifyRoomClosed room = do
config' <- getConfig <$> ask
let ServerOptions {notifyExecutable = notifyExecutable'} = config'
let (name, _) = prettyPrintOpenedRoom room
liftIO $ printf "Room %s closed\n" name
exitCode <- liftIO $ system $ printf "%s closed '%s'" notifyExecutable' name
when (exitCode /= ExitSuccess) $ liftIO $ printf "Failed to notify room %s closed\n" name

View file

@ -1,10 +1,6 @@
module State.ConnectedClientsState
( MonadConnectedClientsModify (..),
MonadConnectedClientsRead (..),
ConnectedClients,
ConnectedClientsState,
HasConnectedClientState (..),
initConnectionsState,
addWSClientGeneric,
updateWSClientGeneric,
removeWSClientGeneric,
@ -14,24 +10,14 @@ where
import ClassyPrelude
import Data.UUID
import Types.AppTypes
import Types.ConnectionState
type ConnectedClientsState = TVar ConnectedClients
initConnectionsState :: IO ConnectedClientsState
initConnectionsState = newTVarIO newConnectedClients
newConnectedClients :: ConnectedClients
newConnectedClients = []
class Monad m => MonadConnectedClientsModify m where
addWSClient :: Client -> m ()
removeWSClient :: UUID -> m ()
updateWSClient :: UUID -> (Client -> Client) -> m ()
class HasConnectedClientState a where
getConnectedClientState :: a -> ConnectedClientsState
addWSClientGeneric ::
( HasConnectedClientState env,
MonadReader env m,
@ -86,7 +72,9 @@ modifyState ::
m ()
modifyState modifyFunc = do
state <- getConnectedClientState <$> ask
atomically $ modifyTVar state modifyFunc
modifyMVar_ state $ \s ->
let s' = modifyFunc s
in return s'
class Monad m => MonadConnectedClientsRead m where
getConnctedClients :: m ConnectedClients
@ -98,4 +86,4 @@ getConnctedClientsGeneric ::
) =>
m ConnectedClients
getConnctedClientsGeneric = do
ask >>= readTVarIO . getConnectedClientState
ask >>= readMVar . getConnectedClientState

View file

@ -1,17 +0,0 @@
module State.GenericTVarState (GenericTVarState, updateGenericTVarState, updateGenericTVarStateWithQuery, getGenericTVarState) where
import ClassyPrelude
type GenericTVarState a = TVar a
updateGenericTVarState :: (MonadIO m) => GenericTVarState a -> a -> m ()
updateGenericTVarState tv a = atomically $ writeTVar tv a
updateGenericTVarStateWithQuery :: (MonadIO m) => GenericTVarState a -> (a -> a -> b) -> a -> m b
updateGenericTVarStateWithQuery tv f a = atomically $ do
b <- readTVar tv
writeTVar tv a
return $ f b a
getGenericTVarState :: (MonadIO m) => GenericTVarState a -> m a
getGenericTVarState = readTVarIO

View file

@ -7,8 +7,8 @@ where
import ClassyPrelude
import Types.RoomData
class (Monad m) => MonadRoomDataStateModify m where
setRoomDataState :: RoomsData -> m RoomsStateDiff
class Monad m => MonadRoomDataStateModify m where
setRoomDataState :: RoomsData -> m ()
class (Monad m) => MonadRoomDataStateRead m where
class Monad m => MonadRoomDataStateRead m where
getRoomDataState :: m RoomsData

View file

@ -1,88 +0,0 @@
module State.RoomsState
( RoomsState,
initRoomsState,
HasRoomsState (..),
roomStateDiffers,
RoomsStateDiff,
roomStateDiffInOpenRooms,
updateRoomState,
getRoomState,
)
where
import ClassyPrelude
import State.GenericTVarState
import State.RoomDataState (MonadRoomDataStateRead (getRoomDataState))
import Types.RoomData (RoomsData, RoomsStateDiff, roomNotEmpty, sameName)
type RoomsState = GenericTVarState RoomsData
initRoomsState :: IO RoomsState
initRoomsState = newTVarIO []
class HasRoomsState a where
getRoomsState :: a -> RoomsState
updateRoomState ::
( HasRoomsState env,
MonadIO m,
MonadReader env m,
MonadRoomDataStateRead m
) =>
RoomsData ->
m RoomsStateDiff
updateRoomState newData = do
state <- getRoomsState <$> ask
current <- getRoomDataState
if not $ eqIgnoreOrdering newData current
then
( do
liftIO $ putStrLn "Upating room state"
diff <- updateGenericTVarStateWithQuery state roomStateDiff newData
liftIO $ putStrLn "Done Upating room state"
return diff
)
else return ([], [])
getRoomState ::
( HasRoomsState env,
MonadIO m,
MonadReader env m
) =>
m RoomsData
getRoomState = do
state <- getRoomsState <$> ask
getGenericTVarState state
roomStateDiffers ::
( MonadRoomDataStateRead m
) =>
RoomsData ->
m Bool
roomStateDiffers newData = do
not . eqIgnoreOrdering newData <$> getRoomDataState
roomStateDiffInOpenRooms ::
( MonadRoomDataStateRead m,
MonadIO m
) =>
RoomsData ->
m (RoomsData, RoomsData)
roomStateDiffInOpenRooms newData = do
current <- getRoomDataState
liftIO $ putStrLn $ pack $ "Current rooms: " ++ show current
liftIO $ putStrLn $ pack $ "New rooms: " ++ show newData
let newRooms = filter roomNotEmpty $ filter (\newRoom -> isNothing $ find (sameName newRoom) (filter roomNotEmpty current)) newData
let oldRooms = filter (\oldRoom -> isNothing $ find (sameName oldRoom) newData) current
return (newRooms, oldRooms)
roomStateDiff :: RoomsData -> RoomsData -> RoomsStateDiff
roomStateDiff newData current = (newRooms, oldRooms)
where
newRooms = filter roomNotEmpty $ filter (\newRoom -> isNothing $ find (sameName newRoom) (filter roomNotEmpty current)) newData
oldRooms = filter (\oldRoom -> isNothing $ find (sameName oldRoom) newData) current
eqIgnoreOrdering :: (Eq a) => [a] -> [a] -> Bool
eqIgnoreOrdering a b = length a == length b && all (`elem` b) a

View file

@ -1,36 +1,31 @@
{-# LANGUAGE DerivingVia #-}
module Types.AppTypes (Env (..), App (..), HasConfig (getConfig), getConnectedClientState, AppProfile (Prod, Dev)) where
module Types.AppTypes (Env (..), App (..), getConnectedClientState, HasConnectedClientState, AppProfile (Prod, Dev)) where
import ClassyPrelude
import State.ConnectedClientsState
import State.RoomsState
import Types.ConnectionState (ConnectedClientsState)
import Types.RoomsState
( HasRoomsState (getRoomsState),
RoomsState,
)
import Types.Config (ServerOptions)
data AppProfile = Prod | Dev
data Env = Env
{ connectedClientsState :: ConnectedClientsState,
roomsState :: RoomsState,
profile :: AppProfile,
config :: ServerOptions
profile :: AppProfile
}
class HasConnectedClientState a where
getConnectedClientState :: a -> ConnectedClientsState
instance HasConnectedClientState Env where
getConnectedClientState = connectedClientsState
instance HasRoomsState Env where
getRoomsState = roomsState
class HasConfig a where
getConfig :: a -> ServerOptions
instance HasConfig Env where
getConfig = config
newtype App env a = App {unApp :: env -> IO a}
deriving
( Functor,

View file

@ -1,61 +0,0 @@
module Types.Config
( ServerOptions (..),
serverOptionsParser,
)
where
import ClassyPrelude
( Applicative ((<*>)),
Int,
Semigroup ((<>)),
Show,
String,
(<$>),
)
import Options.Applicative (Parser, auto, help, long, metavar, option, short, showDefault, strOption, value)
data ServerOptions = ServerOptions
{ port :: Int, -- Main server port
websocketPort :: Int, -- WebSocket server port
listenAddress :: String, -- Address to bind the server
notifyExecutable :: String -- Path to the notify executable
}
deriving (Show)
serverOptionsParser :: Parser ServerOptions
serverOptionsParser =
ServerOptions
<$> option
auto
( long "port"
<> short 'p'
<> metavar "PORT"
<> help "Port number for the main server (default: 8081)"
<> value 8081
<> showDefault
)
<*> option
auto
( long "websocketPort"
<> short 'w'
<> metavar "WS_PORT"
<> help "Port number for the WebSocket server (default: 9160)"
<> value 9160
<> showDefault
)
<*> strOption
( long "listenAddress"
<> short 'l'
<> metavar "ADDRESS"
<> help "IP address or hostname to bind the server (default: 127.0.0.1)"
<> value "127.0.0.1"
<> showDefault
)
<*> strOption
( long "notifyExecutable"
<> short 'n'
<> metavar "EXECUTABLE"
<> help "Path to the notify executable (default: /usr/bin/notify)"
<> value "/usr/bin/notify"
<> showDefault
)

View file

@ -1,6 +1,8 @@
module Types.ConnectionState
( Client (..),
ConnectedClientsState,
ConnectedClients,
initConnectionsState,
)
where
@ -15,4 +17,12 @@ data Client = Client
joinedRoom :: Bool
}
type ConnectedClientsState = MVar ConnectedClients
type ConnectedClients = [Client]
initConnectionsState :: IO ConnectedClientsState
initConnectionsState = newMVar newConnectedClients
newConnectedClients :: ConnectedClients
newConnectedClients = []

View file

@ -1,6 +1,6 @@
{-# LANGUAGE DeriveGeneric #-}
module Types.Participant (Participant (Participant, displayName)) where
module Types.Participant (Participant) where
import ClassyPrelude
import Data.Aeson (FromJSON, ToJSON)

View file

@ -1,10 +1,10 @@
{-# LANGUAGE DeriveGeneric #-}
module Types.RoomData (RoomData, RoomsStateDiff, RoomsData, sameName, roomNotEmpty, prettyPrintOpenedRoom) where
module Types.RoomData (RoomData, RoomsData) where
import ClassyPrelude
import Data.Aeson (FromJSON, ToJSON)
import Types.Participant (Participant (Participant, displayName))
import Types.Participant (Participant)
data RoomData = RoomData
{ roomName :: RoomName,
@ -12,22 +12,10 @@ data RoomData = RoomData
}
deriving (Generic, Show, Eq)
sameName :: RoomData -> RoomData -> Bool
sameName RoomData {roomName = name1} RoomData {roomName = name2} = name1 == name2
roomNotEmpty :: RoomData -> Bool
roomNotEmpty RoomData {participants = participants} = not $ null participants
prettyPrintOpenedRoom :: RoomData -> (Text, Text)
prettyPrintOpenedRoom RoomData {roomName = roomName, participants = participants} =
(roomName, fromMaybe "" (headMay (map (\Participant {displayName = displayName} -> displayName) participants)))
type RoomName = Text
type RoomsData = [RoomData]
type RoomsStateDiff = (RoomsData, RoomsData)
instance ToJSON RoomData
instance FromJSON RoomData

View file

@ -0,0 +1,54 @@
module Types.RoomsState
( RoomsState,
initRoomsState,
HasRoomsState (..),
roomStateDiffers,
updateRoomState,
getRoomState,
)
where
import ClassyPrelude
import State.RoomDataState (MonadRoomDataStateRead (getRoomDataState))
import Types.RoomData (RoomsData)
type RoomsState = MVar RoomsData
initRoomsState :: IO RoomsState
initRoomsState = newMVar []
class HasRoomsState a where
getRoomsState :: a -> RoomsState
updateRoomState ::
( HasRoomsState env,
MonadIO m,
MonadReader env m
) =>
RoomsData ->
m ()
updateRoomState newData = do
state <- getRoomsState <$> ask
_ <- swapMVar state newData
return ()
getRoomState ::
( HasRoomsState env,
MonadIO m,
MonadReader env m
) =>
m RoomsData
getRoomState = do
state <- getRoomsState <$> ask
readMVar state
roomStateDiffers ::
( MonadRoomDataStateRead m
) =>
RoomsData ->
m Bool
roomStateDiffers newData = do
not . eqIgnoreOrdering newData <$> getRoomDataState
eqIgnoreOrdering :: (Eq a) => [a] -> [a] -> Bool
eqIgnoreOrdering a b = length a == length b && all (`elem` b) a

View file

@ -6,12 +6,11 @@ where
import ClassyPrelude
import Network.Wai (Request, Response, ResponseReceived)
import State.ConnectedClientsState (HasConnectedClientState (getConnectedClientState))
import State.RoomsState (HasRoomsState (getRoomsState))
import Types.AppTypes
( Env (..),
HasConfig (getConfig),
HasConnectedClientState (getConnectedClientState),
)
import Types.RoomsState (HasRoomsState (getRoomsState))
class HasWebEnv a where
getRequest :: a -> Request
@ -32,6 +31,3 @@ instance HasRoomsState WebEnv where
instance HasWebEnv WebEnv where
getRequest = request
getRespond = respond
instance HasConfig WebEnv where
getConfig = getConfig . appEnv

View file

@ -1,6 +1,5 @@
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE GADTs #-}
module Types.WebSocketMessages.WebSocketMessages
( WebSocketMessage (..),
@ -30,22 +29,23 @@ data WebSocketMessage = ClientInfoMessage SetClientInfo | JoinRoomMessage JoinRo
instance FromJSON WebSocketMessage where
parseJSON = genericParseJSON defaultOptions {sumEncoding = UntaggedValue}
data SetClientInfo where
SetClientInfo :: {displayName :: Text} -> SetClientInfo
data SetClientInfo = SetClientInfo
{ displayName :: Text
}
deriving (Generic, Show)
instance FromJSON SetClientInfo
data JoinRoom where
JoinRoom :: {roomName :: Text} -> JoinRoom
data JoinRoom = JoinRoom
{ roomName :: Text
}
deriving (Generic, Show)
instance FromJSON JoinRoom
data AllChatMessageIncoming where
AllChatMessageIncoming ::
{content :: Text} ->
AllChatMessageIncoming
data AllChatMessageIncoming = AllChatMessageIncoming
{ content :: Text
}
deriving (Generic, Show)
instance FromJSON AllChatMessageIncoming

View file

@ -15,7 +15,6 @@ import Control.Monad.Except
import Network.HTTP.Types
import Network.Wai
import Network.Wai.Handler.Warp (run)
import Network.Wai.Middleware.RequestLogger (logStdout)
import RoomDataHandler (roomDataHandler)
import State.ConnectedClientsState
( MonadConnectedClientsRead (..),
@ -25,13 +24,13 @@ import State.RoomDataState
( MonadRoomDataStateModify (..),
MonadRoomDataStateRead (getRoomDataState),
)
import State.RoomsState
( getRoomState,
import Types.AppTypes (Env (..))
import Types.RoomsState
( HasRoomsState (getRoomsState),
getRoomState,
roomStateDiffers,
updateRoomState,
)
import Text.Printf (printf)
import Types.AppTypes (Env (..), HasConfig (getConfig))
import Types.Config (ServerOptions (..))
import Types.WebEnv
newtype ExceptTApp e a = E {unExceptTApp :: IO (Either e a)}
@ -84,8 +83,7 @@ app ::
MonadError ResponseReceived m,
MonadRoomDataStateModify m,
MonadRoomDataStateRead m,
MonadBroadcast m,
HasConfig env
MonadBroadcast m
) =>
m ResponseReceived
app = requestPathHandler
@ -97,8 +95,7 @@ requestPathHandler ::
MonadError ResponseReceived m,
MonadRoomDataStateModify m,
MonadRoomDataStateRead m,
MonadBroadcast m,
HasConfig env
MonadBroadcast m
) =>
m ResponseReceived
requestPathHandler = do
@ -126,9 +123,7 @@ notFound = do
runWebApp ::
( MonadIO m,
MonadReader Env m,
HasConfig Env,
HasConfig WebEnv
MonadReader Env m
) =>
m Application
runWebApp = do
@ -147,14 +142,10 @@ runWebApp = do
runWebServer ::
( MonadIO m,
MonadReader Env m,
HasConfig Env,
HasConfig WebEnv
MonadReader Env m
) =>
m ()
runWebServer = do
config' <- getConfig <$> ask
let ServerOptions {port = webPort, listenAddress = address} = config'
putStrLn $ pack $ printf "Webserver up and running at http://%s:%d/" address webPort
runWebApp >>= liftIO . (run webPort . logStdout)
putStrLn "http://localhost:8081/"
runWebApp >>= liftIO . run 8081
return ()

View file

@ -1,5 +1,4 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MonoLocalBinds #-}
module WebSocket.Server
( runWebSocketServer,
@ -10,27 +9,22 @@ where
import ClassyPrelude
import Data.UUID.V4 (nextRandom)
import Network.WebSockets qualified as WS
import Text.Printf
import Types.AppTypes (Env, HasConfig (getConfig))
import Types.Config (ServerOptions (..))
import Types.AppTypes
import WebSocket.WSApp (WSApp (..), WSEnv (..), wsApp)
runWebSocketServer ::
( MonadIO m,
Types.AppTypes.HasConfig Types.AppTypes.Env,
MonadReader Types.AppTypes.Env m
MonadReader Env m
) =>
m ()
runWebSocketServer = do
config' <- getConfig <$> ask
let ServerOptions {websocketPort = wsPort, listenAddress = address} = config'
putStrLn $ pack $ printf "WebSocket server up and running at ws://%s:%d/" address wsPort
putStrLn "Websocket up at 0.0.0.0:9160"
wsApp' <- runWSApp
liftIO $ WS.runServer address wsPort wsApp'
liftIO $ WS.runServer "0.0.0.0" 9160 wsApp'
runWSApp ::
( MonadIO m,
MonadReader Types.AppTypes.Env m
MonadReader Env m
) =>
m WS.ServerApp
runWSApp = do

View file

@ -33,11 +33,11 @@ wsApp = do
broadcastUserData
withCleanUp $ forever $ do
handleWSAction
broadcastUserData
handleWSAction ::
( MonadWebSocketSession m,
MonadConnectedClientsModify m,
MonadRoomDataStateRead m,
MonadBroadcast m,
MonadAllChat m
) =>
@ -47,10 +47,8 @@ handleWSAction = do
case msg of
JoinRoomMessage _ -> do
joinRoom
broadcastUserData
ClientInfoMessage clientInfo -> do
updateClientName clientInfo
broadcastUserData
AllChatMessageIncomingMessage incomingMessage -> do
broadCastAllChatMessage incomingMessage

View file

@ -17,8 +17,7 @@ import ClassyPrelude
import Data.UUID
import Network.WebSockets qualified as WS
import State.ConnectedClientsState
( HasConnectedClientState,
MonadConnectedClientsModify (..),
( MonadConnectedClientsModify (..),
MonadConnectedClientsRead (..),
addWSClientGeneric,
getConnctedClientsGeneric,
@ -26,8 +25,8 @@ import State.ConnectedClientsState
updateWSClientGeneric,
)
import State.RoomDataState
import State.RoomsState (HasRoomsState (..), getRoomState)
import Types.AppTypes
import Types.RoomsState (HasRoomsState (..), getRoomState)
data WSEnv = WSEnv
{ appEnv :: Env,

View file

@ -14,10 +14,9 @@
# Use the latest resolver that uses the same ghc version
# as build for nixos
# this way we can use prebuild binaries for hls
# go to
# https://www.stackage.org/
# and select the lts that is matching you hls version
resolver: lts-22.43
#resolver: nightly-2022-11-12
#resolver: ghc-9.2.4
resolver: lts-20.16
#
# The location of a snapshot can be provided as a file or url. Stack assumes
# a snapshot provided as a file might change, whereas a url resource does not.
@ -74,3 +73,5 @@ packages:
# compiler-check: newer-minor
ghc-options:
"$everything": -haddock

View file

@ -6,7 +6,7 @@
packages: []
snapshots:
- completed:
sha256: 08bd13ce621b41a8f5e51456b38d5b46d7783ce114a50ab604d6bbab0d002146
size: 720271
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/22/43.yaml
original: lts-22.43
sha256: dad15e2ec0c09280a5c2e07190fb18710fc54472f029f34f861f686540824d81
size: 649592
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/16.yaml
original: lts-20.16

View file

@ -1,34 +0,0 @@
{
pkgs ? import <nixpkgs> { },
}:
pkgs.buildNpmPackage {
pname = "jitsi-rooms-backendd";
version = "1.0.0";
# Source files (usually the current directory)
src = pkgs.lib.cleanSource ./.;
# Optionally, you can provide a package-lock.json or yarn.lock file
# This ensures dependencies are installed reproducibly.
packageLock = ./package-lock.json; # Use this for npm
# Node.js version (optional, defaults to pkgs.nodejs)
nodejs = pkgs.nodejs-18_x;
npmDepsHash = "sha256-n9SpPPRvu92RwNPDKZ3f1Splbux2IVGhSazJ4DM2IrA=";
# Add any additional arguments for the build process
buildInputs = [ ];
# Specify the build phase, if needed
buildPhase = ''
echo "Building the app..."
npm run build
'';
# Specify the install phase (what to copy to the output)
installPhase = ''
mkdir -p $out
cp -r dist/* $out/
'';
}

View file

@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>FF Jitsi Rooms</title>
</head>

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 548 B

1
frontend/public/vite.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -1 +0,0 @@
/nix/store/x3fqdrf05dcg52s7x6dkzql551g0bfxw-my-node-app-1.0.0

View file

@ -20,7 +20,6 @@ function App() {
conferenceData={conferenceData}
setConferenceData={setConferenceData}
userInfo={userInfo}
usersData={roomData}
/>
</div>
)

View file

@ -1,9 +1,9 @@
const ISPROD = window.location.protocol == 'https:'
const JITSI_DOMAIN = 'meet.filefighter.de'
const JITSI_DOMAIN = 'thisisnotajitsi.filefighter.de'
const USE_REMOTE_BACKEND = true
const getWebsocketUrl = () => {
if (ISPROD) return 'wss://' + window.location.host + '/ws'
if (USE_REMOTE_BACKEND) return 'wss://' + 'treffen.filefighter.de/ws'
if (USE_REMOTE_BACKEND) return 'wss://' + 'discord.filefighter.de/ws'
return 'ws://' + 'localhost:9160/ws'
}
const WEBSOCKET_URL = getWebsocketUrl()

View file

@ -27,7 +27,7 @@ function JitsiEntrypoint({ roomName, userInfo, conferenceData, setConferenceData
domain={JITSI_DOMAIN}
roomName={roomName}
configOverwrite={{
startWithAudioMuted: false,
startWithAudioMuted: true,
disableModeratorIndicator: true,
startScreenSharing: true,
enableEmailInStats: false,

View file

@ -1,7 +0,0 @@
.meeting-quickjoin {
text-align: center
}
.meeting {
flex-grow: 3;
}

View file

@ -1,27 +1,19 @@
import { ConferenceData } from '../../background/jitsi/eventListeners'
import { UsersData } from '../../background/types/roomData'
import useMeetingStarted from '../../hooks/useMeetingStarted'
import { useRoomName } from '../../hooks/useRoomName'
import JitsiEntrypoint from '../jitsi/JitsiEntrypoint'
import { UserInfo } from '../jitsi/types'
import MeetingNameInput from './MeetingNameInput'
import './Meeting.css'
import React from 'react'
interface Props {
conferenceData: ConferenceData | undefined
setConferenceData: (newData: ConferenceData) => void
userInfo: UserInfo
usersData: UsersData
}
function Meeting(props: Props) {
const { conferenceData, setConferenceData, userInfo, usersData } = props
console.log("[Rooms] meeting usersData", props)
function Meeting({ conferenceData, setConferenceData, userInfo }: Props) {
const [meetingStarted] = useMeetingStarted()
const { roomName } = useRoomName()
const [_, setMeetingStarted] = useMeetingStarted()
const { updateAndSubmitRoomName: updateAndSubmitRoomName } = useRoomName()
if (meetingStarted) {
return (
@ -34,32 +26,7 @@ function Meeting(props: Props) {
)
}
return (
<div className="meeting">
<MeetingNameInput roomName={roomName} currentUser={userInfo.displayName} />
<div className="meeting-quickjoin">
{usersData?.roomsData.map((roomData) => {
return (
<React.Fragment key={roomData.roomName}>
<h3>
<a
href="#"
onClick={() => {
updateAndSubmitRoomName(roomData.roomName)
setMeetingStarted(true)
}}
>
{decodeURI(roomData.roomName)}
</a>
</h3>
{roomData.participants.map((participant) => (
<div key={participant.jid}> {participant.displayName} </div>
))}
</React.Fragment>
)
})}
</div>
</div>)
return <MeetingNameInput roomName={roomName} currentUser={userInfo.displayName} />
}
export default Meeting

View file

@ -8,7 +8,7 @@ function MeetingNameInput(props: { roomName: string; currentUser: string }) {
const [_, setMeetingStarted] = useMeetingStarted()
const onInput: React.ChangeEventHandler<HTMLInputElement> = (event) => {
updateRoomName(encodeURI(event.target.value))
updateRoomName(event.target.value)
event.preventDefault()
}
@ -23,7 +23,7 @@ function MeetingNameInput(props: { roomName: string; currentUser: string }) {
<div className="meeting-name-input">
<h1>Greetings {props.currentUser}</h1>
<form onSubmit={onSubmit}>
<input placeholder="Roomname" type="text" value={decodeURI(roomName)} onChange={onInput} />
<input placeholder="Roomname" type="text" value={roomName} onChange={onInput} />
<button type="submit">Enter the adventure</button>
</form>
</div>

View file

@ -1,5 +1,4 @@
.sidebar {
resize: horizontal;
display: flex;
flex-direction: column;
white-space: nowrap;

View file

@ -5,7 +5,6 @@ import { UsersData } from '../../background/types/roomData'
import useMeetingStarted from '../../hooks/useMeetingStarted'
import { useRoomName } from '../../hooks/useRoomName'
import Chat from '../chat/Chat'
import React from 'react'
interface Props {
usersData: UsersData
@ -23,7 +22,7 @@ function Sidebar(props: Props) {
<div className="sidebar-body">
{props.usersData.roomsData.map((roomData) => {
return (
<React.Fragment key={roomData.roomName}>
<>
<h3>
<a
href="#"
@ -32,18 +31,18 @@ function Sidebar(props: Props) {
setMeetingStarted(true)
}}
>
{decodeURI(roomData.roomName)}
{roomData.roomName}
</a>
</h3>
{roomData.participants.map((participant) => (
<div key={participant.jid}> {participant.displayName} </div>
))}
</React.Fragment>
</>
)
})}
</div>
<div>
<h3>No room</h3>
<h3> No room</h3>
{props.usersData.usersWithOutRoom.map((user) => (
<div key={user.uuid}>{user.name}</div>
))}

View file

@ -20,7 +20,7 @@ function useSidebarVisibility() {
return { sidebarVisibility, toggleSidebarVisibility, sidebarToggleText }
}
const getSidebarToggleText = (sidebarVisibility: sidebarVisibilityOptions) => {
if (sidebarVisibility === 'full') return '<--'
if (sidebarVisibility === 'full') return '<-'
if (sidebarVisibility === 'small') return '<-'
if (sidebarVisibility === 'hidden') return '->'
}

View file

@ -7,23 +7,20 @@ import useWebSocketConnection from './useWebSocketConnection'
function useBackendData(userInfo: UserInfo) {
console.log('[Rooms] useBackendData')
const { onMessage, cleanUpOnMessage, sendMessage, disconnect } = useWebSocketConnection(userInfo)
const { onMessage, sendMessage, disconnect } = useWebSocketConnection(userInfo)
const { roomData, setRoomData } = useRoomData()
const { addChatMessage } = useAllChat()
useEffect(() => {
console.log('[Rooms] add onMessage Listener')
const messageCallback = (messageString: string) => {
onMessage((messageString) => {
console.log('[Rooms] message from ws', messageString)
const messageObject = JSON.parse(messageString)
!!messageObject.roomsData && setRoomData(messageObject)
!!messageObject.content && addChatMessage(messageObject)
return disconnect
}
onMessage(messageCallback)
return () => cleanUpOnMessage(messageCallback)
}, [])
})
}, [onMessage, setRoomData, disconnect])
return { roomData, sendMessage }
}

View file

@ -2,15 +2,7 @@ import { useState } from 'react'
import { UsersData } from '../background/types/roomData'
function useRoomData() {
const [roomData, setRoomDataInternal] = useState<UsersData>()
const setRoomData = (usersData: UsersData) => {
usersData.roomsData = usersData.roomsData.map((roomData) => {
roomData.roomName = decodeURI(roomData.roomName)
return roomData
})
setRoomDataInternal(usersData)
}
const [roomData, setRoomData] = useState<UsersData>()
return { roomData, setRoomData }
}

View file

@ -1,5 +1,5 @@
import { atom, useAtom } from 'jotai'
import { useCallback } from 'react'
import { useCallback, useState } from 'react'
const roomNameAtom = atom(getRoomNameFromUrl())
@ -46,7 +46,7 @@ function setRoomNameInUrl(roomName: string) {
function setRoomNameInTitle(roomName: string) {
if (!!roomName) {
document.title = decodeURI(roomName)
document.title = roomName
}
}

View file

@ -1,19 +1,19 @@
import { atom, useAtom } from 'jotai'
import { useCallback, useEffect } from 'react'
import { useCallback, useState } from 'react'
import { WEBSOCKET_URL } from '../background/constants'
import { UserInfo } from '../components/jitsi/types'
const createWebSocketConnection = (userInfo: UserInfo) => {
const webSocket = new WebSocket(WEBSOCKET_URL)
console.log('[Rooms] createWebSocketConnection')
webSocket.addEventListener('open', (_: Event) => webSocket.send(JSON.stringify(userInfo)))
const webSocket = new WebSocket(WEBSOCKET_URL)
const webSocketConnectionAtom = atom(webSocket)
return webSocket
}
function useWebSocketConnection(userInfo: UserInfo) {
console.log('[Rooms] useWebSocketConnection')
const [webSocketConnection] = useAtom(webSocketConnectionAtom)
useEffect(() => {
sendMessageNowOrLater(webSocketConnection, JSON.stringify(userInfo))
}, [webSocketConnection, userInfo]);
const [webSocketConnection] = useState<WebSocket>(() => createWebSocketConnection(userInfo))
const sendMessage = useCallback(
(message: string) => {
@ -37,19 +37,8 @@ function useWebSocketConnection(userInfo: UserInfo) {
)
const disconnect = useCallback(webSocketConnection.close, [webSocketConnection])
const cleanUpOnMessage = useCallback((callbackToRemove: any) => {
console.log('[Rooms] cleanUpOnMessage')
webSocketConnection.removeEventListener('message', callbackToRemove)
}, [webSocketConnection])
return { onMessage, cleanUpOnMessage, sendMessage, disconnect }
}
const sendMessageNowOrLater = (webSocket: WebSocket, message: string) => {
if (webSocket.readyState !== WebSocket.OPEN) {
return webSocket.addEventListener('open', (_: Event) => webSocket.send(message))
}
webSocket.send(message)
return { onMessage, sendMessage, disconnect }
}
export default useWebSocketConnection

View file

@ -51,9 +51,7 @@ button {
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
color: white;
}
button:hover {
border-color: #646cff;
}
@ -62,6 +60,19 @@ button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}
#root {
height: 100%;
width: 100%;
@ -81,5 +92,4 @@ input {
background-color: #2b2a33;
border-color: #646cff;
border-radius: 8px;
color: white;
}

View file

@ -22,7 +22,7 @@
resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.10.tgz"
integrity sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==
"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.20.7":
"@babel/core@^7.20.7":
version "7.20.12"
resolved "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz"
integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==
@ -209,17 +209,122 @@
"@babel/helper-validator-identifier" "^7.19.1"
to-fast-properties "^2.0.0"
"@esbuild/android-arm64@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.16.15.tgz#d58b9efe279b553b377395318d21e360058b3622"
integrity sha512-OdbkUv7468dSsgoFtHIwTaYAuI5lDEv/v+dlfGBUbVa2xSDIIuSOHXawynw5N9+5lygo/JdXa5/sgGjiEU18gQ==
"@esbuild/android-arm@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.16.15.tgz#6a8ad3016fd9c89bb419bd21605fba242c051809"
integrity sha512-JsJtmadyWcR+DEtHLixM7bAQsfi1s0Xotv9kVOoXbCLyhKPOHvMEyh3kJBuTbCPSE4c2jQkQVmarwc9Mg9k3bA==
"@esbuild/android-x64@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.16.15.tgz#94b0589b6bec5eaf7e8ea2fe6368427899676f21"
integrity sha512-dPUOBiNNWAm+/bxoA75o7R7qqqfcEzXaYlb5uJk2xGHmUMNKSAnDCtRYLgx9/wfE4sXyn8H948OrDyUAHhPOuA==
"@esbuild/darwin-arm64@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.16.15.tgz#525e5603a82837a1e7c8265d3b14433aa869e9b6"
integrity sha512-AksarYV85Hxgwh5/zb6qGl4sYWxIXPQGBAZ+jUro1ZpINy3EWumK+/4DPOKUBPnsrOIvnNXy7Rq4mTeCsMQDNA==
"@esbuild/darwin-x64@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.16.15.tgz#38ec324a3653ade5acc5c190a7a27185caa6223e"
integrity sha512-qqrKJxoohceZGGP+sZ5yXkzW9ZiyFZJ1gWSEfuYdOWzBSL18Uy3w7s/IvnDYHo++/cxwqM0ch3HQVReSZy7/4Q==
"@esbuild/freebsd-arm64@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.15.tgz#05c19bf6e4e56387f6a56bd6933839e889146726"
integrity sha512-LBWaep6RvJm5KnsKkocdVEzuwnGMjz54fcRVZ9d3R7FSEWOtPBxMhuxeA1n98JVbCLMkTPFmKN6xSnfhnM9WXQ==
"@esbuild/freebsd-x64@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.16.15.tgz#68855666ecf1616e2a927154148d4409cd8bc55b"
integrity sha512-LE8mKC6JPR04kPLRP9A6k7ZmG0k2aWF4ru79Sde6UeWCo7yDby5f48uJNFQ2pZqzUUkLrHL8xNdIHerJeZjHXg==
"@esbuild/linux-arm64@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.16.15.tgz#93e8630d19f3a25d31c6467e2b136438391a3ca9"
integrity sha512-mRYpuQGbzY+XLczy3Sk7fMJ3DRKLGDIuvLKkkUkyecDGQMmil6K/xVKP9IpKO7JtNH477qAiMjjX7jfKae8t4g==
"@esbuild/linux-arm@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.16.15.tgz#9343c9d0e18d15ca5b4e293154e4beae2598b5db"
integrity sha512-+1sGlqtMJTOnJUXwLUGnDhPaGRKqxT0UONtYacS+EjdDOrSgpQ/1gUXlnze45Z/BogwYaswQM19Gu1YD1T19/w==
"@esbuild/linux-ia32@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.16.15.tgz#2c63615bb87cb2d080f3dc7dd0e3174b9b977233"
integrity sha512-puXVFvY4m8EB6/fzu3LdgjiNnEZ3gZMSR7NmKoQe51l3hyQalvTjab3Dt7aX4qGf+8Pj7dsCOBNzNzkSlr/4Aw==
"@esbuild/linux-loong64@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.16.15.tgz#85a245672709ce60895baf93b1ad3fb4b3cdab4f"
integrity sha512-ATMGb3eg8T6ZTGZFldlGeFEcevBiVq6SBHvRAO04HMfUjZWneZ/U+JJb3YzlNZxuscJ4Tmzq+JrYxlk7ro4dRg==
"@esbuild/linux-mips64el@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.16.15.tgz#41e325ccd6432f952c674d763d9e5acc25a00267"
integrity sha512-3SEA4L82OnoSATW+Ve8rPgLaKjC8WMt8fnx7De9kvi/NcVbkj8W+J7qnu/tK2P9pUPQP7Au/0sjPEqZtFeyKQQ==
"@esbuild/linux-ppc64@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.16.15.tgz#51c7ed8fec6f9860716cdb1bb86835bdb9206108"
integrity sha512-8PgbeX+N6vmqeySzyxO0NyDOltCEW13OS5jUHTvCHmCgf4kNXZtAWJ+zEfJxjRGYhVezQ1FdIm7WfN1R27uOyg==
"@esbuild/linux-riscv64@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.16.15.tgz#44cb5ad5318d72f52378fa666b2010551c67f333"
integrity sha512-U+coqH+89vbPVoU30no1Fllrn6gvEeO5tfEArBhjYZ+dQ3Gv7ciQXYf5nrT1QdlIFwEjH4Is1U1iiaGWW+tGpQ==
"@esbuild/linux-s390x@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.16.15.tgz#69572a26c2120ddd446d8207d30ae8f94a801a72"
integrity sha512-M0nKLFMdyFGBoitxG42kq6Xap0CPeDC6gfF9lg7ZejzGF6kqYUGT+pQGl2QCQoxJBeat/LzTma1hG8C3dq2ocg==
"@esbuild/linux-x64@0.16.15":
version "0.16.15"
resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.15.tgz"
integrity sha512-t7/fOXBUKfigvhJLGKZ9TPHHgqNgpIpYaAbcXQk1X+fPeUG7x0tpAbXJ2wST9F/gJ02+CLETPMnhG7Tra2wqsQ==
"@esbuild/netbsd-x64@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.16.15.tgz#cba5674608c197bee9d25451ae458ab76a770a45"
integrity sha512-0k0Nxi6DOJmTnLtKD/0rlyqOPpcqONXY53vpkoAsue8CfyhNPWtwzba1ICFNCfCY1dqL3Ho/xEzujJhmdXq1rg==
"@esbuild/openbsd-x64@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.16.15.tgz#4ea4394d5b9c67bac6dcd1e527b47c64990d7d92"
integrity sha512-3SkckazfIbdSjsGpuIYT3d6n2Hx0tck3MS1yVsbahhWiLvdy4QozTpvlbjqO3GmvtvhxY4qdyhFOO2wiZKeTAQ==
"@esbuild/sunos-x64@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.16.15.tgz#91174f7058dfc6cfafdf2251330f6767506db7a7"
integrity sha512-8PNvBC+O8X5EnyIGqE8St2bOjjrXMR17NOLenIrzolvwWnJXvwPo0tE/ahOeiAJmTOS/eAcN8b4LAZcn17Uj7w==
"@esbuild/win32-arm64@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.16.15.tgz#3fa7189ec92d1de87563ab9e73b3e0a4adbfd203"
integrity sha512-YPaSgm/mm7kNcATB53OxVGVfn6rDNbImTn330ZlF3hKej1e9ktCaljGjn2vH08z2dlHEf3kdt57tNjE6zs8SzA==
"@esbuild/win32-ia32@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.16.15.tgz#a114e4878e74fa6b5453cb407be4f2a28b72809d"
integrity sha512-0movUXbSNrTeNf5ZXT0avklEvlJD0hNGZsrrXHfsp9z4tK5xC+apCqmUEZeE9mqrb84Z8XbgGr/MS9LqafTP2A==
"@esbuild/win32-x64@0.16.15":
version "0.16.15"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.16.15.tgz#e28277cdbc1c9cde2b982c814d05f44d4b1f0580"
integrity sha512-27h5GCcbfomVAqAnMJWvR1LqEY0dFqIq4vTe5nY3becnZNu0SX8F0+gTk3JPvgWQHzaGc6VkPzlOiMkdSUunUA==
"@fortawesome/fontawesome-common-types@6.4.0":
version "6.4.0"
resolved "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.0.tgz"
integrity sha512-HNii132xfomg5QVZw0HwXXpN22s7VBHQBv9CeOu9tfJnhsWQNd2lmTNi8CSrnw5B+5YOmzu1UoPAyxaXsJ6RgQ==
"@fortawesome/fontawesome-svg-core@^6.4.0", "@fortawesome/fontawesome-svg-core@~1 || ~6":
"@fortawesome/fontawesome-svg-core@^6.4.0":
version "6.4.0"
resolved "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.0.tgz"
integrity sha512-Bertv8xOiVELz5raB2FlXDPKt+m94MQ3JgDfsVbrqNpLU9+UE2E18GKjLKw+d3XbeYPqg1pzyQKGsrzbw+pPaw==
@ -272,7 +377,7 @@
resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz"
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13", "@jridgewell/sourcemap-codec@1.4.14":
"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13":
version "1.4.14"
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz"
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
@ -329,7 +434,7 @@ ansi-styles@^3.2.1:
dependencies:
color-convert "^1.9.0"
browserslist@^4.21.3, "browserslist@>= 4.21.0":
browserslist@^4.21.3:
version "4.21.4"
resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz"
integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==
@ -425,6 +530,11 @@ escape-string-regexp@^1.0.5:
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
fsevents@~2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
@ -553,7 +663,7 @@ prop-types@^15.8.1:
object-assign "^4.1.1"
react-is "^16.13.1"
react-dom@^18.2.0, "react-dom@16 || 17 || 18":
react-dom@^18.2.0:
version "18.2.0"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz"
integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
@ -571,7 +681,7 @@ react-refresh@^0.14.0:
resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz"
integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==
react@^18.2.0, react@>=16.3, react@>=17.0.0, "react@16 || 17 || 18":
react@^18.2.0:
version "18.2.0"
resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz"
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==

View file

@ -1,68 +0,0 @@
{
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 { });
notifyScript = "/run/current-system/sw/bin/send-signal-jitsi-notify";
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 -n ${notifyScript}";
DynamicUser = true;
User = "jitsi-rooms-backend";
};
};
users.users.jitsi-rooms-backend = {
isSystemUser = true;
group = "jitsi-rooms-backend";
extraGroups = [ "dbus" ];
};
users.groups.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" ];
};
};
}

View file

@ -10,7 +10,7 @@
### Get started
- run `./run_dev.sh`
- open [https://localhost:8443/](https://localhost:8443/) (accept the risk about self-signed certs)
- open [https://localhost:8443/](https://localhost:8443/)
- make changes to mod_jitsi_rooms.lua and save the file
- prosody will be restarted
- join a room in jitsi
@ -19,8 +19,3 @@
## Deploying
- all required changes to the [jitsi meet docker setup](https://github.com/jitsi/docker-jitsi-meet) are marked with a "custom" comment
## ?
Maybe we could just use https://github.com/jitsi-contrib/prosody-plugins/tree/dd1cb9098f5fbd281f2b62c1e2cf30e220ff14b0/event_sync
=> This does not include the whole participants list

View file

@ -1,23 +0,0 @@
{
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

@ -347,7 +347,7 @@ services:
meet.jitsi:
jitsi-rooms:
image: jitsi-rooms
image: jitsi-rooms:67y5d9y2zbi7wkqm2jpcjj1k2614qwx2
restart: ${RESTART_POLICY:-unless-stopped}
ports:
- '9160:9160'

0
prodsody/fix-permissions.sh Executable file → Normal file
View file

View file

@ -4,7 +4,6 @@ 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;
@ -13,9 +12,9 @@ local domain_name = "meet.jitsi"
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 room = get_room_from_jid(room_address);
local occupants_json = array();
if room then
local occupants = room._occupants;
@ -48,6 +47,7 @@ function get_participants_for_room(room_name)
end
function get_all_rooms_with_participants()
local sessions = prosody.full_sessions;
local someTable = (it.join(it.keys(sessions)));
@ -74,7 +74,7 @@ end
-- @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() };
return { status_code = 200; body = get_all_rooms_with_participants() };
end
function module.load()
@ -86,9 +86,9 @@ function module.load()
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,
["GET"] = function(event) return async_handler_wrapper(event, handle_get_sessions) end;
},
};
});
end
@ -125,6 +125,7 @@ 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
@ -146,11 +147,12 @@ function handle_room_event(event)
return;
end
async_http_request("http://jitsi-rooms:8081/roomdata",
async_http_request("http://192.168.2.116: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

View file

@ -1,94 +1,95 @@
---@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 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 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)
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 room = get_room_from_jid(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 someTable = (it.join(it.keys(sessions)))
local actual_rooms = someTable[1][2]
local sessions = prosody.full_sessions;
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 someTable = (it.join(it.keys(sessions)));
local actual_rooms = someTable[1][2]
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 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)
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
@ -102,104 +103,106 @@ 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 is_healthcheck_room(event.room.jid) then
return true
end
if event == nil or event.room == nil or event.room.jid == nil then
return true;
end
if event.occupant and jid.node(event.occupant.jid) == "focus" then
return true
end
if is_healthcheck_room(event.room.jid) then
return true;
end
return false
if event.occupant and jid.node(event.occupant.jid) == "focus" then
return true;
end
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://192.168.2.116: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);
main_module:hook("muc-room-destroyed", handle_room_event, -2);
end);
end);

View file

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