feat: log site deploy ,update and delete events in db

This commit is contained in:
Aravinth Manivannan 2022-11-15 20:24:13 +05:30
parent 1e0fa7279f
commit f89b3e6d4c
Signed by: realaravinth
GPG Key ID: AD9F0F08E855ED88
5 changed files with 245 additions and 5 deletions

9
Cargo.lock generated
View File

@ -1620,6 +1620,7 @@ dependencies = [
"tracing-actix-web",
"url",
"urlencoding",
"uuid 1.2.2",
]
[[package]]
@ -2323,6 +2324,7 @@ dependencies = [
"time",
"tokio-stream",
"url",
"uuid 1.2.2",
"webpki-roots",
"whoami",
]
@ -2610,7 +2612,7 @@ dependencies = [
"actix-web",
"pin-project",
"tracing",
"uuid 1.2.1",
"uuid 1.2.2",
]
[[package]]
@ -2794,11 +2796,12 @@ dependencies = [
[[package]]
name = "uuid"
version = "1.2.1"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83"
checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c"
dependencies = [
"getrandom",
"serde",
]
[[package]]

View File

@ -19,7 +19,7 @@ actix-identity = "0.4.0"
actix-rt = "2"
actix-web-codegen-const-routes = { version = "0.1.0", tag = "0.1.0", git = "https://github.com/realaravinth/actix-web-codegen-const-routes" }
argon2-creds = { branch = "master", git = "https://github.com/realaravinth/argon2-creds"}
sqlx = { version = "0.6.1", features = [ "runtime-actix-rustls", "postgres", "time", "offline", "json"] }
sqlx = { version = "0.6.1", features = ["runtime-actix-rustls", "postgres", "time", "offline", "json", "uuid"] }
clap = { version = "3.2.20", features = ["derive"]}
config = "0.13"
@ -50,6 +50,7 @@ tracing = { version = "0.1.37", features = ["log"]}
tracing-actix-web = "0.6.2"
toml = "0.5.9"
serde_yaml = "0.9.14"
uuid = { version = "1.2.2", features = ["serde"] }
[dependencies.cache-buster]
git = "https://github.com/realaravinth/cache-buster"

View File

@ -0,0 +1,14 @@
CREATE TABLE IF NOT EXISTS librepages_deploy_event_type (
name VARCHAR(30) NOT NULL UNIQUE,
ID SERIAL PRIMARY KEY NOT NULL
);
CREATE UNIQUE INDEX librepages_deploy_event_name_index ON librepages_deploy_event_type(name);
CREATE TABLE IF NOT EXISTS librepages_site_deploy_events (
site INTEGER NOT NULL references librepages_sites(ID) ON DELETE CASCADE,
event_type INTEGER NOT NULL references librepages_deploy_event_type(ID),
time timestamptz NOT NULL,
pub_id uuid NOT NULL UNIQUE,
ID SERIAL PRIMARY KEY NOT NULL
);

View File

@ -347,6 +347,38 @@
},
"query": "SELECT email FROM librepages_users WHERE name = $1"
},
"e4adf1bc9175eeb9d61b495653bb452039cc38818c8792acdc6a1c732b6f4554": {
"describe": {
"columns": [
{
"name": "exists",
"ordinal": 0,
"type_info": "Bool"
}
],
"nullable": [
null
],
"parameters": {
"Left": [
"Text"
]
}
},
"query": "SELECT EXISTS (SELECT 1 from librepages_deploy_event_type WHERE name = $1)"
},
"ed935cd75e805ddd7223ea8ba298ff94018cf305c519120279a5d1f7bb99e23e": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Left": [
"Varchar"
]
}
},
"query": "INSERT INTO librepages_deploy_event_type\n (name) VALUES ($1);"
},
"faa4170a309f19a4abf1ca3f8dd3c0526945aa00f028ebf8bd7063825d448f5b": {
"describe": {
"columns": [],

192
src/db.rs
View File

@ -19,10 +19,10 @@ use std::str::FromStr;
use serde::{Deserialize, Serialize};
use sqlx::postgres::PgPoolOptions;
use sqlx::types::time::OffsetDateTime;
//use sqlx::types::Json;
use sqlx::ConnectOptions;
use sqlx::PgPool;
use tracing::error;
use uuid::Uuid;
use crate::errors::*;
@ -81,6 +81,7 @@ impl Database {
.await
.unwrap();
//.map_err(|e| ServiceError::ServiceError(Box::new(e)))?;
self.create_event_type().await?;
Ok(())
}
@ -383,6 +384,139 @@ impl Database {
Ok(resp)
}
/// check if event type exists
async fn event_type_exists(&self, event: &Event) -> ServiceResult<bool> {
let res = sqlx::query!(
"SELECT EXISTS (SELECT 1 from librepages_deploy_event_type WHERE name = $1)",
event.name,
)
.fetch_one(&self.pool)
.await
.map_err(map_register_err)?;
let mut resp = false;
if let Some(x) = res.exists {
resp = x;
}
Ok(resp)
}
async fn create_event_type(&self) -> ServiceResult<()> {
for e in EVENTS {
if !self.event_type_exists(&e).await? {
sqlx::query!(
"INSERT INTO librepages_deploy_event_type
(name) VALUES ($1);",
e.name
)
.execute(&self.pool)
.await
.map_err(map_register_err)?;
}
}
Ok(())
}
pub async fn log_event(&self, hostname: &str, event: &Event) -> ServiceResult<Uuid> {
let now = now_unix_time_stamp();
let uuid = Uuid::new_v4();
sqlx::query!(
"INSERT INTO librepages_site_deploy_events
(event_type, time, site, pub_id) VALUES (
(SELECT iD from librepages_deploy_event_type WHERE name = $1),
$2,
(SELECT ID from librepages_sites WHERE hostname = $3),
$4
);
",
event.name,
&now,
hostname,
uuid,
)
.execute(&self.pool)
.await
.map_err(map_register_err)?;
Ok(uuid)
}
pub async fn get_event(
&self,
hostname: &str,
event_id: &Uuid,
) -> ServiceResult<LibrePagesEvent> {
let event = sqlx::query_as!(
InnerLibrepagesEvent,
"SELECT
librepages_deploy_event_type.name,
librepages_site_deploy_events.time,
librepages_site_deploy_events.pub_id
FROM
librepages_site_deploy_events
INNER JOIN librepages_deploy_event_type ON
librepages_deploy_event_type.ID = librepages_site_deploy_events.event_type
WHERE
librepages_site_deploy_events.site = (
SELECT ID FROM librepages_sites WHERE hostname = $1
)
AND
librepages_site_deploy_events.pub_id = $2
",
hostname,
event_id,
)
.fetch_one(&self.pool)
.await
.map_err(|e| map_row_not_found_err(e, ServiceError::AccountNotFound))?;
Ok(LibrePagesEvent {
id: event.pub_id,
time: event.time,
event_type: Event::from_str(&event.name).unwrap(),
site: hostname.to_owned(),
})
}
pub async fn list_all_site_events(
&self,
hostname: &str,
) -> ServiceResult<Vec<LibrePagesEvent>> {
let mut inner_events = sqlx::query_as!(
InnerLibrepagesEvent,
"SELECT
librepages_deploy_event_type.name,
librepages_site_deploy_events.time,
librepages_site_deploy_events.pub_id
FROM
librepages_site_deploy_events
INNER JOIN librepages_deploy_event_type ON
librepages_deploy_event_type.ID = librepages_site_deploy_events.event_type
WHERE
librepages_site_deploy_events.site = (
SELECT ID FROM librepages_sites WHERE hostname = $1
);
",
hostname,
)
.fetch_all(&self.pool)
.await
.map_err(|e| map_row_not_found_err(e, ServiceError::AccountNotFound))?;
let mut events = Vec::with_capacity(inner_events.len());
for e in inner_events.drain(0..) {
events.push(LibrePagesEvent {
id: e.pub_id,
time: e.time,
event_type: Event::from_str(&e.name).unwrap(),
site: hostname.to_owned(),
})
}
Ok(events)
}
}
struct InnerSite {
site_secret: String,
@ -451,6 +585,40 @@ pub struct NameHash {
pub hash: String,
}
#[derive(Deserialize, Serialize, Clone, Debug, Eq, PartialEq)]
pub struct Event {
pub name: &'static str,
}
impl Event {
const fn new(name: &'static str) -> Self {
Self { name }
}
pub fn from_str(name: &str) -> Option<Event> {
EVENTS.into_iter().find(|e| e.name == name)
}
}
pub const EVENT_TYPE_CREATE: Event = Event::new("site.event.create");
pub const EVENT_TYPE_UPDATE: Event = Event::new("site.event.update");
pub const EVENT_TYPE_DELETE: Event = Event::new("site.event.delete");
pub const EVENTS: [Event; 3] = [EVENT_TYPE_DELETE, EVENT_TYPE_DELETE, EVENT_TYPE_CREATE];
struct InnerLibrepagesEvent {
name: String,
time: OffsetDateTime,
pub_id: Uuid,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LibrePagesEvent {
pub event_type: Event,
pub time: OffsetDateTime,
pub site: String,
pub id: Uuid,
}
fn now_unix_time_stamp() -> OffsetDateTime {
OffsetDateTime::now_utc()
}
@ -637,6 +805,13 @@ mod tests {
const PASSWORD: &str = "pasdfasdfasdfadf";
db.migrate().await.unwrap();
// check if events are created
for e in EVENTS {
println!("Testing event type exists {}", e.name);
assert!(db.event_type_exists(&e).await.unwrap());
}
let p = super::Register {
username: NAME,
email: EMAIL,
@ -685,6 +860,21 @@ mod tests {
assert_eq!(db_sites.len(), 1);
assert_eq!(db_sites, vec![site.clone()]);
// add event to site
let event_id = db
.log_event(&site.hostname, &EVENT_TYPE_CREATE)
.await
.unwrap();
let event = db.get_event(&site.hostname, &event_id).await.unwrap();
assert_eq!(event.id, event_id);
assert_eq!(event.event_type, EVENT_TYPE_CREATE);
assert_eq!(event.site, site.hostname);
assert_eq!(
db.list_all_site_events(&site.hostname).await.unwrap(),
vec![event]
);
// delete site
db.delete_site(p.username, &site.hostname).await.unwrap();