From 6b5f3228f80ad5c44d53d9e05d0e4b2dbbb3dba0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andrej=20Rama=C5=A1euski?= <andrej@x2.cz>
Date: Tue, 22 Dec 2020 22:47:30 +0100
Subject: [PATCH] Pocitani aktivnich uzivatelu, ban, unban, userinfo,
 autorizace v socketu

---
 lib/CF/Controller/Users.pm           | 63 ++++++++++++++++++++++++++++
 lib/CF/Controller/Websockets.pm      | 35 +++++++++++++++-
 lib/CF/Schema/Result/Announcement.pm |  3 +-
 lib/CF/Schema/Result/User.pm         | 15 +++++++
 openapi.yaml                         | 61 ++++++++++++++++++++++++++-
 sql/1/up.sql                         |  2 +
 6 files changed, 175 insertions(+), 4 deletions(-)
 create mode 100644 lib/CF/Controller/Users.pm

diff --git a/lib/CF/Controller/Users.pm b/lib/CF/Controller/Users.pm
new file mode 100644
index 0000000..6d6ba2c
--- /dev/null
+++ b/lib/CF/Controller/Users.pm
@@ -0,0 +1,63 @@
+package CF::Controller::Users;
+
+use Mojo::Base 'Mojolicious::Controller';
+use Mojo::Pg::PubSub;
+use feature 'signatures';
+no warnings qw{ experimental::signatures };
+
+sub me ($c){
+    my $user = $c->_get( $c->user->{id} ) // return;
+    $c->render(openapi => $c->spec_filter($user->formatted, 'User'));
+}
+
+sub ban ($c){
+    my $user   = $c->_get( $c->stash->{id} ) // return;
+    my $pubsub = Mojo::Pg::PubSub->new(pg => $c->pg);
+    my $guard  = $c->schema->txn_scope_guard;
+
+    $user->update({ banned_until => \"now()+'8 hour'", });
+    #TODO: ANN
+
+    $pubsub->json('notify')->notify( notify => {
+        event   => 'user_banned',
+        payload => $c->spec_filter($user->formatted, 'User'),
+    });
+
+    $guard->commit;
+    $c->render(status => 204, text => '');
+}
+
+sub unban ($c){
+    my $user   = $c->_get( $c->stash->{id} ) // return;
+    my $pubsub = Mojo::Pg::PubSub->new(pg => $c->pg);
+    my $guard  = $c->schema->txn_scope_guard;
+
+
+    $user->update({ banned_until => undef });
+    #TODO: ANN
+
+    $pubsub->json('notify')->notify( notify => {
+        event   => 'user_unbanned',
+        payload => $c->spec_filter($user->formatted, 'User'),
+    });
+
+    $guard->commit;
+    $c->render(status => 204, text => '');
+}
+
+sub _get ($c, $id ) {
+
+    my $user;
+
+    if ( $id =~ /^\d+$/) {
+        $user = $c->schema->resultset('User')->find( { id => $id } );
+    }
+    else {
+        $user = $c->schema->resultset('User')->find( { uuid => $id } );
+    }
+
+    return $c->error(404, 'User not found') if ! $user;
+    return $user;
+}
+
+1;
diff --git a/lib/CF/Controller/Websockets.pm b/lib/CF/Controller/Websockets.pm
index 04bad7e..3b3daf9 100644
--- a/lib/CF/Controller/Websockets.pm
+++ b/lib/CF/Controller/Websockets.pm
@@ -2,13 +2,32 @@ package CF::Controller::Websockets;
 
 use Mojo::Base 'Mojolicious::Controller';
 use Mojo::Pg::PubSub;
+use JSON;
 use feature 'signatures';
 no warnings qw{ experimental::signatures };
 
+use constant SOCKET_INACTIVITY_TIMEOUT => 300;
+use constant USER_ALIVE_TIMEOUT        => 100;
+
 sub main {
     my $c = shift;
 
-    $c->inactivity_timeout(600);
+    $c->inactivity_timeout(SOCKET_INACTIVITY_TIMEOUT);
+
+    my $user;
+
+    if ( my $key = $c->req->headers->authorization ) {
+       if ( $key =~ s/Bearer\s+//i ) {
+            $c->oauth_token($key);
+
+            if ( $c->user ) {
+                $user = $c->schema->resultset('User')->find_or_create(
+                    $c->user, { key => 'uuid'}
+                );
+            }
+        }
+    }
+
 
     my $pubsub = Mojo::Pg::PubSub->new(pg => $c->pg);
 
@@ -16,6 +35,20 @@ sub main {
         $c->send($payload);
     });
 
+    $c->on(message => sub( $c, $message ) {
+        $user->update({keepalive => \'now()'}) if $user;
+
+        my $min_alive_time = "now() - '" . USER_ALIVE_TIMEOUT. " s'::interval";
+
+        my $alive = $c->schema->resultset('User')->count(
+            {
+                keepalive => { '>' => \$min_alive_time },
+            }
+        );
+        $c->send(to_json({ online_users => $alive }));
+
+    });
+
     $c->on(finish => sub ($c, $code, $reason = undef) {
         $pubsub->unlisten('notify');
         $c->app->log->debug("WebSocket closed with status $code");
diff --git a/lib/CF/Schema/Result/Announcement.pm b/lib/CF/Schema/Result/Announcement.pm
index 97c4159..bf12c5b 100644
--- a/lib/CF/Schema/Result/Announcement.pm
+++ b/lib/CF/Schema/Result/Announcement.pm
@@ -29,7 +29,6 @@ __PACKAGE__->add_columns(
 
 __PACKAGE__->set_primary_key('id');
 
-1;
 
 sub format {
     my $self = shift;
@@ -46,3 +45,5 @@ sub format {
     return $announcement;
 
 }
+
+1;
diff --git a/lib/CF/Schema/Result/User.pm b/lib/CF/Schema/Result/User.pm
index bc54616..3bf40ca 100644
--- a/lib/CF/Schema/Result/User.pm
+++ b/lib/CF/Schema/Result/User.pm
@@ -22,6 +22,8 @@ __PACKAGE__->add_columns(
         username
         name
         main_group_name
+        keepalive
+        banned_until
     ),
 );
 
@@ -36,4 +38,17 @@ __PACKAGE__->has_many(
     { 'foreign.user_id' => 'self.id', },
 );
 
+sub formatted {
+    my $self = shift;
+
+    my $user = {
+        $self->get_columns,
+        group => $self->main_group_name,
+        is_banned => $self->banned_until ? 1:0,
+    };
+
+    return $user;
+
+}
+
 1;
diff --git a/openapi.yaml b/openapi.yaml
index 2e30286..41226eb 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -92,7 +92,7 @@ components:
           type: string
         originator:
           type: string
-    Author:
+    User:
       type: object
       properties:
         id:
@@ -103,6 +103,8 @@ components:
             type: string
         username:
             type: string
+        is_banned:
+            type: boolean
     Announcement:
       type: object
       description: Notifikace
@@ -144,7 +146,7 @@ components:
         is_archived:
           type: boolean
         author:
-            $ref: '#/components/schemas/Author'
+            $ref: '#/components/schemas/User'
         ranking:
             $ref: '#/components/schemas/Ranking'
         history_log:
@@ -419,6 +421,18 @@ paths:
         204:
           description: Post updated
 
+    delete:
+      x-mojo-to: posts#delete
+      security:
+        - Bearer: ['chairman']
+      tags:
+        - posts
+      summary: "Smazat zpravu"
+      operationId: deletePost
+      responses:
+        204:
+          description: Post deleted
+
   /posts/{id}/like:
     patch:
       security:
@@ -460,3 +474,46 @@ paths:
       responses:
         204:
           description: Post disliked
+
+  /users/me:
+    get:
+      x-mojo-to: users#me
+      security:
+        - Bearer: []
+      tags:
+        - users
+      summary: "Detail uzivatele"
+      operationId: getCurrentUser
+      responses:
+        200:
+          description: User
+          content:
+            application/json:
+              schema:
+                  $ref: '#/components/schemas/User'
+
+  /users/{id}/ban:
+    patch:
+      security:
+        - Bearer: ['chairman']
+      x-mojo-to: users#ban
+      tags:
+        - users
+      summary: Ban user
+      operationId: banUser
+      responses:
+        204:
+          description: User banned
+
+  /users/{id}/unban:
+    patch:
+      security:
+        - Bearer: ['chairman']
+      x-mojo-to: users#unban
+      tags:
+        - users
+      summary: Unban user
+      operationId: UnbanUser
+      responses:
+        204:
+          description: User banned
diff --git a/sql/1/up.sql b/sql/1/up.sql
index 8917a50..b7e7770 100644
--- a/sql/1/up.sql
+++ b/sql/1/up.sql
@@ -21,6 +21,8 @@ create table "users" (
     "username" text,
     "name" text,
     "main_group_name" text,
+    "keepalive" timestamp,
+    "banned_until" timestamp,
     primary key("id"),
     unique("uuid")
 );
-- 
GitLab