diff --git a/Cargo.lock b/Cargo.lock index c709b38..051db96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 39b241e..57e44e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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] diff --git a/Rocket.toml b/Rocket.toml new file mode 100644 index 0000000..78c8efe --- /dev/null +++ b/Rocket.toml @@ -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/" \ No newline at end of file diff --git a/migrations/2024-03-02-000805_create_clients/up.sql b/migrations/2024-03-02-000805_create_clients/up.sql index 15d72b8..61c54ae 100644 --- a/migrations/2024-03-02-000805_create_clients/up.sql +++ b/migrations/2024-03-02-000805_create_clients/up.sql @@ -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 ); \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 5693a2a..8d742ba 100644 --- a/src/lib.rs +++ b/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) diff --git a/src/main.rs b/src/main.rs index 5ae2856..c1a71d8 100644 --- a/src/main.rs +++ b/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, @@ -37,17 +23,12 @@ struct RequestInfo { query: Vec } -#[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 { 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 = vec![]; let mut cookies: Vec = vec![]; let mut query: Vec = vec![]; @@ -111,7 +96,8 @@ impl<'a, 'r> FromRequest<'a, 'r> for RequestInfo { } } - Outcome::Success(RequestInfo{ + // Send the Request Info as successful outcome + Outcome::Success(RequestInfo { header: header, cookies: cookies, query: query @@ -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 { // 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, } + // 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 = "")] fn test_post(body: RequestBody, request: RequestInfo, cookies: Cookies) -> Template { - println!("{request:?}"); - - println!("Input: {}", body.0); - - let cookie_id = cookies.get("mirror-id"); - - if cookie_id.is_some() { + let client_id = cookies.get("mirror-id"); + + // 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 } + // New vector to store converted History structs let mut history_records: Vec = 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{ - history_records: history_records - }) + // 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/")] -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 = 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 = 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) - .map(|res: &PairRecord| res) - .collect(); - - let query: Vec<&PairRecord> = pairs.iter() - .filter(|res: &&PairRecord| res.pair_type == PairType::Query 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 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(); } \ No newline at end of file diff --git a/src/models.rs b/src/models.rs index 39830e2..b21ac78 100644 --- a/src/models.rs +++ b/src/models.rs @@ -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 } \ No newline at end of file diff --git a/src/schema.rs b/src/schema.rs index 725d2bd..d249f7f 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -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, }