Skip to content

Commit 8d25613

Browse files
committed
Expose request log via rpc
1 parent 61ac10c commit 8d25613

8 files changed

Lines changed: 159 additions & 5 deletions

File tree

src/db/log/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ fn get_migrations() -> Result<Vec<Migration>> {
108108
#[cfg(test)]
109109
pub mod test {
110110
use crate::db::log::run_migrations;
111+
use deadpool_sqlite::{Config, Hook, Runtime};
111112
use rusqlite::Connection;
112113

113114
pub fn conn() -> Connection {
@@ -117,6 +118,25 @@ pub mod test {
117118
conn
118119
}
119120

121+
pub fn pool() -> super::LogPool {
122+
let pool_size = std::thread::available_parallelism()
123+
.map(|n| n.get() * 2)
124+
.unwrap_or(8);
125+
let inner = Config::new(":memory:")
126+
.builder(Runtime::Tokio1)
127+
.unwrap()
128+
.max_size(pool_size)
129+
.post_create(Hook::Fn(Box::new(|conn, _| {
130+
let mut conn = conn.lock().unwrap();
131+
super::super::configure_connection(&mut conn);
132+
run_migrations(&mut conn).unwrap();
133+
Ok(())
134+
})))
135+
.build()
136+
.unwrap();
137+
super::LogPool::new(inner)
138+
}
139+
120140
#[test]
121141
fn migrations() {
122142
let mut conn = Connection::open_in_memory().unwrap();

src/db/log/request/blocking_queries.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,29 @@ pub fn insert(request: InsertArgs, conn: &Connection) -> Result<()> {
6262
Ok(())
6363
}
6464

65+
#[allow(dead_code)]
66+
pub fn select_latest(minutes: i64, conn: &Connection) -> Result<Vec<schema::Request>> {
67+
let sql = format!(
68+
r#"
69+
SELECT {projection}
70+
FROM {table}
71+
WHERE {date} > strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-{minutes} minutes')
72+
ORDER BY {date} DESC
73+
"#,
74+
projection = schema::Request::projection(),
75+
table = schema::TABLE_NAME,
76+
date = Columns::Date.as_str(),
77+
minutes = minutes,
78+
);
79+
let mut stmt = conn.prepare(&sql)?;
80+
let rows = stmt.query_map([], schema::Request::mapper())?;
81+
let mut requests = Vec::new();
82+
for row in rows {
83+
requests.push(row?);
84+
}
85+
Ok(requests)
86+
}
87+
6588
#[cfg(test)]
6689
mod test {
6790
use crate::db::log::request::blocking_queries::InsertArgs;
@@ -179,4 +202,29 @@ mod test {
179202

180203
Ok(())
181204
}
205+
206+
#[test]
207+
fn select_latest() -> crate::Result<()> {
208+
let conn = conn();
209+
210+
conn.execute(
211+
"INSERT INTO request (ip, path, response_code, processing_time_ns, date) VALUES ('10.0.0.1', '/api/v1/old', 200, 1000000, strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-2 minutes'))",
212+
[],
213+
)?;
214+
215+
conn.execute(
216+
"INSERT INTO request (ip, path, response_code, processing_time_ns, date) VALUES ('10.0.0.2', '/api/v1/recent', 200, 1000000, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))",
217+
[],
218+
)?;
219+
220+
let requests = super::select_latest(1, &conn)?;
221+
assert_eq!(requests.len(), 1);
222+
assert_eq!(requests[0].ip, "10.0.0.2");
223+
assert_eq!(requests[0].path, "/api/v1/recent");
224+
225+
let requests = super::select_latest(5, &conn)?;
226+
assert_eq!(requests.len(), 2);
227+
228+
Ok(())
229+
}
182230
}

src/db/log/request/queries.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::super::LogPool;
22
use super::blocking_queries;
33
use super::blocking_queries::InsertArgs;
4+
use crate::db::log::request::schema::Request;
45
use crate::Result;
56

67
pub async fn insert(args: InsertArgs, pool: &LogPool) -> Result<()> {
@@ -9,3 +10,11 @@ pub async fn insert(args: InsertArgs, pool: &LogPool) -> Result<()> {
910
.interact(move |conn| blocking_queries::insert(args, conn))
1011
.await?
1112
}
13+
14+
#[allow(dead_code)]
15+
pub async fn select_latest(minutes: i64, pool: &LogPool) -> Result<Vec<Request>> {
16+
pool.get()
17+
.await?
18+
.interact(move |conn| blocking_queries::select_latest(minutes, conn))
19+
.await?
20+
}

src/db/log/request/schema.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
pub const TABLE_NAME: &str = "request";
2-
31
use std::sync::OnceLock;
42

3+
pub const TABLE_NAME: &str = "request";
4+
55
pub enum Columns {
66
Id,
77
Date,
@@ -32,7 +32,6 @@ impl Columns {
3232
}
3333
}
3434

35-
#[allow(dead_code)]
3635
pub struct Request {
3736
pub id: i64,
3837
pub date: String,

src/rpc/handler.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
db::{self, main::conf::schema::Conf, main::user::schema::Role, main::MainPool},
2+
db::{self, log::LogPool, main::conf::schema::Conf, main::user::schema::Role, main::MainPool},
33
Result,
44
};
55
use actix_web::{
@@ -91,6 +91,8 @@ pub enum RpcMethod {
9191
SyncSubmittedPlaces,
9292
// Matrix
9393
SendMatrixMessage,
94+
// Debug
95+
GetRequestLog,
9496
}
9597

9698
impl Role {
@@ -271,6 +273,7 @@ pub async fn handle(
271273
req_body: String,
272274
pool: Data<MainPool>,
273275
conf: Data<Conf>,
276+
log_pool: Data<LogPool>,
274277
) -> Result<Json<RpcResponse>> {
275278
let headers = req.headers();
276279
let Ok(req) = serde_json::from_str::<Map<String, Value>>(&req_body) else {
@@ -556,6 +559,10 @@ pub async fn handle(
556559
serde_json::Value::Null,
557560
))
558561
}
562+
RpcMethod::GetRequestLog => RpcResponse::from(
563+
req.id.clone(),
564+
super::log::get_request_log::run(params(req.params)?, &log_pool).await?,
565+
),
559566
}?;
560567

561568
Ok(Json(res))
@@ -583,7 +590,9 @@ pub fn handle_rpc_error<B>(res: ServiceResponse<B>) -> actix_web::Result<ErrorHa
583590
#[cfg(test)]
584591
mod test {
585592
use super::*;
586-
use crate::{db::main::test::pool, service::overpass::OverpassElement};
593+
use crate::{
594+
db::log::test::pool as log_pool, db::main::test::pool, service::overpass::OverpassElement,
595+
};
587596
use actix_web::{
588597
http::{header, StatusCode},
589598
test,
@@ -598,11 +607,13 @@ mod test {
598607
let pool = pool();
599608
let conf = Conf::mock();
600609
let client: Option<Client> = None;
610+
let log_pool = log_pool();
601611
let app = test::init_service(
602612
App::new()
603613
.app_data(Data::new(pool))
604614
.app_data(Data::new(conf))
605615
.app_data(Data::new(client))
616+
.app_data(Data::new(log_pool))
606617
.service(scope("/").service(super::handle)),
607618
)
608619
.await;
@@ -624,11 +635,13 @@ mod test {
624635
let pool = pool();
625636
let conf = Conf::mock();
626637
let client: Option<Client> = None;
638+
let log_pool = log_pool();
627639
let app = test::init_service(
628640
App::new()
629641
.app_data(Data::new(pool))
630642
.app_data(Data::new(conf))
631643
.app_data(Data::new(client))
644+
.app_data(Data::new(log_pool))
632645
.service(scope("/").service(super::handle)),
633646
)
634647
.await;
@@ -651,11 +664,13 @@ mod test {
651664
db::main::element::queries::insert(OverpassElement::mock(1), &pool).await?;
652665
let conf = Conf::mock();
653666
let client: Option<Client> = None;
667+
let log_pool = log_pool();
654668
let app = test::init_service(
655669
App::new()
656670
.app_data(Data::new(pool))
657671
.app_data(Data::new(conf))
658672
.app_data(Data::new(client))
673+
.app_data(Data::new(log_pool))
659674
.service(scope("/").service(super::handle)),
660675
)
661676
.await;
@@ -680,11 +695,13 @@ mod test {
680695
let pool = pool();
681696
let conf = Conf::mock();
682697
let client: Option<Client> = None;
698+
let log_pool = log_pool();
683699
let app = test::init_service(
684700
App::new()
685701
.app_data(Data::new(pool))
686702
.app_data(Data::new(conf))
687703
.app_data(Data::new(client))
704+
.app_data(Data::new(log_pool))
688705
.service(scope("/").service(super::handle)),
689706
)
690707
.await;
@@ -709,11 +726,13 @@ mod test {
709726
let pool = pool();
710727
let conf = Conf::mock();
711728
let client: Option<Client> = None;
729+
let log_pool = log_pool();
712730
let app = test::init_service(
713731
App::new()
714732
.app_data(Data::new(pool))
715733
.app_data(Data::new(conf))
716734
.app_data(Data::new(client))
735+
.app_data(Data::new(log_pool))
717736
.service(scope("/").service(super::handle)),
718737
)
719738
.await;
@@ -746,11 +765,13 @@ mod test {
746765
.await?;
747766
let conf = Conf::mock();
748767
let client: Option<Client> = None;
768+
let log_pool = log_pool();
749769
let app = test::init_service(
750770
App::new()
751771
.app_data(Data::new(pool))
752772
.app_data(Data::new(conf))
753773
.app_data(Data::new(client))
774+
.app_data(Data::new(log_pool))
754775
.service(scope("/").service(super::handle)),
755776
)
756777
.await;
@@ -775,11 +796,13 @@ mod test {
775796
let pool = pool();
776797
let conf = Conf::mock();
777798
let client: Option<Client> = None;
799+
let log_pool = log_pool();
778800
let app = test::init_service(
779801
App::new()
780802
.app_data(Data::new(pool))
781803
.app_data(Data::new(conf))
782804
.app_data(Data::new(client))
805+
.app_data(Data::new(log_pool))
783806
.service(scope("/").service(super::handle)),
784807
)
785808
.await;

src/rpc/log/get_request_log.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
use crate::db::log::request::queries;
2+
use crate::db::log::LogPool;
3+
use crate::Result;
4+
use serde::{Deserialize, Serialize};
5+
6+
#[derive(Deserialize)]
7+
pub struct Params {
8+
#[serde(default = "default_minutes")]
9+
pub minutes: i64,
10+
}
11+
12+
fn default_minutes() -> i64 {
13+
1
14+
}
15+
16+
#[derive(Serialize)]
17+
pub struct Res {
18+
pub requests: Vec<Request>,
19+
}
20+
21+
#[derive(Serialize)]
22+
pub struct Request {
23+
pub id: i64,
24+
pub date: String,
25+
pub ip: String,
26+
pub user_agent: Option<String>,
27+
pub user_id: Option<i64>,
28+
pub path: String,
29+
pub query: Option<String>,
30+
pub body: Option<String>,
31+
pub response_code: i64,
32+
pub processing_time_ns: i64,
33+
}
34+
35+
pub async fn run(params: Params, pool: &LogPool) -> Result<Res> {
36+
let requests = queries::select_latest(params.minutes, pool).await?;
37+
let requests = requests
38+
.into_iter()
39+
.map(|r| Request {
40+
id: r.id,
41+
date: r.date,
42+
ip: r.ip,
43+
user_agent: r.user_agent,
44+
user_id: r.user_id,
45+
path: r.path,
46+
query: r.query,
47+
body: r.body,
48+
response_code: r.response_code,
49+
processing_time_ns: r.processing_time_ns,
50+
})
51+
.collect();
52+
Ok(Res { requests })
53+
}

src/rpc/log/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod get_request_log;

src/rpc/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub mod get_user_activity;
2121
pub mod handler;
2222
pub mod import;
2323
pub mod invoice;
24+
pub mod log;
2425
pub mod matrix;
2526
pub mod paywall_add_element_comment;
2627
pub mod paywall_boost_element;

0 commit comments

Comments
 (0)