Commented and Refactored codeMade ids in the database i64 values to increase available id spaceMoved some structs and enums from main.rs to models.rsCommented structs and functions
This commit is contained in:
parent
183da5e38c
commit
da0f1ddf2d
|
|
@ -591,6 +591,18 @@ dependencies = [
|
|||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-sync-rustls"
|
||||
version = "0.3.0-rc.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d1a443a90413a118ac6739e024f6a5180aa3b3f43f7de65f9d388a961cff19b"
|
||||
dependencies = [
|
||||
"hyper",
|
||||
"rustls",
|
||||
"webpki",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.60"
|
||||
|
|
@ -1150,6 +1162,18 @@ dependencies = [
|
|||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c4db68a2e35f3497146b7e4563df7d4773a2433230c5e4b448328e31740458a"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rocket"
|
||||
version = "0.4.11"
|
||||
|
|
@ -1210,9 +1234,11 @@ checksum = "2bf9cbd128e1f321a2d0bebd2b7cf0aafd89ca43edf69e49b56a5c46e48eb19f"
|
|||
dependencies = [
|
||||
"cookie",
|
||||
"hyper",
|
||||
"hyper-sync-rustls",
|
||||
"indexmap",
|
||||
"pear",
|
||||
"percent-encoding 1.0.1",
|
||||
"rustls",
|
||||
"smallvec",
|
||||
"state",
|
||||
"time",
|
||||
|
|
@ -1225,6 +1251,20 @@ version = "0.1.23"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b7891791343c75b73ed9a18cadcafd8c8563d11a88ebe2d87f5b8a3182654d9"
|
||||
dependencies = [
|
||||
"base64 0.9.3",
|
||||
"log 0.4.20",
|
||||
"ring",
|
||||
"sct",
|
||||
"untrusted",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.17"
|
||||
|
|
@ -1252,6 +1292,16 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb8f61f9e6eadd062a71c380043d28036304a4706b3c4dd001ff3387ed00745a"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.197"
|
||||
|
|
@ -1611,6 +1661,12 @@ dependencies = [
|
|||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "1.7.2"
|
||||
|
|
@ -1738,6 +1794,26 @@ version = "0.2.91"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17d7967316d8411ca3b01821ee6c332bde138ba4363becdb492f12e514daa17f"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85d1f408918fd590908a70d36b7ac388db2edc221470333e4d6e5b598e44cabf"
|
||||
dependencies = [
|
||||
"untrusted",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@ edition = "2021"
|
|||
chrono = "0.4.34"
|
||||
diesel = { version = "2.1.4", features = ["postgres", "chrono"] }
|
||||
dotenvy = "0.15.7"
|
||||
rocket = "0.4.10"
|
||||
rocket = { version = "0.4.11", features = ["tls"] }
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
serde_json = "1.0.114"
|
||||
tokio = { version = "1.36.0", features = ["full"] }
|
||||
|
||||
[dependencies.rocket_contrib]
|
||||
version = "0.4.10"
|
||||
version = "0.4.11"
|
||||
features = ["handlebars_templates", "tera_templates"]
|
||||
|
||||
[dependencies.uuid]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
[development]
|
||||
address = "0.0.0.0"
|
||||
port = 8000
|
||||
keep_alive = 5
|
||||
read_timeout = 5
|
||||
write_timeout = 5
|
||||
log = "normal"
|
||||
limits = { forms = 32768 }
|
||||
|
||||
[staging]
|
||||
address = "0.0.0.0"
|
||||
port = 8000
|
||||
keep_alive = 5
|
||||
read_timeout = 5
|
||||
write_timeout = 5
|
||||
log = "normal"
|
||||
limits = { forms = 32768 }
|
||||
|
||||
[production]
|
||||
address = "0.0.0.0"
|
||||
port = 80
|
||||
keep_alive = 5
|
||||
read_timeout = 5
|
||||
write_timeout = 5
|
||||
log = "critical"
|
||||
limits = { forms = 32768 }
|
||||
template_dir = "/templates/"
|
||||
|
|
@ -1,21 +1,21 @@
|
|||
-- Your SQL goes here
|
||||
CREATE TABLE IF NOT EXISTS clients
|
||||
(
|
||||
id SERIAL PRIMARY KEY,
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
ip TEXT NOT NULL,
|
||||
mirror_id TEXT NOT NULL
|
||||
client_id TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ownership
|
||||
(
|
||||
id SERIAL PRIMARY KEY,
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
owner_id TEXT NOT NULL,
|
||||
client_id TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS history
|
||||
(
|
||||
id SERIAL PRIMARY KEY,
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
client_id TEXT NOT NULL,
|
||||
request_type TEXT NOT NULL,
|
||||
timestamp TIMESTAMP NOT NULL
|
||||
|
|
@ -23,9 +23,9 @@ CREATE TABLE IF NOT EXISTS history
|
|||
|
||||
CREATE TABLE IF NOT EXISTS pair_records
|
||||
(
|
||||
id SERIAL PRIMARY KEY,
|
||||
history_id INTEGER NOT NULL,
|
||||
pair_type INTEGER NOT NULL,
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
history_id BIGINT NOT NULL,
|
||||
pair_type SMALLINT NOT NULL,
|
||||
key TEXT NOT NULL,
|
||||
value TEXT NOT NULL
|
||||
);
|
||||
39
src/lib.rs
39
src/lib.rs
|
|
@ -3,16 +3,23 @@ pub mod schema;
|
|||
|
||||
use chrono::offset::Utc;
|
||||
use diesel::prelude::*;
|
||||
use std::env;
|
||||
use dotenvy::dotenv;
|
||||
use models::{
|
||||
Client, NewClient, NewHistoryRecord, NewPairRecord
|
||||
Client, NewClient, NewHistoryRecord, NewPairRecord, PairType
|
||||
};
|
||||
use schema::{
|
||||
clients, history, pair_records
|
||||
};
|
||||
use std::env;
|
||||
|
||||
/// Establishes diesel Postgres connection that can be used to iteract with the database
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// let connection = establish_connection();
|
||||
/// ```
|
||||
pub fn establish_connection() -> PgConnection {
|
||||
|
||||
dotenv().ok();
|
||||
|
||||
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||
|
|
@ -20,17 +27,30 @@ pub fn establish_connection() -> PgConnection {
|
|||
.unwrap_or_else(|_| panic!("Error connecting to {}", database_url))
|
||||
}
|
||||
|
||||
pub fn create_client(conn: &mut PgConnection, ip: &str, mirror_id: &str) -> usize {
|
||||
let new_client = NewClient {ip: &ip, mirror_id: &mirror_id};
|
||||
/// Used to create a new client in the database. You need to pass a connection, the ip and client_id
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// let connection = establish_connection();
|
||||
///
|
||||
/// create_client(connection, "192.168.0.1", "195222-222-123123");
|
||||
/// ```
|
||||
///
|
||||
/// create_client returns a value of usize which represents the number of entries created
|
||||
pub fn create_client(conn: &mut PgConnection, ip: &str, client_id: &str) -> usize {
|
||||
let new_client = NewClient {ip: &ip, client_id: &client_id};
|
||||
|
||||
diesel::insert_into(clients::table)
|
||||
.values(&new_client)
|
||||
.returning(Client::as_returning())
|
||||
.execute(conn)
|
||||
.expect("Error")
|
||||
.expect("Error: Couldn't create a new client in the database.")
|
||||
}
|
||||
|
||||
pub fn create_history_record(conn: &mut PgConnection, client_id: &str, request_type: &str, ) -> i32 {
|
||||
/// Creates a new history record in the database. A history record is the initial record to store a
|
||||
/// request that is sent to the application.
|
||||
/// The history record has a client_id, request_type, and a timestamp.
|
||||
pub fn create_history_record(conn: &mut PgConnection, client_id: &str, request_type: &str, ) -> i64 {
|
||||
let new_history_record = NewHistoryRecord {
|
||||
client_id: &client_id,
|
||||
request_type: request_type,
|
||||
|
|
@ -44,8 +64,11 @@ pub fn create_history_record(conn: &mut PgConnection, client_id: &str, request_t
|
|||
.get_result(conn).unwrap()
|
||||
}
|
||||
|
||||
pub fn create_pair_record(conn: &mut PgConnection, history_id: i32, pair_type: i32, key: &str, value: &str) -> usize {
|
||||
let new_pair_record = NewPairRecord {history_id: history_id, pair_type: pair_type, key: &key, value: &value};
|
||||
/// Creates a new pair record in the database.
|
||||
/// Pass the history_id that this pair will be associated, the PairType, key and value of the pair
|
||||
/// pair_type is automatically casted as i32 type
|
||||
pub fn create_pair_record(conn: &mut PgConnection, history_id: i64, pair_type: PairType, key: &str, value: &str) -> usize {
|
||||
let new_pair_record = NewPairRecord {history_id: history_id, pair_type: pair_type as i16, key: &key, value: &value};
|
||||
|
||||
diesel::insert_into(pair_records::table)
|
||||
.values(&new_pair_record)
|
||||
|
|
|
|||
205
src/main.rs
205
src/main.rs
|
|
@ -16,20 +16,6 @@ use request_mirror::models::*;
|
|||
use diesel::prelude::*;
|
||||
use request_mirror::*;
|
||||
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
enum PairType {
|
||||
Header,
|
||||
Cookie,
|
||||
Query,
|
||||
Body
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
struct Pair {
|
||||
key: String,
|
||||
value: String
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
struct RequestInfo {
|
||||
header: Vec<Pair>,
|
||||
|
|
@ -37,17 +23,12 @@ struct RequestInfo {
|
|||
query: Vec<Pair>
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
struct RequestBody(String);
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ApiError {
|
||||
}
|
||||
#[derive(Serialize)]
|
||||
|
||||
struct ErrorContext {
|
||||
error_msg: String
|
||||
}
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
struct RequestBody(String);
|
||||
|
||||
// Always use a limit to prevent DoS attacks.
|
||||
const LIMIT: u64 = 256;
|
||||
|
|
@ -55,15 +36,16 @@ const LIMIT: u64 = 256;
|
|||
impl<'a, 'r> FromRequest<'a, 'r> for RequestInfo {
|
||||
type Error = ApiError;
|
||||
|
||||
/// Used for parsing request information and making it available for request functions to access
|
||||
/// Also handles creating cookies when the client doesn't send one in the request
|
||||
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
|
||||
let mut req_cookies = request.cookies();
|
||||
|
||||
for row in req_cookies.iter() {
|
||||
println!("{}: {}", row.name(), row.value());
|
||||
}
|
||||
// Initially set cookie
|
||||
if req_cookies.get(&"mirror-id").is_none() {
|
||||
|
||||
// When the client doesn't send the mirror-id cookie, from_request will create
|
||||
// one in the database and send it back to the client.
|
||||
let new_uuid = Uuid::new_v4().to_string();
|
||||
|
||||
println!("Creating new cookie");
|
||||
|
|
@ -78,9 +60,12 @@ impl<'a, 'r> FromRequest<'a, 'r> for RequestInfo {
|
|||
|
||||
let connection = &mut establish_connection();
|
||||
|
||||
// Creates a new client record in the database
|
||||
create_client(connection, &address, &new_uuid);
|
||||
}
|
||||
|
||||
// Compile vector of Pair structs with each header, cookie and query param that is coming
|
||||
// from the client
|
||||
let mut header: Vec<Pair> = vec![];
|
||||
let mut cookies: Vec<Pair> = vec![];
|
||||
let mut query: Vec<Pair> = vec![];
|
||||
|
|
@ -111,6 +96,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for RequestInfo {
|
|||
}
|
||||
}
|
||||
|
||||
// Send the Request Info as successful outcome
|
||||
Outcome::Success(RequestInfo {
|
||||
header: header,
|
||||
cookies: cookies,
|
||||
|
|
@ -122,6 +108,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for RequestInfo {
|
|||
impl FromDataSimple for RequestBody {
|
||||
type Error = String;
|
||||
|
||||
/// Used to extract the body of a request and make it available to request functions
|
||||
fn from_data(_req: &Request, data: Data) -> data::Outcome<Self, String> {
|
||||
// Read the data into a String.
|
||||
let mut string = String::new();
|
||||
|
|
@ -134,11 +121,10 @@ impl FromDataSimple for RequestBody {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the index template
|
||||
#[get("/")]
|
||||
fn index(_info: RequestInfo) -> Template {
|
||||
|
||||
//Redirect::to("/test")
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Context{
|
||||
}
|
||||
|
|
@ -146,37 +132,38 @@ fn index(_info: RequestInfo) -> Template {
|
|||
Template::render("index", Context {})
|
||||
}
|
||||
|
||||
/// Processes /test get request and responds with some of the contents of the request
|
||||
/// This function also stores information from the request in the database
|
||||
#[get("/test")]
|
||||
fn test_get(request: RequestInfo, cookies: Cookies) -> Template {
|
||||
|
||||
println!("{request:?}");
|
||||
let client_id = cookies.get("mirror-id");
|
||||
|
||||
let cookie_id = cookies.get("mirror-id");
|
||||
|
||||
if cookie_id.is_some() {
|
||||
// If the cookie exists, create new database records for this request
|
||||
if client_id.is_some() {
|
||||
let connection = &mut establish_connection();
|
||||
let history_id = create_history_record(connection, cookie_id.unwrap().value(), "Get");
|
||||
|
||||
println!("Creating header Records for {history_id}");
|
||||
// Create new history record and retrieve new history_id
|
||||
let history_id = create_history_record(connection, client_id.unwrap().value(), "Get");
|
||||
|
||||
// Create header pair records
|
||||
for row in &request.header {
|
||||
create_pair_record(connection, history_id, PairType::Header as i32, &row.key, &row.value);
|
||||
create_pair_record(connection, history_id, PairType::Header, &row.key, &row.value);
|
||||
}
|
||||
|
||||
println!("Creating cookie Records for {history_id}");
|
||||
|
||||
// Create cookie pair records
|
||||
for row in &request.cookies {
|
||||
create_pair_record(connection, history_id, PairType::Cookie as i32, &row.key, &row.value);
|
||||
create_pair_record(connection, history_id, PairType::Cookie, &row.key, &row.value);
|
||||
}
|
||||
|
||||
println!("Creating query Records for {history_id}");
|
||||
|
||||
// Create query pair records
|
||||
for row in &request.query {
|
||||
create_pair_record(connection, history_id, PairType::Query as i32, &row.key, &row.value);
|
||||
create_pair_record(connection, history_id, PairType::Query, &row.key, &row.value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Define context for the template
|
||||
#[derive(Serialize)]
|
||||
struct Context<'a> {
|
||||
request_type: &'a str,
|
||||
|
|
@ -185,6 +172,7 @@ fn test_get(request: RequestInfo, cookies: Cookies) -> Template {
|
|||
query: Vec<Pair>,
|
||||
}
|
||||
|
||||
// Compile context
|
||||
let context = Context {
|
||||
request_type: "Get",
|
||||
header: request.header,
|
||||
|
|
@ -192,45 +180,46 @@ fn test_get(request: RequestInfo, cookies: Cookies) -> Template {
|
|||
query: request.query
|
||||
};
|
||||
|
||||
// Render request_details
|
||||
Template::render("request_details", context)
|
||||
}
|
||||
|
||||
/// Processes /test post request and responds with some of the contents of the request
|
||||
/// This function also stores information from the request in the database
|
||||
#[post("/test", data = "<body>")]
|
||||
fn test_post(body: RequestBody, request: RequestInfo, cookies: Cookies) -> Template {
|
||||
|
||||
println!("{request:?}");
|
||||
let client_id = cookies.get("mirror-id");
|
||||
|
||||
println!("Input: {}", body.0);
|
||||
|
||||
let cookie_id = cookies.get("mirror-id");
|
||||
|
||||
if cookie_id.is_some() {
|
||||
// If the cookie exists, create new database records for this request
|
||||
if client_id.is_some() {
|
||||
let connection = &mut establish_connection();
|
||||
let history_id = create_history_record(connection, cookie_id.unwrap().value(), "Post");
|
||||
|
||||
println!("Creating header Records for {history_id}");
|
||||
// Create new history record and retrieve new history_id
|
||||
let history_id = create_history_record(connection, client_id.unwrap().value(), "Post");
|
||||
|
||||
// Create header pair records
|
||||
for row in &request.header {
|
||||
create_pair_record(connection, history_id, PairType::Header as i32, &row.key, &row.value);
|
||||
create_pair_record(connection, history_id, PairType::Header, &row.key, &row.value);
|
||||
}
|
||||
|
||||
println!("Creating cookie Records for {history_id}");
|
||||
|
||||
// Create cookie pair records
|
||||
for row in &request.cookies {
|
||||
create_pair_record(connection, history_id, PairType::Cookie as i32, &row.key, &row.value);
|
||||
create_pair_record(connection, history_id, PairType::Cookie, &row.key, &row.value);
|
||||
}
|
||||
|
||||
println!("Creating query Records for {history_id}");
|
||||
|
||||
// Create query pair records
|
||||
for row in &request.query {
|
||||
create_pair_record(connection, history_id, PairType::Query as i32, &row.key, &row.value);
|
||||
create_pair_record(connection, history_id, PairType::Query, &row.key, &row.value);
|
||||
}
|
||||
|
||||
// Create a pair record for body of the request
|
||||
println!("Creating body Records for {history_id}");
|
||||
create_pair_record(connection, history_id, PairType::Body as i32, "body", &body.0.clone());
|
||||
create_pair_record(connection, history_id, PairType::Body, "body", &body.0.clone());
|
||||
|
||||
}
|
||||
|
||||
// Define context for the template
|
||||
#[derive(Serialize)]
|
||||
struct Context<'a> {
|
||||
request_type: &'a str,
|
||||
|
|
@ -240,6 +229,7 @@ fn test_post(body: RequestBody, request: RequestInfo, cookies: Cookies) -> Templ
|
|||
body: String
|
||||
}
|
||||
|
||||
// Compile context
|
||||
let context = Context {
|
||||
request_type: "Post",
|
||||
header: request.header,
|
||||
|
|
@ -248,113 +238,122 @@ fn test_post(body: RequestBody, request: RequestInfo, cookies: Cookies) -> Templ
|
|||
body: body.0
|
||||
};
|
||||
|
||||
// Render request_details Template
|
||||
Template::render("request_details", context)
|
||||
}
|
||||
|
||||
/// Request function that returns a history of requests that the current client has made
|
||||
/// The user can click a history_id and view the request itself
|
||||
#[get("/history")]
|
||||
fn history_req(cookies: Cookies) -> Template {
|
||||
|
||||
let cookie_id = cookies.get("mirror-id");
|
||||
|
||||
let cookie_id = cookie_id.unwrap().value();
|
||||
println!("Client ID: {}", cookie_id);
|
||||
// Get the client_id from the cookies
|
||||
let client_id = cookies.get("mirror-id").unwrap().value();
|
||||
|
||||
let connection = &mut establish_connection();
|
||||
|
||||
// Query the database for history records
|
||||
let results = history::dsl::history
|
||||
.filter(history::client_id.eq(cookie_id.to_string()))
|
||||
.filter(history::client_id.eq(client_id.to_string()))
|
||||
.select(HistoryRecord::as_select())
|
||||
.load(connection)
|
||||
.expect("Error loading clients");
|
||||
|
||||
for record in results.iter() {
|
||||
println!("{:?}", record);
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct History {
|
||||
pub id: i32,
|
||||
pub client_id: String,
|
||||
pub request_type: String,
|
||||
pub timestamp: String
|
||||
}
|
||||
|
||||
/// Template Context
|
||||
#[derive(Serialize)]
|
||||
struct Context {
|
||||
history_records: Vec<History>
|
||||
}
|
||||
|
||||
// New vector to store converted History structs
|
||||
let mut history_records: Vec<History> = Vec::new();
|
||||
|
||||
// For each HistoryRecord, create a new History struct
|
||||
for history_rec in results {
|
||||
history_records.push(
|
||||
History {
|
||||
id: history_rec.id,
|
||||
client_id: history_rec.client_id,
|
||||
request_type: history_rec.request_type,
|
||||
timestamp: history_rec.timestamp.format("%Y-%m-%d %H:%M:%S UTC").to_string()
|
||||
timestamp: history_rec.timestamp.format("%Y-%m-%d %H:%M:%S UTC").to_string() // convert to string
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Template::render("history", Context{
|
||||
// Render the template
|
||||
Template::render(
|
||||
"history",
|
||||
Context{
|
||||
history_records: history_records
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
/// The history_details request function will provide a webpage that a user can view the contents of a
|
||||
/// request that was recorded to the database.
|
||||
/// This includes the body of a post request, headers, cookies and query parameters.
|
||||
#[get("/history/<history_id>")]
|
||||
fn history_details(history_id: i32, cookies: Cookies) -> Template {
|
||||
fn history_details(history_id: i64, cookies: Cookies) -> Template {
|
||||
|
||||
let cookie_id = cookies.get("mirror-id");
|
||||
|
||||
let cookie_id = cookie_id.unwrap().value();
|
||||
println!("Client ID: {}", cookie_id);
|
||||
let client_id = cookies.get("mirror-id").unwrap().value();
|
||||
|
||||
let connection = &mut establish_connection();
|
||||
|
||||
// Query history table where the history id is what was in the route. Also filter client_ids.
|
||||
// If this record has a different client_id nothing will be returned
|
||||
let results: Vec<HistoryRecord> = history::dsl::history
|
||||
.filter(history::id.eq(&history_id))
|
||||
.filter(history::client_id.eq(cookie_id.to_string()))
|
||||
.filter(history::client_id.eq(client_id.to_string()))
|
||||
.select(HistoryRecord::as_select())
|
||||
.load(connection)
|
||||
.expect("Error loading history records");
|
||||
|
||||
if results.len() <= 0 {
|
||||
|
||||
// Error
|
||||
// Error when no results are returned
|
||||
return Template::render(
|
||||
"error",
|
||||
ErrorContext{ error_msg: "No Results Found. You may be unauthorized...".to_string() }
|
||||
ErrorContext{ error_msg: "No Results Found. You may be not be authorized to view this record...".to_string() }
|
||||
);
|
||||
}
|
||||
|
||||
let connection = &mut establish_connection();
|
||||
|
||||
// Query all Pair Records for this history_id
|
||||
let pairs: Vec<PairRecord> = pair_records::dsl::pair_records
|
||||
.filter(pair_records::history_id.eq(history_id))
|
||||
.select(PairRecord::as_select())
|
||||
.load(connection)
|
||||
.expect("Error loading history records");
|
||||
|
||||
let body: String = match &pairs.iter().filter(|res| res.pair_type == PairType::Body as i32).last() {
|
||||
Some(pair) => pair.value.clone(),
|
||||
_ => "".to_string()
|
||||
// Filter Body Pair Records for this history_id
|
||||
let body: String = match &pairs.iter()
|
||||
.filter(|res| res.pair_type == PairType::Body as i16).last() // Filter and get last record
|
||||
{
|
||||
Some(pair) => pair.value.clone().to_string(), // Return pair value if Some
|
||||
_ => "".to_string() // Return empty string if None
|
||||
};
|
||||
|
||||
// Collect Header Pair Records into a vector
|
||||
let header: Vec<&PairRecord> = pairs.iter()
|
||||
.filter(|res: &&PairRecord| res.pair_type == PairType::Header as i32)
|
||||
.filter(|res: &&PairRecord| res.pair_type == PairType::Header as i16)
|
||||
.map(|res: &PairRecord| res)
|
||||
.collect();
|
||||
|
||||
// Collect Cookie Pair Records into a vector
|
||||
let cookies: Vec<&PairRecord> = pairs.iter()
|
||||
.filter(|res: &&PairRecord| res.pair_type == PairType::Cookie as i32)
|
||||
.filter(|res: &&PairRecord| res.pair_type == PairType::Cookie as i16)
|
||||
.map(|res: &PairRecord| res)
|
||||
.collect();
|
||||
|
||||
// Collect Query Pair Records into a vector
|
||||
let query: Vec<&PairRecord> = pairs.iter()
|
||||
.filter(|res: &&PairRecord| res.pair_type == PairType::Query as i32)
|
||||
.filter(|res: &&PairRecord| res.pair_type == PairType::Query as i16)
|
||||
.map(|res: &PairRecord| res)
|
||||
.collect();
|
||||
|
||||
/// history_details specific context
|
||||
#[derive(Serialize)]
|
||||
struct Context<'a> {
|
||||
request_type: String,
|
||||
|
|
@ -364,11 +363,12 @@ fn history_details(history_id: i32, cookies: Cookies) -> Template {
|
|||
query: Vec<&'a PairRecord>,
|
||||
}
|
||||
|
||||
// Render request_details with data taken from the database
|
||||
Template::render(
|
||||
"request_details",
|
||||
Context{
|
||||
request_type: results[0].request_type.clone(),
|
||||
body: body.to_string(),
|
||||
request_type: "Previous ".to_owned() + &results[0].request_type.clone(),
|
||||
body: body,
|
||||
header: header,
|
||||
cookies: cookies,
|
||||
query: query
|
||||
|
|
@ -377,17 +377,30 @@ fn history_details(history_id: i32, cookies: Cookies) -> Template {
|
|||
|
||||
}
|
||||
|
||||
|
||||
/// 404 Response
|
||||
#[catch(404)]
|
||||
fn not_found(req: &Request) -> String {
|
||||
print!("{}", req);
|
||||
format!("Oh no! We couldn't find the requested path '{}'", req.uri())
|
||||
fn not_found(req: &Request) -> Template {
|
||||
Template::render(
|
||||
"error",
|
||||
ErrorContext{
|
||||
error_msg: format!("Oh no! We couldn't find the requested path '{}'", req.uri())
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
rocket::ignite()
|
||||
.register(catchers![not_found])
|
||||
.mount("/", routes![index, test_get, test_post, history_req, history_details])
|
||||
.mount(
|
||||
"/",
|
||||
routes![
|
||||
index,
|
||||
test_get,
|
||||
test_post,
|
||||
history_req,
|
||||
history_details
|
||||
]
|
||||
)
|
||||
.attach(Template::fairing())
|
||||
.launch();
|
||||
}
|
||||
|
|
@ -8,32 +8,48 @@ use crate::schema::{
|
|||
};
|
||||
use diesel::pg::Pg;
|
||||
|
||||
/// Client Record that keeps track of unique clients that have connected to this application
|
||||
/// These are used to keep track of who submitted what request that will be recorded
|
||||
#[derive(Queryable, Selectable, Debug)]
|
||||
#[diesel(table_name = clients)]
|
||||
#[diesel(check_for_backend(Pg))]
|
||||
pub struct Client {
|
||||
pub id: i32,
|
||||
pub id: i64,
|
||||
pub ip: String,
|
||||
pub mirror_id: String,
|
||||
pub client_id: String,
|
||||
}
|
||||
|
||||
/// Used to create a new Client Record
|
||||
#[derive(Insertable)]
|
||||
#[diesel(table_name = clients)]
|
||||
pub struct NewClient<'a> {
|
||||
pub ip: &'a str,
|
||||
pub mirror_id: &'a str,
|
||||
pub client_id: &'a str,
|
||||
}
|
||||
|
||||
/// History record that keeps track of each unique request that comes in to the /test route
|
||||
/// Any request is recorded here and the additional attributes are stored as a Pair Record
|
||||
#[derive(Queryable, Selectable, Debug)]
|
||||
#[diesel(table_name = history)]
|
||||
#[diesel(check_for_backend(Pg))]
|
||||
pub struct HistoryRecord {
|
||||
pub id: i32,
|
||||
pub id: i64,
|
||||
pub client_id: String,
|
||||
pub request_type: String,
|
||||
pub timestamp: NaiveDateTime
|
||||
}
|
||||
|
||||
/// History Struct used to represent HistoryRecord in a serializable way.
|
||||
/// This is intended to allow an instance to be used with a Handlebars Template
|
||||
#[derive(Serialize)]
|
||||
pub struct History {
|
||||
pub id: i64,
|
||||
pub client_id: String,
|
||||
pub request_type: String,
|
||||
pub timestamp: String
|
||||
}
|
||||
|
||||
/// Used to create a new History Record
|
||||
#[derive(Insertable)]
|
||||
#[diesel(table_name = history)]
|
||||
pub struct NewHistoryRecord<'a> {
|
||||
|
|
@ -42,22 +58,50 @@ pub struct NewHistoryRecord<'a> {
|
|||
pub timestamp: NaiveDateTime
|
||||
}
|
||||
|
||||
/// Pair Records are used to store attributes of a request. This includes a body record, header, query and cookie record.
|
||||
/// Each has a key and value.
|
||||
#[derive(Queryable, Selectable, Debug, Serialize)]
|
||||
#[diesel(table_name = pair_records)]
|
||||
#[diesel(check_for_backend(Pg))]
|
||||
pub struct PairRecord {
|
||||
pub id: i32,
|
||||
pub history_id: i32,
|
||||
pub pair_type: i32,
|
||||
pub id: i64,
|
||||
pub history_id: i64,
|
||||
pub pair_type: i16,
|
||||
pub key: String,
|
||||
pub value: String
|
||||
}
|
||||
|
||||
/// Used to create a new Pair Record
|
||||
#[derive(Insertable)]
|
||||
#[diesel(table_name = pair_records)]
|
||||
pub struct NewPairRecord<'a> {
|
||||
pub history_id: i32,
|
||||
pub pair_type: i32,
|
||||
pub history_id: i64,
|
||||
pub pair_type: i16,
|
||||
pub key: &'a str,
|
||||
pub value: &'a str
|
||||
}
|
||||
|
||||
/// Used to indicate the type of pair a Pair Record may be.
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
pub enum PairType {
|
||||
Header,
|
||||
Cookie,
|
||||
Query,
|
||||
Body
|
||||
}
|
||||
|
||||
/// Used to store a pair of key values
|
||||
/// This structure is used for storing different values in the database and
|
||||
/// rendering mustache templates with an array of key value pairs.
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
pub struct Pair {
|
||||
pub key: String,
|
||||
pub value: String
|
||||
}
|
||||
|
||||
/// Used when needing to return a template with an error message.
|
||||
/// Specify error_msg to display your custom message.
|
||||
#[derive(Serialize)]
|
||||
pub struct ErrorContext {
|
||||
pub error_msg: String
|
||||
}
|
||||
|
|
@ -2,15 +2,15 @@
|
|||
|
||||
diesel::table! {
|
||||
clients (id) {
|
||||
id -> Int4,
|
||||
id -> Int8,
|
||||
ip -> Text,
|
||||
mirror_id -> Text,
|
||||
client_id -> Text,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
history (id) {
|
||||
id -> Int4,
|
||||
id -> Int8,
|
||||
client_id -> Text,
|
||||
request_type -> Text,
|
||||
timestamp -> Timestamp,
|
||||
|
|
@ -19,7 +19,7 @@ diesel::table! {
|
|||
|
||||
diesel::table! {
|
||||
ownership (id) {
|
||||
id -> Int4,
|
||||
id -> Int8,
|
||||
owner_id -> Text,
|
||||
client_id -> Text,
|
||||
}
|
||||
|
|
@ -27,9 +27,9 @@ diesel::table! {
|
|||
|
||||
diesel::table! {
|
||||
pair_records (id) {
|
||||
id -> Int4,
|
||||
history_id -> Int4,
|
||||
pair_type -> Int4,
|
||||
id -> Int8,
|
||||
history_id -> Int8,
|
||||
pair_type -> Int2,
|
||||
key -> Text,
|
||||
value -> Text,
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue