package CF::Controller::Websockets; use Mojo::Base 'Mojolicious::Controller'; use Mojo::Pg::PubSub; use Digest::SHA qw(hmac_sha1_hex); use POSIX qw(ceil); use feature 'signatures'; no warnings qw{ experimental::signatures }; use constant SOCKET_INACTIVITY_TIMEOUT => 300; sub main { my $c = shift; my $ip = $c->tx->remote_address; $c->inactivity_timeout(SOCKET_INACTIVITY_TIMEOUT); my $pubsub = Mojo::Pg::PubSub->new(pg => $c->pg); $pubsub->listen(notify => sub($pubsub, $payload) { $c->send($payload); }); $c->on(json => sub( $c, $message ) { if ( $message->{event} eq 'KEEPALIVE' ) { my $user; if ($message->{payload} =~ /^(\d+)$/) { # TODO: check signtaure $user = $c->schema->resultset('User')->find( { id => $1 } ); } $c->schema->resultset('Socket')->update_or_create({ id => $c->req->headers->header('Sec-WebSocket-Key'), ip => $ip, keepalive => \'now()', user_id => $user ? $user->id : undef, }, { key => $user ? 'user':'primary'} ); my $all = $c->schema->resultset('Socket_view')->count( { is_alive => 't', } ); my $members = $c->schema->resultset('Socket_view')->count( { is_alive => 't', is_member => 't' } ); my $group_size = $c->_member_group_size($members); $c->send({json => { event => 'online_users_updated', payload => { all => $all, members => $members, group_size_full => $group_size->{full}, group_size_half => $group_size->{half}, }}}); if ( $user ) { my $jitsi = $user->jitsi_allowed || $user->roles =~ /chairman/; $c->send({json => { event => 'user_status', payload => { jitsi_allowed => $jitsi ? \1:\0, # is_banned => $user->banned_until ? 1:0, }}}); } } }); $c->on(finish => sub ($c, $code, $reason = undef) { $pubsub->unlisten('notify'); $c->app->log->debug("WebSocket closed with status $code"); }); } sub _member_group_size ($c, $total = 0){ my $group = 2 * sqrt($total); my $min = $total / 100; my $max = $total / 5; $group = $min if $group < $min; $group = $max if $group > $max; return { full => ceil( $group ), half => ceil( $group/2 ), }; } 1;