From a05646a19b1a5ac4fc5cf0ede2d57ad29a219b91 Mon Sep 17 00:00:00 2001
From: Philippe Loctaux
Date: Mon, 8 May 2023 19:43:44 +0200
Subject: [PATCH] admin dashboard: apps, users, roles, logins in the last 24
hours
---
crates/authorization_codes/src/database.rs | 20 ++++
.../queries/authorization_codes/get_all.sql | 7 ++
crates/database/sqlx-data.json | 48 ++++++++
.../src/tables/authorization_codes.rs | 7 ++
crates/ezidam/src/icons.rs | 4 +-
crates/ezidam/src/routes/admin.rs | 4 +
crates/ezidam/src/routes/admin/dashboard.rs | 25 +++++
.../templates/pages/admin/dashboard.html.tera | 103 ++++++++++++++++++
8 files changed, 217 insertions(+), 1 deletion(-)
create mode 100644 crates/database/queries/authorization_codes/get_all.sql
diff --git a/crates/authorization_codes/src/database.rs b/crates/authorization_codes/src/database.rs
index 94c104d..064795b 100644
--- a/crates/authorization_codes/src/database.rs
+++ b/crates/authorization_codes/src/database.rs
@@ -22,6 +22,26 @@ impl From for AuthorizationCode {
}
impl AuthorizationCode {
+ pub async fn get_all(conn: impl SqliteExecutor<'_>) -> Result, Error> {
+ Ok(DatabaseAuthorizationCodes::get_all(conn)
+ .await?
+ .into_iter()
+ .map(Self::from)
+ .collect::>())
+ }
+
+ pub async fn used_in_last_24_hours(conn: impl SqliteExecutor<'_>) -> Result {
+ let all = Self::get_all(conn).await?;
+
+ let last_24_hours = Utc::now() - Duration::hours(24);
+
+ Ok(all
+ .into_iter()
+ .filter_map(|code| code.used_at)
+ .filter(|&date| date >= last_24_hours)
+ .count())
+ }
+
pub async fn insert(
conn: impl SqliteExecutor<'_>,
code: &str,
diff --git a/crates/database/queries/authorization_codes/get_all.sql b/crates/database/queries/authorization_codes/get_all.sql
new file mode 100644
index 0000000..8090e1f
--- /dev/null
+++ b/crates/database/queries/authorization_codes/get_all.sql
@@ -0,0 +1,7 @@
+select code,
+ app,
+ user,
+ created_at as "created_at: DateTime",
+ expires_at as "expires_at: DateTime",
+ used_at as "used_at: DateTime"
+from authorization_codes
diff --git a/crates/database/sqlx-data.json b/crates/database/sqlx-data.json
index 4ba5362..33c88e9 100644
--- a/crates/database/sqlx-data.json
+++ b/crates/database/sqlx-data.json
@@ -1332,6 +1332,54 @@
},
"query": "update users\n\nset email = ?\n\nwhere id is ?"
},
+ "c3dcd38a2d4ff391aed4a2ac3f393646319950334494ecb5fa7effe9806d07ab": {
+ "describe": {
+ "columns": [
+ {
+ "name": "code",
+ "ordinal": 0,
+ "type_info": "Text"
+ },
+ {
+ "name": "app",
+ "ordinal": 1,
+ "type_info": "Text"
+ },
+ {
+ "name": "user",
+ "ordinal": 2,
+ "type_info": "Text"
+ },
+ {
+ "name": "created_at: DateTime",
+ "ordinal": 3,
+ "type_info": "Text"
+ },
+ {
+ "name": "expires_at: DateTime",
+ "ordinal": 4,
+ "type_info": "Text"
+ },
+ {
+ "name": "used_at: DateTime",
+ "ordinal": 5,
+ "type_info": "Text"
+ }
+ ],
+ "nullable": [
+ false,
+ false,
+ false,
+ false,
+ false,
+ true
+ ],
+ "parameters": {
+ "Right": 0
+ }
+ },
+ "query": "select code,\n app,\n user,\n created_at as \"created_at: DateTime\",\n expires_at as \"expires_at: DateTime\",\n used_at as \"used_at: DateTime\"\nfrom authorization_codes\n"
+ },
"c6157ec3928527ec0ac5f493a5a91faff7e3668204a179e827a87d6279a02c40": {
"describe": {
"columns": [],
diff --git a/crates/database/src/tables/authorization_codes.rs b/crates/database/src/tables/authorization_codes.rs
index fcf5817..78f8fdd 100644
--- a/crates/database/src/tables/authorization_codes.rs
+++ b/crates/database/src/tables/authorization_codes.rs
@@ -17,6 +17,13 @@ pub struct AuthorizationCodes {
}
impl AuthorizationCodes {
+ pub async fn get_all(conn: impl SqliteExecutor<'_>) -> Result, Error> {
+ sqlx::query_file_as!(Self, "queries/authorization_codes/get_all.sql")
+ .fetch_all(conn)
+ .await
+ .map_err(handle_error)
+ }
+
pub async fn insert(
conn: impl SqliteExecutor<'_>,
code: &str,
diff --git a/crates/ezidam/src/icons.rs b/crates/ezidam/src/icons.rs
index 1cc77b3..cf7901c 100644
--- a/crates/ezidam/src/icons.rs
+++ b/crates/ezidam/src/icons.rs
@@ -53,7 +53,8 @@ impl Icon {
"users-group", UsersGroup, r#""#,
"users-group-large", UsersGroupLarge, r#""#,
"adjustments", Adjustments, r#""#,
- "device-floppy", DeviceFloppy, r#""#
+ "device-floppy", DeviceFloppy, r#""#,
+ "login", Login, r#""#
}
}
@@ -88,6 +89,7 @@ pub fn icons_to_templates(tera: &mut Tera) {
Icon::UsersGroupLarge,
Icon::Adjustments,
Icon::DeviceFloppy,
+ Icon::Login,
];
// For each icon, it will output: ("icons/name", "")
diff --git a/crates/ezidam/src/routes/admin.rs b/crates/ezidam/src/routes/admin.rs
index e4fac3d..d38eb52 100644
--- a/crates/ezidam/src/routes/admin.rs
+++ b/crates/ezidam/src/routes/admin.rs
@@ -69,6 +69,10 @@ pub mod content {
#[derive(Clone)]
pub struct AdminDashboard {
pub user: JwtClaims,
+ pub users: Vec,
+ pub roles: Vec,
+ pub apps: Vec,
+ pub number_logins_last_24_hours: usize,
}
#[derive(Serialize)]
diff --git a/crates/ezidam/src/routes/admin/dashboard.rs b/crates/ezidam/src/routes/admin/dashboard.rs
index 9c44d5e..fdee596 100644
--- a/crates/ezidam/src/routes/admin/dashboard.rs
+++ b/crates/ezidam/src/routes/admin/dashboard.rs
@@ -1,9 +1,34 @@
use crate::routes::prelude::*;
+use apps::App;
+use authorization_codes::AuthorizationCode;
use rocket::get;
+use roles::Role;
+use users::User;
#[get("/admin")]
pub async fn admin_dashboard(mut db: Connection, admin: JwtAdmin) -> Result {
+ let mut transaction = db.begin().await?;
+
+ // Get users
+ let users = User::get_all(&mut transaction).await?;
+
+ // Get roles
+ let roles = Role::get_all(&mut transaction).await?;
+
+ // Get apps
+ let apps = App::get_all(&mut transaction, None).await?;
+
+ // Get number of logins in the last 24 hours
+ let number_logins_last_24_hours =
+ AuthorizationCode::used_in_last_24_hours(&mut transaction).await?;
+
+ transaction.commit().await?;
+
Ok(Page::AdminDashboard(super::content::AdminDashboard {
user: admin.0,
+ users,
+ roles,
+ apps,
+ number_logins_last_24_hours,
}))
}
diff --git a/crates/ezidam/templates/pages/admin/dashboard.html.tera b/crates/ezidam/templates/pages/admin/dashboard.html.tera
index 825b2ad..765b3ed 100644
--- a/crates/ezidam/templates/pages/admin/dashboard.html.tera
+++ b/crates/ezidam/templates/pages/admin/dashboard.html.tera
@@ -1,4 +1,107 @@
{% extends "shell" %}
{% block content %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% include "icons/apps" %}
+
+
+
+
+
+ {{ apps | length }} Applications
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% include "icons/user" %}
+
+
+
+
+ {{ users | length }} Users
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% include "icons/users-group" %}
+
+
+
+
+ {{ roles | length }} Roles
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% include "icons/login" %}
+
+
+
+
+ {{ number_logins_last_24_hours }} logins in the last 24 hours
+
+
+
+
+
+
+
+
+
{% endblock content %}