Compare commits
No commits in common. "main" and "v0.0.1-debug3" have entirely different histories.
main
...
v0.0.1-deb
|
@ -18,7 +18,6 @@ trigger:
|
||||||
event:
|
event:
|
||||||
include:
|
include:
|
||||||
- tag
|
- tag
|
||||||
- push
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- name: drone-shared
|
- name: drone-shared
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
`stack build`
|
`stack build`
|
||||||
|
`stack stack run`
|
||||||
`stack run`
|
|
||||||
|
|
||||||
## Debug
|
## Debug
|
||||||
|
|
||||||
|
@ -13,44 +12,3 @@ Using websocat
|
||||||
`websocat ws://127.0.0.1:9160`
|
`websocat ws://127.0.0.1:9160`
|
||||||
|
|
||||||
`curl --data 'body data' localhost:8081`
|
`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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]'
|
|
||||||
```
|
|
||||||
|
|
|
@ -1,27 +1,7 @@
|
||||||
module Main (main) where
|
module Main (main) where
|
||||||
|
|
||||||
import ClassyPrelude
|
import ClassyPrelude
|
||||||
import GHC.IO.Encoding (setLocaleEncoding)
|
|
||||||
import GHC.IO.Encoding.UTF8 (utf8)
|
|
||||||
import Lib (runBothServers)
|
import Lib (runBothServers)
|
||||||
import Options.Applicative
|
|
||||||
import Types.Config (ServerOptions, serverOptionsParser)
|
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = runBothServers
|
||||||
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"
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
{
|
{ mkDerivation
|
||||||
mkDerivation,
|
, aeson
|
||||||
aeson,
|
, base
|
||||||
base,
|
, bytestring
|
||||||
bytestring,
|
, classy-prelude
|
||||||
classy-prelude,
|
, http-types
|
||||||
http-types,
|
, lib
|
||||||
lib,
|
, lifted-base
|
||||||
lifted-base,
|
, mtl
|
||||||
mtl,
|
, text
|
||||||
text,
|
, uuid
|
||||||
uuid,
|
, wai
|
||||||
wai,
|
, wai-extra
|
||||||
wai-extra,
|
, warp
|
||||||
warp,
|
, websockets
|
||||||
websockets,
|
|
||||||
optparse-applicative,
|
|
||||||
}:
|
}:
|
||||||
mkDerivation {
|
mkDerivation {
|
||||||
pname = "jitsi-rooms";
|
pname = "jitsi-rooms";
|
||||||
|
@ -36,7 +34,6 @@ mkDerivation {
|
||||||
wai-extra
|
wai-extra
|
||||||
warp
|
warp
|
||||||
websockets
|
websockets
|
||||||
optparse-applicative
|
|
||||||
];
|
];
|
||||||
executableHaskellDepends = [
|
executableHaskellDepends = [
|
||||||
aeson
|
aeson
|
||||||
|
@ -52,7 +49,6 @@ mkDerivation {
|
||||||
wai-extra
|
wai-extra
|
||||||
warp
|
warp
|
||||||
websockets
|
websockets
|
||||||
optparse-applicative
|
|
||||||
];
|
];
|
||||||
homepage = "https://github.com/githubuser/jitsi-rooms#readme";
|
homepage = "https://github.com/githubuser/jitsi-rooms#readme";
|
||||||
license = lib.licenses.bsd3;
|
license = lib.licenses.bsd3;
|
||||||
|
|
|
@ -1,4 +1,43 @@
|
||||||
{ 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" = { };
|
||||||
|
};
|
||||||
|
Healthcheck = {
|
||||||
|
"Test" = [
|
||||||
|
"CMD-SHELL"
|
||||||
|
"${pkgs.curl} -f http://0.0.0.0:8081"
|
||||||
|
];
|
||||||
|
"Interval" = 30000000000;
|
||||||
|
"Timeout" = 10000000000;
|
||||||
|
"Retries" = 3;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -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"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
cabal-version: 1.12
|
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.2.
|
||||||
--
|
--
|
||||||
-- see: https://github.com/sol/hpack
|
-- see: https://github.com/sol/hpack
|
||||||
|
|
||||||
|
@ -29,14 +29,12 @@ library
|
||||||
Lib
|
Lib
|
||||||
RoomDataHandler
|
RoomDataHandler
|
||||||
State.ConnectedClientsState
|
State.ConnectedClientsState
|
||||||
State.GenericTVarState
|
|
||||||
State.RoomDataState
|
State.RoomDataState
|
||||||
State.RoomsState
|
|
||||||
Types.AppTypes
|
Types.AppTypes
|
||||||
Types.Config
|
|
||||||
Types.ConnectionState
|
Types.ConnectionState
|
||||||
Types.Participant
|
Types.Participant
|
||||||
Types.RoomData
|
Types.RoomData
|
||||||
|
Types.RoomsState
|
||||||
Types.User
|
Types.User
|
||||||
Types.UsersData
|
Types.UsersData
|
||||||
Types.WebEnv
|
Types.WebEnv
|
||||||
|
@ -63,8 +61,6 @@ library
|
||||||
, http-types
|
, http-types
|
||||||
, lifted-base
|
, lifted-base
|
||||||
, mtl
|
, mtl
|
||||||
, optparse-applicative
|
|
||||||
, process
|
|
||||||
, text
|
, text
|
||||||
, time
|
, time
|
||||||
, uuid
|
, uuid
|
||||||
|
@ -92,8 +88,6 @@ executable jitsi-rooms-exe
|
||||||
, jitsi-rooms
|
, jitsi-rooms
|
||||||
, lifted-base
|
, lifted-base
|
||||||
, mtl
|
, mtl
|
||||||
, optparse-applicative
|
|
||||||
, process
|
|
||||||
, text
|
, text
|
||||||
, time
|
, time
|
||||||
, uuid
|
, uuid
|
||||||
|
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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); }
|
|
|
@ -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
|
|
||||||
];
|
|
||||||
}
|
|
|
@ -34,8 +34,6 @@ dependencies:
|
||||||
- mtl
|
- mtl
|
||||||
- time
|
- time
|
||||||
- wai-extra
|
- wai-extra
|
||||||
- process
|
|
||||||
- optparse-applicative
|
|
||||||
|
|
||||||
ghc-options:
|
ghc-options:
|
||||||
- -Wall
|
- -Wall
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
{ pkgs ? import <nixpkgs> { } }:
|
|
||||||
|
|
||||||
pkgs.mkShell {
|
|
||||||
buildInputs = with pkgs; [
|
|
||||||
haskell-language-server
|
|
||||||
stack
|
|
||||||
stylish-haskell
|
|
||||||
|
|
||||||
libnotify
|
|
||||||
];
|
|
||||||
|
|
||||||
}
|
|
|
@ -8,9 +8,9 @@ where
|
||||||
import ClassyPrelude
|
import ClassyPrelude
|
||||||
import Data.Aeson (encode)
|
import Data.Aeson (encode)
|
||||||
import Network.WebSockets qualified as WS
|
import Network.WebSockets qualified as WS
|
||||||
import State.ConnectedClientsState (ConnectedClients, MonadConnectedClientsRead (getConnctedClients))
|
import State.ConnectedClientsState (MonadConnectedClientsRead (getConnctedClients))
|
||||||
import State.RoomDataState (MonadRoomDataStateRead (getRoomDataState))
|
import State.RoomDataState (MonadRoomDataStateRead (getRoomDataState))
|
||||||
import Types.ConnectionState (Client (..))
|
import Types.ConnectionState (Client (..), ConnectedClients)
|
||||||
import Types.User (User, clientToUser)
|
import Types.User (User, clientToUser)
|
||||||
import Types.UsersData (UsersData (..))
|
import Types.UsersData (UsersData (..))
|
||||||
|
|
||||||
|
@ -46,5 +46,5 @@ broadCastToClientsGeneric message = do
|
||||||
|
|
||||||
broadcast :: Text -> ConnectedClients -> IO ()
|
broadcast :: Text -> ConnectedClients -> IO ()
|
||||||
broadcast message clients = do
|
broadcast message clients = do
|
||||||
putStrLn $ "Broadcasting: " ++ message
|
putStrLn message
|
||||||
forM_ clients $ \client -> WS.sendTextData (conn client) message
|
forM_ clients $ \client -> WS.sendTextData (conn client) message
|
||||||
|
|
|
@ -6,15 +6,14 @@ module Lib
|
||||||
where
|
where
|
||||||
|
|
||||||
import ClassyPrelude
|
import ClassyPrelude
|
||||||
import State.ConnectedClientsState (initConnectionsState)
|
|
||||||
import State.RoomsState (initRoomsState)
|
|
||||||
import Types.AppTypes
|
import Types.AppTypes
|
||||||
import Types.Config (ServerOptions)
|
import Types.ConnectionState (initConnectionsState)
|
||||||
|
import Types.RoomsState (initRoomsState)
|
||||||
import WebServer (runWebServer)
|
import WebServer (runWebServer)
|
||||||
import WebSocket.Server (runWebSocketServer)
|
import WebSocket.Server (runWebSocketServer)
|
||||||
|
|
||||||
runBothServers :: ServerOptions -> IO ()
|
runBothServers :: IO ()
|
||||||
runBothServers serverOptions = do
|
runBothServers = do
|
||||||
connectedClientsState <- initConnectionsState
|
connectedClientsState <- initConnectionsState
|
||||||
roomsState <- initRoomsState
|
roomsState <- initRoomsState
|
||||||
|
|
||||||
|
@ -22,8 +21,7 @@ runBothServers serverOptions = do
|
||||||
Env
|
Env
|
||||||
{ connectedClientsState = connectedClientsState,
|
{ connectedClientsState = connectedClientsState,
|
||||||
profile = Dev,
|
profile = Dev,
|
||||||
roomsState = roomsState,
|
roomsState = roomsState
|
||||||
config = serverOptions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ <- concurrently (unApp runWebSocketServer env) (unApp runWebServer env)
|
_ <- concurrently (unApp runWebSocketServer env) (unApp runWebServer env)
|
||||||
|
|
|
@ -10,26 +10,23 @@ import ClassyPrelude
|
||||||
import Control.Monad.Except (MonadError, throwError)
|
import Control.Monad.Except (MonadError, throwError)
|
||||||
import Data.Aeson (eitherDecodeStrict)
|
import Data.Aeson (eitherDecodeStrict)
|
||||||
import Data.Aeson.Types (FromJSON)
|
import Data.Aeson.Types (FromJSON)
|
||||||
import GHC.IO.Exception (ExitCode (ExitSuccess))
|
|
||||||
import Network.HTTP.Types (status200, status500)
|
import Network.HTTP.Types (status200, status500)
|
||||||
import Network.Wai
|
import Network.Wai
|
||||||
( ResponseReceived,
|
( ResponseReceived,
|
||||||
consumeRequestBodyStrict,
|
consumeRequestBodyStrict,
|
||||||
responseLBS,
|
responseLBS,
|
||||||
)
|
)
|
||||||
|
import State.ConnectedClientsState (MonadConnectedClientsRead)
|
||||||
import State.RoomDataState
|
import State.RoomDataState
|
||||||
( MonadRoomDataStateModify (setRoomDataState),
|
( MonadRoomDataStateModify (setRoomDataState),
|
||||||
MonadRoomDataStateRead,
|
MonadRoomDataStateRead,
|
||||||
)
|
)
|
||||||
import State.RoomsState
|
import Types.AppTypes (HasConnectedClientState)
|
||||||
( roomStateDiffInOpenRooms,
|
import Types.RoomsState
|
||||||
|
( HasRoomsState,
|
||||||
roomStateDiffers,
|
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
|
import Types.WebEnv
|
||||||
( HasWebEnv (getRequest),
|
( HasWebEnv (getRequest),
|
||||||
getRespond,
|
getRespond,
|
||||||
|
@ -42,19 +39,15 @@ roomDataHandler ::
|
||||||
MonadError ResponseReceived m,
|
MonadError ResponseReceived m,
|
||||||
MonadRoomDataStateRead m,
|
MonadRoomDataStateRead m,
|
||||||
MonadRoomDataStateModify m,
|
MonadRoomDataStateModify m,
|
||||||
MonadBroadcast m,
|
MonadBroadcast m
|
||||||
HasConfig env
|
|
||||||
) =>
|
) =>
|
||||||
m ResponseReceived
|
m ResponseReceived
|
||||||
roomDataHandler = do
|
roomDataHandler = do
|
||||||
newRoomData <- parseBodyOrBadRequest
|
newRoomData <- parseBodyOrBadRequest
|
||||||
liftIO $ putStrLn "Got triggered from prosody"
|
liftIO $ putStrLn "Got triggered from prosody"
|
||||||
(openedRooms, closedRooms) <- setRoomDataState newRoomData
|
whenM (roomStateDiffers newRoomData) $ do
|
||||||
|
setRoomDataState newRoomData
|
||||||
mapM_ notifyRoomOpend openedRooms
|
broadcastUserData
|
||||||
mapM_ notifyRoomClosed closedRooms
|
|
||||||
|
|
||||||
broadcastUserData
|
|
||||||
success
|
success
|
||||||
|
|
||||||
parseBodyOrBadRequest ::
|
parseBodyOrBadRequest ::
|
||||||
|
@ -66,7 +59,6 @@ parseBodyOrBadRequest ::
|
||||||
) =>
|
) =>
|
||||||
m a
|
m a
|
||||||
parseBodyOrBadRequest = do
|
parseBodyOrBadRequest = do
|
||||||
liftIO $ putStrLn "Parsing body"
|
|
||||||
body <- getRequestBody
|
body <- getRequestBody
|
||||||
case eitherDecodeStrict body of
|
case eitherDecodeStrict body of
|
||||||
Left errorMessage -> do
|
Left errorMessage -> do
|
||||||
|
@ -115,22 +107,3 @@ success = do
|
||||||
status200
|
status200
|
||||||
[("Content-Type", "text/plain")]
|
[("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
|
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
module State.ConnectedClientsState
|
module State.ConnectedClientsState
|
||||||
( MonadConnectedClientsModify (..),
|
( MonadConnectedClientsModify (..),
|
||||||
MonadConnectedClientsRead (..),
|
MonadConnectedClientsRead (..),
|
||||||
ConnectedClients,
|
|
||||||
ConnectedClientsState,
|
|
||||||
HasConnectedClientState (..),
|
|
||||||
initConnectionsState,
|
|
||||||
addWSClientGeneric,
|
addWSClientGeneric,
|
||||||
updateWSClientGeneric,
|
updateWSClientGeneric,
|
||||||
removeWSClientGeneric,
|
removeWSClientGeneric,
|
||||||
|
@ -14,24 +10,14 @@ where
|
||||||
|
|
||||||
import ClassyPrelude
|
import ClassyPrelude
|
||||||
import Data.UUID
|
import Data.UUID
|
||||||
|
import Types.AppTypes
|
||||||
import Types.ConnectionState
|
import Types.ConnectionState
|
||||||
|
|
||||||
type ConnectedClientsState = TVar ConnectedClients
|
|
||||||
|
|
||||||
initConnectionsState :: IO ConnectedClientsState
|
|
||||||
initConnectionsState = newTVarIO newConnectedClients
|
|
||||||
|
|
||||||
newConnectedClients :: ConnectedClients
|
|
||||||
newConnectedClients = []
|
|
||||||
|
|
||||||
class Monad m => MonadConnectedClientsModify m where
|
class Monad m => MonadConnectedClientsModify m where
|
||||||
addWSClient :: Client -> m ()
|
addWSClient :: Client -> m ()
|
||||||
removeWSClient :: UUID -> m ()
|
removeWSClient :: UUID -> m ()
|
||||||
updateWSClient :: UUID -> (Client -> Client) -> m ()
|
updateWSClient :: UUID -> (Client -> Client) -> m ()
|
||||||
|
|
||||||
class HasConnectedClientState a where
|
|
||||||
getConnectedClientState :: a -> ConnectedClientsState
|
|
||||||
|
|
||||||
addWSClientGeneric ::
|
addWSClientGeneric ::
|
||||||
( HasConnectedClientState env,
|
( HasConnectedClientState env,
|
||||||
MonadReader env m,
|
MonadReader env m,
|
||||||
|
|
|
@ -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
|
|
|
@ -7,8 +7,8 @@ where
|
||||||
import ClassyPrelude
|
import ClassyPrelude
|
||||||
import Types.RoomData
|
import Types.RoomData
|
||||||
|
|
||||||
class (Monad m) => MonadRoomDataStateModify m where
|
class Monad m => MonadRoomDataStateModify m where
|
||||||
setRoomDataState :: RoomsData -> m RoomsStateDiff
|
setRoomDataState :: RoomsData -> m ()
|
||||||
|
|
||||||
class (Monad m) => MonadRoomDataStateRead m where
|
class Monad m => MonadRoomDataStateRead m where
|
||||||
getRoomDataState :: m RoomsData
|
getRoomDataState :: m RoomsData
|
||||||
|
|
|
@ -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
|
|
|
@ -1,36 +1,31 @@
|
||||||
{-# LANGUAGE DerivingVia #-}
|
{-# 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 ClassyPrelude
|
||||||
import State.ConnectedClientsState
|
import Types.ConnectionState (ConnectedClientsState)
|
||||||
import State.RoomsState
|
import Types.RoomsState
|
||||||
( HasRoomsState (getRoomsState),
|
( HasRoomsState (getRoomsState),
|
||||||
RoomsState,
|
RoomsState,
|
||||||
)
|
)
|
||||||
import Types.Config (ServerOptions)
|
|
||||||
|
|
||||||
data AppProfile = Prod | Dev
|
data AppProfile = Prod | Dev
|
||||||
|
|
||||||
data Env = Env
|
data Env = Env
|
||||||
{ connectedClientsState :: ConnectedClientsState,
|
{ connectedClientsState :: ConnectedClientsState,
|
||||||
roomsState :: RoomsState,
|
roomsState :: RoomsState,
|
||||||
profile :: AppProfile,
|
profile :: AppProfile
|
||||||
config :: ServerOptions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class HasConnectedClientState a where
|
||||||
|
getConnectedClientState :: a -> ConnectedClientsState
|
||||||
|
|
||||||
instance HasConnectedClientState Env where
|
instance HasConnectedClientState Env where
|
||||||
getConnectedClientState = connectedClientsState
|
getConnectedClientState = connectedClientsState
|
||||||
|
|
||||||
instance HasRoomsState Env where
|
instance HasRoomsState Env where
|
||||||
getRoomsState = roomsState
|
getRoomsState = roomsState
|
||||||
|
|
||||||
class HasConfig a where
|
|
||||||
getConfig :: a -> ServerOptions
|
|
||||||
|
|
||||||
instance HasConfig Env where
|
|
||||||
getConfig = config
|
|
||||||
|
|
||||||
newtype App env a = App {unApp :: env -> IO a}
|
newtype App env a = App {unApp :: env -> IO a}
|
||||||
deriving
|
deriving
|
||||||
( Functor,
|
( Functor,
|
||||||
|
|
|
@ -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
|
|
||||||
)
|
|
|
@ -1,6 +1,8 @@
|
||||||
module Types.ConnectionState
|
module Types.ConnectionState
|
||||||
( Client (..),
|
( Client (..),
|
||||||
|
ConnectedClientsState,
|
||||||
ConnectedClients,
|
ConnectedClients,
|
||||||
|
initConnectionsState,
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -15,4 +17,12 @@ data Client = Client
|
||||||
joinedRoom :: Bool
|
joinedRoom :: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ConnectedClientsState = TVar ConnectedClients
|
||||||
|
|
||||||
type ConnectedClients = [Client]
|
type ConnectedClients = [Client]
|
||||||
|
|
||||||
|
initConnectionsState :: IO ConnectedClientsState
|
||||||
|
initConnectionsState = newTVarIO newConnectedClients
|
||||||
|
|
||||||
|
newConnectedClients :: ConnectedClients
|
||||||
|
newConnectedClients = []
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{-# LANGUAGE DeriveGeneric #-}
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
|
||||||
module Types.Participant (Participant (Participant, displayName)) where
|
module Types.Participant (Participant) where
|
||||||
|
|
||||||
import ClassyPrelude
|
import ClassyPrelude
|
||||||
import Data.Aeson (FromJSON, ToJSON)
|
import Data.Aeson (FromJSON, ToJSON)
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
{-# LANGUAGE DeriveGeneric #-}
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
|
||||||
module Types.RoomData (RoomData, RoomsStateDiff, RoomsData, sameName, roomNotEmpty, prettyPrintOpenedRoom) where
|
module Types.RoomData (RoomData, RoomsData) where
|
||||||
|
|
||||||
import ClassyPrelude
|
import ClassyPrelude
|
||||||
import Data.Aeson (FromJSON, ToJSON)
|
import Data.Aeson (FromJSON, ToJSON)
|
||||||
import Types.Participant (Participant (Participant, displayName))
|
import Types.Participant (Participant)
|
||||||
|
|
||||||
data RoomData = RoomData
|
data RoomData = RoomData
|
||||||
{ roomName :: RoomName,
|
{ roomName :: RoomName,
|
||||||
|
@ -12,22 +12,10 @@ data RoomData = RoomData
|
||||||
}
|
}
|
||||||
deriving (Generic, Show, Eq)
|
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 RoomName = Text
|
||||||
|
|
||||||
type RoomsData = [RoomData]
|
type RoomsData = [RoomData]
|
||||||
|
|
||||||
type RoomsStateDiff = (RoomsData, RoomsData)
|
|
||||||
|
|
||||||
instance ToJSON RoomData
|
instance ToJSON RoomData
|
||||||
|
|
||||||
instance FromJSON RoomData
|
instance FromJSON RoomData
|
||||||
|
|
55
backend/src/Types/RoomsState.hs
Normal file
55
backend/src/Types/RoomsState.hs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
module Types.RoomsState
|
||||||
|
( RoomsState,
|
||||||
|
initRoomsState,
|
||||||
|
HasRoomsState (..),
|
||||||
|
roomStateDiffers,
|
||||||
|
updateRoomState,
|
||||||
|
getRoomState,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import ClassyPrelude
|
||||||
|
import State.RoomDataState (MonadRoomDataStateRead (getRoomDataState))
|
||||||
|
import Types.RoomData (RoomsData)
|
||||||
|
|
||||||
|
type RoomsState = TVar RoomsData
|
||||||
|
|
||||||
|
initRoomsState :: IO RoomsState
|
||||||
|
initRoomsState = newTVarIO []
|
||||||
|
|
||||||
|
class HasRoomsState a where
|
||||||
|
getRoomsState :: a -> RoomsState
|
||||||
|
|
||||||
|
updateRoomState ::
|
||||||
|
( HasRoomsState env,
|
||||||
|
MonadIO m,
|
||||||
|
MonadReader env m
|
||||||
|
) =>
|
||||||
|
RoomsData ->
|
||||||
|
m ()
|
||||||
|
updateRoomState newData = do
|
||||||
|
state <- getRoomsState <$> ask
|
||||||
|
liftIO $ putStrLn "Upating room state"
|
||||||
|
atomically $ writeTVar state newData
|
||||||
|
liftIO $ putStrLn "Done Upating room state"
|
||||||
|
|
||||||
|
getRoomState ::
|
||||||
|
( HasRoomsState env,
|
||||||
|
MonadIO m,
|
||||||
|
MonadReader env m
|
||||||
|
) =>
|
||||||
|
m RoomsData
|
||||||
|
getRoomState = do
|
||||||
|
state <- getRoomsState <$> ask
|
||||||
|
readTVarIO 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
|
|
@ -6,12 +6,11 @@ where
|
||||||
|
|
||||||
import ClassyPrelude
|
import ClassyPrelude
|
||||||
import Network.Wai (Request, Response, ResponseReceived)
|
import Network.Wai (Request, Response, ResponseReceived)
|
||||||
import State.ConnectedClientsState (HasConnectedClientState (getConnectedClientState))
|
|
||||||
import State.RoomsState (HasRoomsState (getRoomsState))
|
|
||||||
import Types.AppTypes
|
import Types.AppTypes
|
||||||
( Env (..),
|
( Env (..),
|
||||||
HasConfig (getConfig),
|
HasConnectedClientState (getConnectedClientState),
|
||||||
)
|
)
|
||||||
|
import Types.RoomsState (HasRoomsState (getRoomsState))
|
||||||
|
|
||||||
class HasWebEnv a where
|
class HasWebEnv a where
|
||||||
getRequest :: a -> Request
|
getRequest :: a -> Request
|
||||||
|
@ -32,6 +31,3 @@ instance HasRoomsState WebEnv where
|
||||||
instance HasWebEnv WebEnv where
|
instance HasWebEnv WebEnv where
|
||||||
getRequest = request
|
getRequest = request
|
||||||
getRespond = respond
|
getRespond = respond
|
||||||
|
|
||||||
instance HasConfig WebEnv where
|
|
||||||
getConfig = getConfig . appEnv
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
{-# LANGUAGE DeriveGeneric #-}
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
{-# LANGUAGE DuplicateRecordFields #-}
|
{-# LANGUAGE DuplicateRecordFields #-}
|
||||||
{-# LANGUAGE GADTs #-}
|
|
||||||
|
|
||||||
module Types.WebSocketMessages.WebSocketMessages
|
module Types.WebSocketMessages.WebSocketMessages
|
||||||
( WebSocketMessage (..),
|
( WebSocketMessage (..),
|
||||||
|
@ -30,22 +29,23 @@ data WebSocketMessage = ClientInfoMessage SetClientInfo | JoinRoomMessage JoinRo
|
||||||
instance FromJSON WebSocketMessage where
|
instance FromJSON WebSocketMessage where
|
||||||
parseJSON = genericParseJSON defaultOptions {sumEncoding = UntaggedValue}
|
parseJSON = genericParseJSON defaultOptions {sumEncoding = UntaggedValue}
|
||||||
|
|
||||||
data SetClientInfo where
|
data SetClientInfo = SetClientInfo
|
||||||
SetClientInfo :: {displayName :: Text} -> SetClientInfo
|
{ displayName :: Text
|
||||||
|
}
|
||||||
deriving (Generic, Show)
|
deriving (Generic, Show)
|
||||||
|
|
||||||
instance FromJSON SetClientInfo
|
instance FromJSON SetClientInfo
|
||||||
|
|
||||||
data JoinRoom where
|
data JoinRoom = JoinRoom
|
||||||
JoinRoom :: {roomName :: Text} -> JoinRoom
|
{ roomName :: Text
|
||||||
|
}
|
||||||
deriving (Generic, Show)
|
deriving (Generic, Show)
|
||||||
|
|
||||||
instance FromJSON JoinRoom
|
instance FromJSON JoinRoom
|
||||||
|
|
||||||
data AllChatMessageIncoming where
|
data AllChatMessageIncoming = AllChatMessageIncoming
|
||||||
AllChatMessageIncoming ::
|
{ content :: Text
|
||||||
{content :: Text} ->
|
}
|
||||||
AllChatMessageIncoming
|
|
||||||
deriving (Generic, Show)
|
deriving (Generic, Show)
|
||||||
|
|
||||||
instance FromJSON AllChatMessageIncoming
|
instance FromJSON AllChatMessageIncoming
|
||||||
|
|
|
@ -15,7 +15,7 @@ import Control.Monad.Except
|
||||||
import Network.HTTP.Types
|
import Network.HTTP.Types
|
||||||
import Network.Wai
|
import Network.Wai
|
||||||
import Network.Wai.Handler.Warp (run)
|
import Network.Wai.Handler.Warp (run)
|
||||||
import Network.Wai.Middleware.RequestLogger (logStdout)
|
import Network.Wai.Middleware.RequestLogger (logStdoutDev)
|
||||||
import RoomDataHandler (roomDataHandler)
|
import RoomDataHandler (roomDataHandler)
|
||||||
import State.ConnectedClientsState
|
import State.ConnectedClientsState
|
||||||
( MonadConnectedClientsRead (..),
|
( MonadConnectedClientsRead (..),
|
||||||
|
@ -25,13 +25,13 @@ import State.RoomDataState
|
||||||
( MonadRoomDataStateModify (..),
|
( MonadRoomDataStateModify (..),
|
||||||
MonadRoomDataStateRead (getRoomDataState),
|
MonadRoomDataStateRead (getRoomDataState),
|
||||||
)
|
)
|
||||||
import State.RoomsState
|
import Types.AppTypes (Env (..))
|
||||||
( getRoomState,
|
import Types.RoomsState
|
||||||
|
( HasRoomsState (getRoomsState),
|
||||||
|
getRoomState,
|
||||||
|
roomStateDiffers,
|
||||||
updateRoomState,
|
updateRoomState,
|
||||||
)
|
)
|
||||||
import Text.Printf (printf)
|
|
||||||
import Types.AppTypes (Env (..), HasConfig (getConfig))
|
|
||||||
import Types.Config (ServerOptions (..))
|
|
||||||
import Types.WebEnv
|
import Types.WebEnv
|
||||||
|
|
||||||
newtype ExceptTApp e a = E {unExceptTApp :: IO (Either e a)}
|
newtype ExceptTApp e a = E {unExceptTApp :: IO (Either e a)}
|
||||||
|
@ -84,8 +84,7 @@ app ::
|
||||||
MonadError ResponseReceived m,
|
MonadError ResponseReceived m,
|
||||||
MonadRoomDataStateModify m,
|
MonadRoomDataStateModify m,
|
||||||
MonadRoomDataStateRead m,
|
MonadRoomDataStateRead m,
|
||||||
MonadBroadcast m,
|
MonadBroadcast m
|
||||||
HasConfig env
|
|
||||||
) =>
|
) =>
|
||||||
m ResponseReceived
|
m ResponseReceived
|
||||||
app = requestPathHandler
|
app = requestPathHandler
|
||||||
|
@ -97,8 +96,7 @@ requestPathHandler ::
|
||||||
MonadError ResponseReceived m,
|
MonadError ResponseReceived m,
|
||||||
MonadRoomDataStateModify m,
|
MonadRoomDataStateModify m,
|
||||||
MonadRoomDataStateRead m,
|
MonadRoomDataStateRead m,
|
||||||
MonadBroadcast m,
|
MonadBroadcast m
|
||||||
HasConfig env
|
|
||||||
) =>
|
) =>
|
||||||
m ResponseReceived
|
m ResponseReceived
|
||||||
requestPathHandler = do
|
requestPathHandler = do
|
||||||
|
@ -124,11 +122,27 @@ notFound = do
|
||||||
"404 - Not Found"
|
"404 - Not Found"
|
||||||
throwError response
|
throwError response
|
||||||
|
|
||||||
|
-- notFound ::
|
||||||
|
-- ( MonadIO m,
|
||||||
|
-- HasWebEnv env,
|
||||||
|
-- MonadReader env m,
|
||||||
|
-- MonadError ResponseReceived m
|
||||||
|
-- ) =>
|
||||||
|
-- m ResponseReceived
|
||||||
|
-- notFound = do
|
||||||
|
-- respond' <- getRespond <$> ask
|
||||||
|
-- response <-
|
||||||
|
-- liftIO $
|
||||||
|
-- respond' $
|
||||||
|
-- responseLBS
|
||||||
|
-- status200
|
||||||
|
-- [("Content-Type", "text/plain")]
|
||||||
|
-- "200 - Success"
|
||||||
|
-- response
|
||||||
|
|
||||||
runWebApp ::
|
runWebApp ::
|
||||||
( MonadIO m,
|
( MonadIO m,
|
||||||
MonadReader Env m,
|
MonadReader Env m
|
||||||
HasConfig Env,
|
|
||||||
HasConfig WebEnv
|
|
||||||
) =>
|
) =>
|
||||||
m Application
|
m Application
|
||||||
runWebApp = do
|
runWebApp = do
|
||||||
|
@ -147,14 +161,10 @@ runWebApp = do
|
||||||
|
|
||||||
runWebServer ::
|
runWebServer ::
|
||||||
( MonadIO m,
|
( MonadIO m,
|
||||||
MonadReader Env m,
|
MonadReader Env m
|
||||||
HasConfig Env,
|
|
||||||
HasConfig WebEnv
|
|
||||||
) =>
|
) =>
|
||||||
m ()
|
m ()
|
||||||
runWebServer = do
|
runWebServer = do
|
||||||
config' <- getConfig <$> ask
|
putStrLn "http://localhost:8081/"
|
||||||
let ServerOptions {port = webPort, listenAddress = address} = config'
|
runWebApp >>= liftIO . (run 8081 . logStdoutDev)
|
||||||
putStrLn $ pack $ printf "Webserver up and running at http://%s:%d/" address webPort
|
|
||||||
runWebApp >>= liftIO . (run webPort . logStdout)
|
|
||||||
return ()
|
return ()
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
{-# LANGUAGE MonoLocalBinds #-}
|
|
||||||
|
|
||||||
module WebSocket.Server
|
module WebSocket.Server
|
||||||
( runWebSocketServer,
|
( runWebSocketServer,
|
||||||
|
@ -10,27 +9,22 @@ where
|
||||||
import ClassyPrelude
|
import ClassyPrelude
|
||||||
import Data.UUID.V4 (nextRandom)
|
import Data.UUID.V4 (nextRandom)
|
||||||
import Network.WebSockets qualified as WS
|
import Network.WebSockets qualified as WS
|
||||||
import Text.Printf
|
import Types.AppTypes
|
||||||
import Types.AppTypes (Env, HasConfig (getConfig))
|
|
||||||
import Types.Config (ServerOptions (..))
|
|
||||||
import WebSocket.WSApp (WSApp (..), WSEnv (..), wsApp)
|
import WebSocket.WSApp (WSApp (..), WSEnv (..), wsApp)
|
||||||
|
|
||||||
runWebSocketServer ::
|
runWebSocketServer ::
|
||||||
( MonadIO m,
|
( MonadIO m,
|
||||||
Types.AppTypes.HasConfig Types.AppTypes.Env,
|
MonadReader Env m
|
||||||
MonadReader Types.AppTypes.Env m
|
|
||||||
) =>
|
) =>
|
||||||
m ()
|
m ()
|
||||||
runWebSocketServer = do
|
runWebSocketServer = do
|
||||||
config' <- getConfig <$> ask
|
putStrLn "Websocket up at 0.0.0.0:9160"
|
||||||
let ServerOptions {websocketPort = wsPort, listenAddress = address} = config'
|
|
||||||
putStrLn $ pack $ printf "WebSocket server up and running at ws://%s:%d/" address wsPort
|
|
||||||
wsApp' <- runWSApp
|
wsApp' <- runWSApp
|
||||||
liftIO $ WS.runServer address wsPort wsApp'
|
liftIO $ WS.runServer "0.0.0.0" 9160 wsApp'
|
||||||
|
|
||||||
runWSApp ::
|
runWSApp ::
|
||||||
( MonadIO m,
|
( MonadIO m,
|
||||||
MonadReader Types.AppTypes.Env m
|
MonadReader Env m
|
||||||
) =>
|
) =>
|
||||||
m WS.ServerApp
|
m WS.ServerApp
|
||||||
runWSApp = do
|
runWSApp = do
|
||||||
|
|
|
@ -33,11 +33,11 @@ wsApp = do
|
||||||
broadcastUserData
|
broadcastUserData
|
||||||
withCleanUp $ forever $ do
|
withCleanUp $ forever $ do
|
||||||
handleWSAction
|
handleWSAction
|
||||||
|
broadcastUserData
|
||||||
|
|
||||||
handleWSAction ::
|
handleWSAction ::
|
||||||
( MonadWebSocketSession m,
|
( MonadWebSocketSession m,
|
||||||
MonadConnectedClientsModify m,
|
MonadConnectedClientsModify m,
|
||||||
MonadRoomDataStateRead m,
|
|
||||||
MonadBroadcast m,
|
MonadBroadcast m,
|
||||||
MonadAllChat m
|
MonadAllChat m
|
||||||
) =>
|
) =>
|
||||||
|
@ -47,10 +47,8 @@ handleWSAction = do
|
||||||
case msg of
|
case msg of
|
||||||
JoinRoomMessage _ -> do
|
JoinRoomMessage _ -> do
|
||||||
joinRoom
|
joinRoom
|
||||||
broadcastUserData
|
|
||||||
ClientInfoMessage clientInfo -> do
|
ClientInfoMessage clientInfo -> do
|
||||||
updateClientName clientInfo
|
updateClientName clientInfo
|
||||||
broadcastUserData
|
|
||||||
AllChatMessageIncomingMessage incomingMessage -> do
|
AllChatMessageIncomingMessage incomingMessage -> do
|
||||||
broadCastAllChatMessage incomingMessage
|
broadCastAllChatMessage incomingMessage
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,7 @@ import ClassyPrelude
|
||||||
import Data.UUID
|
import Data.UUID
|
||||||
import Network.WebSockets qualified as WS
|
import Network.WebSockets qualified as WS
|
||||||
import State.ConnectedClientsState
|
import State.ConnectedClientsState
|
||||||
( HasConnectedClientState,
|
( MonadConnectedClientsModify (..),
|
||||||
MonadConnectedClientsModify (..),
|
|
||||||
MonadConnectedClientsRead (..),
|
MonadConnectedClientsRead (..),
|
||||||
addWSClientGeneric,
|
addWSClientGeneric,
|
||||||
getConnctedClientsGeneric,
|
getConnctedClientsGeneric,
|
||||||
|
@ -26,8 +25,8 @@ import State.ConnectedClientsState
|
||||||
updateWSClientGeneric,
|
updateWSClientGeneric,
|
||||||
)
|
)
|
||||||
import State.RoomDataState
|
import State.RoomDataState
|
||||||
import State.RoomsState (HasRoomsState (..), getRoomState)
|
|
||||||
import Types.AppTypes
|
import Types.AppTypes
|
||||||
|
import Types.RoomsState (HasRoomsState (..), getRoomState)
|
||||||
|
|
||||||
data WSEnv = WSEnv
|
data WSEnv = WSEnv
|
||||||
{ appEnv :: Env,
|
{ appEnv :: Env,
|
||||||
|
|
|
@ -14,10 +14,9 @@
|
||||||
# Use the latest resolver that uses the same ghc version
|
# Use the latest resolver that uses the same ghc version
|
||||||
# as build for nixos
|
# as build for nixos
|
||||||
# this way we can use prebuild binaries for hls
|
# this way we can use prebuild binaries for hls
|
||||||
# go to
|
#resolver: nightly-2022-11-12
|
||||||
# https://www.stackage.org/
|
#resolver: ghc-9.2.4
|
||||||
# and select the lts that is matching you hls version
|
resolver: lts-20.26
|
||||||
resolver: lts-22.43
|
|
||||||
#
|
#
|
||||||
# The location of a snapshot can be provided as a file or url. Stack assumes
|
# 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.
|
# a snapshot provided as a file might change, whereas a url resource does not.
|
||||||
|
@ -74,3 +73,5 @@ packages:
|
||||||
# compiler-check: newer-minor
|
# compiler-check: newer-minor
|
||||||
ghc-options:
|
ghc-options:
|
||||||
"$everything": -haddock
|
"$everything": -haddock
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
packages: []
|
packages: []
|
||||||
snapshots:
|
snapshots:
|
||||||
- completed:
|
- completed:
|
||||||
sha256: 08bd13ce621b41a8f5e51456b38d5b46d7783ce114a50ab604d6bbab0d002146
|
sha256: 5a59b2a405b3aba3c00188453be172b85893cab8ebc352b1ef58b0eae5d248a2
|
||||||
size: 720271
|
size: 650475
|
||||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/22/43.yaml
|
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/26.yaml
|
||||||
original: lts-22.43
|
original: lts-20.26
|
||||||
|
|
|
@ -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/
|
|
||||||
'';
|
|
||||||
}
|
|
|
@ -2,7 +2,7 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<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" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>FF Jitsi Rooms</title>
|
<title>FF Jitsi Rooms</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
1632
frontend/package-lock.json
generated
1632
frontend/package-lock.json
generated
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
1
frontend/public/vite.svg
Normal 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 |
|
@ -1 +0,0 @@
|
||||||
/nix/store/x3fqdrf05dcg52s7x6dkzql551g0bfxw-my-node-app-1.0.0
|
|
|
@ -20,7 +20,6 @@ function App() {
|
||||||
conferenceData={conferenceData}
|
conferenceData={conferenceData}
|
||||||
setConferenceData={setConferenceData}
|
setConferenceData={setConferenceData}
|
||||||
userInfo={userInfo}
|
userInfo={userInfo}
|
||||||
usersData={roomData}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
const ISPROD = window.location.protocol == 'https:'
|
const ISPROD = window.location.protocol == 'https:'
|
||||||
const JITSI_DOMAIN = 'meet.filefighter.de'
|
const JITSI_DOMAIN = 'thisisnotajitsi.filefighter.de'
|
||||||
const USE_REMOTE_BACKEND = true
|
const USE_REMOTE_BACKEND = true
|
||||||
const getWebsocketUrl = () => {
|
const getWebsocketUrl = () => {
|
||||||
if (ISPROD) return 'wss://' + window.location.host + '/ws'
|
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'
|
return 'ws://' + 'localhost:9160/ws'
|
||||||
}
|
}
|
||||||
const WEBSOCKET_URL = getWebsocketUrl()
|
const WEBSOCKET_URL = getWebsocketUrl()
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
.meeting-quickjoin {
|
|
||||||
text-align: center
|
|
||||||
}
|
|
||||||
|
|
||||||
.meeting {
|
|
||||||
flex-grow: 3;
|
|
||||||
}
|
|
|
@ -1,27 +1,19 @@
|
||||||
import { ConferenceData } from '../../background/jitsi/eventListeners'
|
import { ConferenceData } from '../../background/jitsi/eventListeners'
|
||||||
import { UsersData } from '../../background/types/roomData'
|
|
||||||
import useMeetingStarted from '../../hooks/useMeetingStarted'
|
import useMeetingStarted from '../../hooks/useMeetingStarted'
|
||||||
import { useRoomName } from '../../hooks/useRoomName'
|
import { useRoomName } from '../../hooks/useRoomName'
|
||||||
import JitsiEntrypoint from '../jitsi/JitsiEntrypoint'
|
import JitsiEntrypoint from '../jitsi/JitsiEntrypoint'
|
||||||
import { UserInfo } from '../jitsi/types'
|
import { UserInfo } from '../jitsi/types'
|
||||||
import MeetingNameInput from './MeetingNameInput'
|
import MeetingNameInput from './MeetingNameInput'
|
||||||
import './Meeting.css'
|
|
||||||
import React from 'react'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
conferenceData: ConferenceData | undefined
|
conferenceData: ConferenceData | undefined
|
||||||
setConferenceData: (newData: ConferenceData) => void
|
setConferenceData: (newData: ConferenceData) => void
|
||||||
userInfo: UserInfo
|
userInfo: UserInfo
|
||||||
usersData: UsersData
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function Meeting(props: Props) {
|
function Meeting({ conferenceData, setConferenceData, userInfo }: Props) {
|
||||||
const { conferenceData, setConferenceData, userInfo, usersData } = props
|
|
||||||
console.log("[Rooms] meeting usersData", props)
|
|
||||||
const [meetingStarted] = useMeetingStarted()
|
const [meetingStarted] = useMeetingStarted()
|
||||||
const { roomName } = useRoomName()
|
const { roomName } = useRoomName()
|
||||||
const [_, setMeetingStarted] = useMeetingStarted()
|
|
||||||
const { updateAndSubmitRoomName: updateAndSubmitRoomName } = useRoomName()
|
|
||||||
|
|
||||||
if (meetingStarted) {
|
if (meetingStarted) {
|
||||||
return (
|
return (
|
||||||
|
@ -34,32 +26,7 @@ function Meeting(props: Props) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <MeetingNameInput roomName={roomName} currentUser={userInfo.displayName} />
|
||||||
<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>)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Meeting
|
export default Meeting
|
||||||
|
|
|
@ -8,7 +8,7 @@ function MeetingNameInput(props: { roomName: string; currentUser: string }) {
|
||||||
const [_, setMeetingStarted] = useMeetingStarted()
|
const [_, setMeetingStarted] = useMeetingStarted()
|
||||||
|
|
||||||
const onInput: React.ChangeEventHandler<HTMLInputElement> = (event) => {
|
const onInput: React.ChangeEventHandler<HTMLInputElement> = (event) => {
|
||||||
updateRoomName(encodeURI(event.target.value))
|
updateRoomName(event.target.value)
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ function MeetingNameInput(props: { roomName: string; currentUser: string }) {
|
||||||
<div className="meeting-name-input">
|
<div className="meeting-name-input">
|
||||||
<h1>Greetings {props.currentUser}</h1>
|
<h1>Greetings {props.currentUser}</h1>
|
||||||
<form onSubmit={onSubmit}>
|
<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>
|
<button type="submit">Enter the adventure</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
.sidebar {
|
.sidebar {
|
||||||
resize: horizontal;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { UsersData } from '../../background/types/roomData'
|
||||||
import useMeetingStarted from '../../hooks/useMeetingStarted'
|
import useMeetingStarted from '../../hooks/useMeetingStarted'
|
||||||
import { useRoomName } from '../../hooks/useRoomName'
|
import { useRoomName } from '../../hooks/useRoomName'
|
||||||
import Chat from '../chat/Chat'
|
import Chat from '../chat/Chat'
|
||||||
import React from 'react'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
usersData: UsersData
|
usersData: UsersData
|
||||||
|
@ -23,7 +22,7 @@ function Sidebar(props: Props) {
|
||||||
<div className="sidebar-body">
|
<div className="sidebar-body">
|
||||||
{props.usersData.roomsData.map((roomData) => {
|
{props.usersData.roomsData.map((roomData) => {
|
||||||
return (
|
return (
|
||||||
<React.Fragment key={roomData.roomName}>
|
<>
|
||||||
<h3>
|
<h3>
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
|
@ -32,18 +31,18 @@ function Sidebar(props: Props) {
|
||||||
setMeetingStarted(true)
|
setMeetingStarted(true)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{decodeURI(roomData.roomName)}
|
{roomData.roomName}
|
||||||
</a>
|
</a>
|
||||||
</h3>
|
</h3>
|
||||||
{roomData.participants.map((participant) => (
|
{roomData.participants.map((participant) => (
|
||||||
<div key={participant.jid}> {participant.displayName} </div>
|
<div key={participant.jid}> {participant.displayName} </div>
|
||||||
))}
|
))}
|
||||||
</React.Fragment>
|
</>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3>No room</h3>
|
<h3> No room</h3>
|
||||||
{props.usersData.usersWithOutRoom.map((user) => (
|
{props.usersData.usersWithOutRoom.map((user) => (
|
||||||
<div key={user.uuid}>{user.name}</div>
|
<div key={user.uuid}>{user.name}</div>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -20,7 +20,7 @@ function useSidebarVisibility() {
|
||||||
return { sidebarVisibility, toggleSidebarVisibility, sidebarToggleText }
|
return { sidebarVisibility, toggleSidebarVisibility, sidebarToggleText }
|
||||||
}
|
}
|
||||||
const getSidebarToggleText = (sidebarVisibility: sidebarVisibilityOptions) => {
|
const getSidebarToggleText = (sidebarVisibility: sidebarVisibilityOptions) => {
|
||||||
if (sidebarVisibility === 'full') return '<--'
|
if (sidebarVisibility === 'full') return '<-'
|
||||||
if (sidebarVisibility === 'small') return '<-'
|
if (sidebarVisibility === 'small') return '<-'
|
||||||
if (sidebarVisibility === 'hidden') return '->'
|
if (sidebarVisibility === 'hidden') return '->'
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,23 +7,20 @@ import useWebSocketConnection from './useWebSocketConnection'
|
||||||
function useBackendData(userInfo: UserInfo) {
|
function useBackendData(userInfo: UserInfo) {
|
||||||
console.log('[Rooms] useBackendData')
|
console.log('[Rooms] useBackendData')
|
||||||
|
|
||||||
const { onMessage, cleanUpOnMessage, sendMessage, disconnect } = useWebSocketConnection(userInfo)
|
const { onMessage, sendMessage, disconnect } = useWebSocketConnection(userInfo)
|
||||||
const { roomData, setRoomData } = useRoomData()
|
const { roomData, setRoomData } = useRoomData()
|
||||||
const { addChatMessage } = useAllChat()
|
const { addChatMessage } = useAllChat()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('[Rooms] add onMessage Listener')
|
onMessage((messageString) => {
|
||||||
const messageCallback = (messageString: string) => {
|
|
||||||
console.log('[Rooms] message from ws', messageString)
|
console.log('[Rooms] message from ws', messageString)
|
||||||
const messageObject = JSON.parse(messageString)
|
const messageObject = JSON.parse(messageString)
|
||||||
|
|
||||||
!!messageObject.roomsData && setRoomData(messageObject)
|
!!messageObject.roomsData && setRoomData(messageObject)
|
||||||
!!messageObject.content && addChatMessage(messageObject)
|
!!messageObject.content && addChatMessage(messageObject)
|
||||||
return disconnect
|
return disconnect
|
||||||
}
|
})
|
||||||
onMessage(messageCallback)
|
}, [onMessage, setRoomData, disconnect])
|
||||||
return () => cleanUpOnMessage(messageCallback)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return { roomData, sendMessage }
|
return { roomData, sendMessage }
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,7 @@ import { useState } from 'react'
|
||||||
import { UsersData } from '../background/types/roomData'
|
import { UsersData } from '../background/types/roomData'
|
||||||
|
|
||||||
function useRoomData() {
|
function useRoomData() {
|
||||||
const [roomData, setRoomDataInternal] = useState<UsersData>()
|
const [roomData, setRoomData] = useState<UsersData>()
|
||||||
|
|
||||||
const setRoomData = (usersData: UsersData) => {
|
|
||||||
usersData.roomsData = usersData.roomsData.map((roomData) => {
|
|
||||||
roomData.roomName = decodeURI(roomData.roomName)
|
|
||||||
return roomData
|
|
||||||
})
|
|
||||||
setRoomDataInternal(usersData)
|
|
||||||
}
|
|
||||||
|
|
||||||
return { roomData, setRoomData }
|
return { roomData, setRoomData }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { atom, useAtom } from 'jotai'
|
import { atom, useAtom } from 'jotai'
|
||||||
import { useCallback } from 'react'
|
import { useCallback, useState } from 'react'
|
||||||
|
|
||||||
const roomNameAtom = atom(getRoomNameFromUrl())
|
const roomNameAtom = atom(getRoomNameFromUrl())
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ function setRoomNameInUrl(roomName: string) {
|
||||||
|
|
||||||
function setRoomNameInTitle(roomName: string) {
|
function setRoomNameInTitle(roomName: string) {
|
||||||
if (!!roomName) {
|
if (!!roomName) {
|
||||||
document.title = decodeURI(roomName)
|
document.title = roomName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
import { atom, useAtom } from 'jotai'
|
import { useCallback, useState } from 'react'
|
||||||
import { useCallback, useEffect } from 'react'
|
|
||||||
import { WEBSOCKET_URL } from '../background/constants'
|
import { WEBSOCKET_URL } from '../background/constants'
|
||||||
import { UserInfo } from '../components/jitsi/types'
|
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)
|
return webSocket
|
||||||
const webSocketConnectionAtom = atom(webSocket)
|
}
|
||||||
|
|
||||||
function useWebSocketConnection(userInfo: UserInfo) {
|
function useWebSocketConnection(userInfo: UserInfo) {
|
||||||
console.log('[Rooms] useWebSocketConnection')
|
console.log('[Rooms] useWebSocketConnection')
|
||||||
|
|
||||||
const [webSocketConnection] = useAtom(webSocketConnectionAtom)
|
const [webSocketConnection] = useState<WebSocket>(() => createWebSocketConnection(userInfo))
|
||||||
useEffect(() => {
|
|
||||||
sendMessageNowOrLater(webSocketConnection, JSON.stringify(userInfo))
|
|
||||||
}, [webSocketConnection, userInfo]);
|
|
||||||
|
|
||||||
const sendMessage = useCallback(
|
const sendMessage = useCallback(
|
||||||
(message: string) => {
|
(message: string) => {
|
||||||
|
@ -37,19 +37,8 @@ function useWebSocketConnection(userInfo: UserInfo) {
|
||||||
)
|
)
|
||||||
|
|
||||||
const disconnect = useCallback(webSocketConnection.close, [webSocketConnection])
|
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 }
|
return { onMessage, sendMessage, disconnect }
|
||||||
}
|
|
||||||
|
|
||||||
const sendMessageNowOrLater = (webSocket: WebSocket, message: string) => {
|
|
||||||
if (webSocket.readyState !== WebSocket.OPEN) {
|
|
||||||
return webSocket.addEventListener('open', (_: Event) => webSocket.send(message))
|
|
||||||
}
|
|
||||||
webSocket.send(message)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default useWebSocketConnection
|
export default useWebSocketConnection
|
||||||
|
|
|
@ -51,9 +51,7 @@ button {
|
||||||
background-color: #1a1a1a;
|
background-color: #1a1a1a;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: border-color 0.25s;
|
transition: border-color 0.25s;
|
||||||
color: white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button:hover {
|
button:hover {
|
||||||
border-color: #646cff;
|
border-color: #646cff;
|
||||||
}
|
}
|
||||||
|
@ -62,6 +60,19 @@ button:focus-visible {
|
||||||
outline: 4px auto -webkit-focus-ring-color;
|
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 {
|
#root {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -81,5 +92,4 @@ input {
|
||||||
background-color: #2b2a33;
|
background-color: #2b2a33;
|
||||||
border-color: #646cff;
|
border-color: #646cff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
color: white;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.10.tgz"
|
resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.10.tgz"
|
||||||
integrity sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==
|
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"
|
version "7.20.12"
|
||||||
resolved "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz"
|
resolved "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz"
|
||||||
integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==
|
integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==
|
||||||
|
@ -209,17 +209,122 @@
|
||||||
"@babel/helper-validator-identifier" "^7.19.1"
|
"@babel/helper-validator-identifier" "^7.19.1"
|
||||||
to-fast-properties "^2.0.0"
|
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":
|
"@esbuild/linux-x64@0.16.15":
|
||||||
version "0.16.15"
|
version "0.16.15"
|
||||||
resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.15.tgz"
|
resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.15.tgz"
|
||||||
integrity sha512-t7/fOXBUKfigvhJLGKZ9TPHHgqNgpIpYaAbcXQk1X+fPeUG7x0tpAbXJ2wST9F/gJ02+CLETPMnhG7Tra2wqsQ==
|
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":
|
"@fortawesome/fontawesome-common-types@6.4.0":
|
||||||
version "6.4.0"
|
version "6.4.0"
|
||||||
resolved "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.0.tgz"
|
resolved "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.0.tgz"
|
||||||
integrity sha512-HNii132xfomg5QVZw0HwXXpN22s7VBHQBv9CeOu9tfJnhsWQNd2lmTNi8CSrnw5B+5YOmzu1UoPAyxaXsJ6RgQ==
|
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"
|
version "6.4.0"
|
||||||
resolved "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.0.tgz"
|
resolved "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.0.tgz"
|
||||||
integrity sha512-Bertv8xOiVELz5raB2FlXDPKt+m94MQ3JgDfsVbrqNpLU9+UE2E18GKjLKw+d3XbeYPqg1pzyQKGsrzbw+pPaw==
|
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"
|
resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz"
|
||||||
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
|
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"
|
version "1.4.14"
|
||||||
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz"
|
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz"
|
||||||
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
|
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
|
||||||
|
@ -329,7 +434,7 @@ ansi-styles@^3.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
color-convert "^1.9.0"
|
color-convert "^1.9.0"
|
||||||
|
|
||||||
browserslist@^4.21.3, "browserslist@>= 4.21.0":
|
browserslist@^4.21.3:
|
||||||
version "4.21.4"
|
version "4.21.4"
|
||||||
resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz"
|
resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz"
|
||||||
integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==
|
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"
|
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
|
||||||
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
|
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:
|
function-bind@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
|
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"
|
object-assign "^4.1.1"
|
||||||
react-is "^16.13.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"
|
version "18.2.0"
|
||||||
resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz"
|
resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz"
|
||||||
integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
|
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"
|
resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz"
|
||||||
integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==
|
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"
|
version "18.2.0"
|
||||||
resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz"
|
resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz"
|
||||||
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
|
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
|
||||||
|
|
68
nixos.nix
68
nixos.nix
|
@ -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" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -10,7 +10,7 @@
|
||||||
### Get started
|
### Get started
|
||||||
|
|
||||||
- run `./run_dev.sh`
|
- 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
|
- make changes to mod_jitsi_rooms.lua and save the file
|
||||||
- prosody will be restarted
|
- prosody will be restarted
|
||||||
- join a room in jitsi
|
- join a room in jitsi
|
||||||
|
@ -19,8 +19,3 @@
|
||||||
## Deploying
|
## Deploying
|
||||||
|
|
||||||
- all required changes to the [jitsi meet docker setup](https://github.com/jitsi/docker-jitsi-meet) are marked with a "custom" comment
|
- 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
|
|
||||||
|
|
|
@ -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";
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -347,7 +347,7 @@ services:
|
||||||
meet.jitsi:
|
meet.jitsi:
|
||||||
|
|
||||||
jitsi-rooms:
|
jitsi-rooms:
|
||||||
image: jitsi-rooms
|
image: jitsi-rooms:67y5d9y2zbi7wkqm2jpcjj1k2614qwx2
|
||||||
restart: ${RESTART_POLICY:-unless-stopped}
|
restart: ${RESTART_POLICY:-unless-stopped}
|
||||||
ports:
|
ports:
|
||||||
- '9160:9160'
|
- '9160:9160'
|
||||||
|
|
0
prodsody/fix-permissions.sh
Executable file → Normal file
0
prodsody/fix-permissions.sh
Executable file → Normal file
|
@ -4,7 +4,6 @@ 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 async_handler_wrapper = module:require "util".async_handler_wrapper;
|
local async_handler_wrapper = module:require "util".async_handler_wrapper;
|
||||||
local get_room_from_jid = module:require "util".get_room_from_jid;
|
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)
|
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 room = get_room_from_jid(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;
|
||||||
|
@ -48,6 +47,7 @@ function get_participants_for_room(room_name)
|
||||||
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)));
|
||||||
|
@ -74,7 +74,7 @@ end
|
||||||
-- @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()
|
||||||
|
@ -86,9 +86,9 @@ function module.load()
|
||||||
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
|
||||||
|
|
||||||
|
@ -125,6 +125,7 @@ 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
|
||||||
|
@ -146,11 +147,12 @@ function handle_room_event(event)
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
async_http_request("http://jitsi-rooms:8081/roomdata",
|
async_http_request("http://192.168.2.116: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
|
||||||
|
|
|
@ -1,94 +1,95 @@
|
||||||
---@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 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 decoded_room_address = http.urldecode(room_address)
|
local room_address = jid.join(room_name, muc_domain_prefix .. "." .. domain_name);
|
||||||
local room = get_room_from_jid(decoded_room_address)
|
local room = get_room_from_jid(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", "there are %s occupants in room", tostring(participant_count))
|
log("debug",
|
||||||
return occupants_json
|
"there are %s occupants in room", tostring(participant_count));
|
||||||
else
|
return occupants_json
|
||||||
log("debug", "no such room exists")
|
else
|
||||||
end
|
log("debug", "no such room exists");
|
||||||
return occupants_json
|
end
|
||||||
|
return occupants_json
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_all_rooms_with_participants()
|
function get_all_rooms_with_participants()
|
||||||
local sessions = prosody.full_sessions
|
|
||||||
|
|
||||||
local someTable = (it.join(it.keys(sessions)))
|
local sessions = prosody.full_sessions;
|
||||||
local actual_rooms = someTable[1][2]
|
|
||||||
|
|
||||||
local roomNames = {}
|
local someTable = (it.join(it.keys(sessions)));
|
||||||
for _, v in pairs(actual_rooms) do
|
local actual_rooms = someTable[1][2]
|
||||||
local roomName = v["jitsi_web_query_room"]
|
|
||||||
if roomName ~= nil then
|
|
||||||
roomNames[roomName] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local rooms_with_participants = array()
|
local roomNames = {}
|
||||||
for room_name, _ in pairs(roomNames) do
|
for _, v in pairs(actual_rooms) do
|
||||||
local room = { roomName = room_name, participants = get_participants_for_room(room_name) }
|
local roomName = v["jitsi_web_query_room"]
|
||||||
rooms_with_participants:push(room)
|
if (roomName ~= nil) then
|
||||||
end
|
roomNames[roomName] = true;
|
||||||
return json.encode(rooms_with_participants)
|
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
|
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)
|
["GET"] = function(event) return async_handler_wrapper(event, handle_get_sessions) end;
|
||||||
return async_handler_wrapper(event, handle_get_sessions)
|
|
||||||
end,
|
};
|
||||||
},
|
});
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-------- event stuff
|
-------- event stuff
|
||||||
|
@ -102,104 +103,106 @@ 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
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
if is_healthcheck_room(event.room.jid) then
|
if event == nil or event.room == nil or event.room.jid == nil then
|
||||||
return true
|
return true;
|
||||||
end
|
end
|
||||||
|
|
||||||
if event.occupant and jid.node(event.occupant.jid) == "focus" then
|
if is_healthcheck_room(event.room.jid) then
|
||||||
return true
|
return true;
|
||||||
end
|
end
|
||||||
|
|
||||||
return false
|
if event.occupant and jid.node(event.occupant.jid) == "focus" then
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
|
||||||
|
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://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
|
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);
|
||||||
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 +0,0 @@
|
||||||
/nix/store/88ak9a0jwp38h95kb3hpd3965pmbhnjx-jitsi-room-prosody-1.0.0
|
|
Loading…
Reference in a new issue