Merge pull request #4 from RaspberryProgramming/ownership_registraton

Added ownership registration page.
This commit is contained in:
Camerin Figueroa 2024-07-10 13:45:42 -04:00 committed by GitHub
commit 1d97eb1b78
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 273 additions and 85 deletions

51
Cargo.lock generated
View File

@ -65,9 +65,9 @@ dependencies = [
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.80" version = "0.1.81"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -169,9 +169,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.104" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490" checksum = "eaff6f8ce506b9773fa786672d63fc7a191ffea1be33f72bbd4aeacefca9ffc8"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@ -246,9 +246,9 @@ dependencies = [
[[package]] [[package]]
name = "darling" name = "darling"
version = "0.20.9" version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"darling_macro", "darling_macro",
@ -256,9 +256,9 @@ dependencies = [
[[package]] [[package]]
name = "darling_core" name = "darling_core"
version = "0.20.9" version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
dependencies = [ dependencies = [
"fnv", "fnv",
"ident_case", "ident_case",
@ -270,9 +270,9 @@ dependencies = [
[[package]] [[package]]
name = "darling_macro" name = "darling_macro"
version = "0.20.9" version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
@ -674,9 +674,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "0.14.29" version = "0.14.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
@ -1277,11 +1277,12 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]] [[package]]
name = "request-mirror" name = "request-mirror"
version = "0.1.1" version = "0.1.2"
dependencies = [ dependencies = [
"chrono", "chrono",
"diesel", "diesel",
"dotenvy", "dotenvy",
"regex",
"rocket", "rocket",
"rocket_dyn_templates", "rocket_dyn_templates",
"serde", "serde",
@ -1497,18 +1498,18 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.203" version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.203" version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1621,9 +1622,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.68" version = "2.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1790,9 +1791,9 @@ dependencies = [
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.22.14" version = "0.22.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde", "serde",
@ -1925,9 +1926,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.9.1" version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
dependencies = [ dependencies = [
"getrandom", "getrandom",
"rand", "rand",
@ -1936,9 +1937,9 @@ dependencies = [
[[package]] [[package]]
name = "uuid-macro-internal" name = "uuid-macro-internal"
version = "1.9.1" version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3ff64d5cde1e2cb5268bdb497235b6bd255ba8244f910dbc3574e59593de68c" checksum = "ee1cd046f83ea2c4e920d6ee9f7c3537ef928d75dce5d84a87c2c5d6b3999a3a"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

3
Cargo.toml Executable file → Normal file
View File

@ -1,6 +1,6 @@
[package] [package]
name = "request-mirror" name = "request-mirror"
version = "0.1.1" version = "0.1.2"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -9,6 +9,7 @@ edition = "2021"
chrono = "0.4.34" chrono = "0.4.34"
diesel = { version = "2.1.4", features = ["postgres", "chrono"] } diesel = { version = "2.1.4", features = ["postgres", "chrono"] }
dotenvy = "0.15.7" dotenvy = "0.15.7"
regex = "1.10.5"
rocket = { version = "0.5.1", features = ["tls"] } rocket = { version = "0.5.1", features = ["tls"] }
serde = { version = "1.0.197", features = ["derive"] } serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.114" serde_json = "1.0.114"

View File

@ -6,10 +6,18 @@ use diesel::prelude::*;
use std::env; use std::env;
use dotenvy::dotenv; use dotenvy::dotenv;
use models::{ use models::{
Client, NewClient, NewHistoryRecord, NewPairRecord, PairType Client,
NewClient,
NewHistoryRecord,
NewPairRecord,
PairType,
Ownership
}; };
use schema::{ use schema::{
clients, history, pair_records clients,
history,
pair_records,
ownership
}; };
/// Establishes diesel Postgres connection that can be used to iteract with the database /// Establishes diesel Postgres connection that can be used to iteract with the database
@ -88,3 +96,36 @@ pub fn create_pair_record(conn: &mut PgConnection, history_id: i64, pair_type: P
.values(&new_pair_record) .values(&new_pair_record)
.execute(conn).unwrap() .execute(conn).unwrap()
} }
/// Creates a new ownership record in the database
/// Pass the owner_id and client_id
pub fn create_owner_record(conn: &mut PgConnection, owner_id: String, client_id: String) -> usize {
let new_ownership_record = Ownership {
owner_id,
client_id
};
diesel::insert_into(ownership::table)
.values(&new_ownership_record)
.execute(conn).unwrap()
}
/// retrieve a Vec of ownership relationships which client_id owns
pub fn get_ownerships(client_id: &str, connection: &mut PgConnection) -> Vec<Ownership> {
ownership::dsl::ownership
.filter(ownership::owner_id.eq(client_id))
.select(Ownership::as_select())
.load(connection)
.expect("Error loading history records")
}
/// Returns boolean for whether the given ownership exists between client_id and owner_id
pub fn ownership_exists(client_id: &str, owner_id: &str, connection: &mut PgConnection) -> bool {
let ownerships = ownership::dsl::ownership
.filter(ownership::client_id.eq(client_id).and(ownership::owner_id.eq(owner_id)))
.select(Ownership::as_select())
.load(connection)
.expect("Error loading history records");
ownerships.len() > 0
}

View File

@ -13,22 +13,33 @@ use uuid::Uuid;
use request_mirror::models::*; use request_mirror::models::*;
use diesel::prelude::*; use diesel::prelude::*;
use request_mirror::*; use request_mirror::*;
use regex::Regex;
/// Contains information about a request
#[derive(Serialize, Debug, Clone)] #[derive(Serialize, Debug, Clone)]
struct RequestInfo { pub struct RequestInfo {
header: Vec<Pair>, header: Vec<Pair>,
cookies: Vec<Pair>, cookies: Vec<Pair>,
query: Vec<Pair> query: Vec<Pair>
} }
#[derive(Debug)] impl RequestInfo {
enum Error { /// Find query pair with given key
TooLarge, pub fn find_query_key(&self, key: &str) -> Option<&Pair> {
Io(std::io::Error),
for pair in self.query.iter() {
if pair.key == key {
return Some(pair);
}
}
None
}
} }
/// Contains string representation of body of a request
#[derive(Serialize, Debug, Clone)] #[derive(Serialize, Debug, Clone)]
struct RequestBody(String); pub struct RequestBody(String);
// Always use a limit to prevent DoS attacks. // Always use a limit to prevent DoS attacks.
const LIMIT: u64 = 256; const LIMIT: u64 = 256;
@ -257,12 +268,28 @@ fn history_req(cookies: &CookieJar<'_>) -> Template {
let connection = &mut establish_connection(); let connection = &mut establish_connection();
// Query the database for history records // Query the database for history records
let results = history::dsl::history let mut results = history::dsl::history
.filter(history::client_id.eq(client_id.to_string())) .filter(history::client_id.eq(client_id.to_string()))
.select(HistoryRecord::as_select()) .select(HistoryRecord::as_select())
.load(connection) .load(connection)
.expect("Error loading clients"); .expect("Error loading clients");
// Get ownership relationships
let ownerships: Vec<Ownership> = get_ownerships(client_id, connection);
// Add any records that owned clients have
for ownership in ownerships {
let mut owned_records: Vec<HistoryRecord> = history::dsl::history
.filter(history::client_id.eq(ownership.client_id.to_string()))
.select(HistoryRecord::as_select())
.load(connection)
.expect("Error loading history records");
results.append(&mut owned_records);
}
results.sort_unstable_by_key(|item| item.id);
/// Template Context /// Template Context
#[derive(Serialize)] #[derive(Serialize)]
struct Context { struct Context {
@ -302,13 +329,19 @@ fn history_details(history_id: i64, cookies: &CookieJar<'_>) -> Template {
let client_id = cookies.get("mirror-id").unwrap().value(); let client_id = cookies.get("mirror-id").unwrap().value();
let connection = &mut establish_connection(); let connection: &mut PgConnection = &mut establish_connection();
// Get owned client ids
let owned_clients: Vec<String> = get_ownerships(client_id, connection)
.iter()
.map(|x|x.client_id.to_string())
.collect::<Vec<String>>();
// Query history table where the history id is what was in the route. Also filter client_ids. // 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 // If this record has a different client_id nothing will be returned
let results: Vec<HistoryRecord> = history::dsl::history let results: Vec<HistoryRecord> = history::dsl::history
.filter(history::id.eq(&history_id)) .filter(history::id.eq(&history_id))
.filter(history::client_id.eq(client_id.to_string())) .filter(history::client_id.eq(client_id.to_string()).or(history::client_id.eq_any(owned_clients)))
.select(HistoryRecord::as_select()) .select(HistoryRecord::as_select())
.load(connection) .load(connection)
.expect("Error loading history records"); .expect("Error loading history records");
@ -322,8 +355,6 @@ fn history_details(history_id: i64, cookies: &CookieJar<'_>) -> Template {
); );
} }
let connection = &mut establish_connection();
// Query all Pair Records for this history_id // Query all Pair Records for this history_id
let pairs: Vec<PairRecord> = pair_records::dsl::pair_records let pairs: Vec<PairRecord> = pair_records::dsl::pair_records
.filter(pair_records::history_id.eq(history_id)) .filter(pair_records::history_id.eq(history_id))
@ -381,6 +412,48 @@ fn history_details(history_id: i64, cookies: &CookieJar<'_>) -> Template {
} }
#[get("/ownership_registration")]
fn ownership_registration(info: RequestInfo, cookies: &CookieJar<'_>) -> Template {
let client_id = cookies.get("mirror-id").unwrap().value().to_string();
let owner_id_pair = info.find_query_key("owner_id");
let mut disp_owner_reg = false;
let re = Regex::new(r"^[{]?[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}[}]?$").unwrap();
let connection: &mut PgConnection = &mut establish_connection();
let ownerships: Vec<Ownership> = get_ownerships(&client_id, connection);
let owner_id = match owner_id_pair {
Some(v) => v.value.to_string(),
None => "".to_string()
};
if
owner_id_pair.is_some() &&
re.is_match(&owner_id) &&
owner_id != client_id &&
!ownership_exists(&client_id, &owner_id, connection)
{
create_owner_record(connection, owner_id.clone(), client_id.clone());
disp_owner_reg = true;
}
#[derive(Serialize)]
struct Context{
client_id: String,
owner_id: String,
disp_owner_reg: bool,
ownerships: Vec<Ownership>
}
Template::render("ownership_registration", Context {
client_id,
owner_id,
disp_owner_reg,
ownerships
})
}
/// 404 Response /// 404 Response
#[catch(404)] #[catch(404)]
fn not_found(req: &Request) -> Template { fn not_found(req: &Request) -> Template {
@ -402,7 +475,8 @@ fn rocket() -> _ {
test_get, test_get,
test_post, test_post,
history_req, history_req,
history_details history_details,
ownership_registration
] ]
) )
.register("/", catchers![not_found]) .register("/", catchers![not_found])

View File

@ -4,6 +4,7 @@ use serde::Serialize;
use crate::schema::{ use crate::schema::{
clients, clients,
history, history,
ownership,
pair_records pair_records
}; };
use diesel::pg::Pg; use diesel::pg::Pg;
@ -58,6 +59,16 @@ pub struct NewHistoryRecord<'a> {
pub timestamp: NaiveDateTime pub timestamp: NaiveDateTime
} }
/// Used to store ownership relationship between two clients
/// This structure is used for allowing separate clients to view history records of other clients
#[derive(Queryable, Selectable, Insertable, Debug, Serialize)]
#[diesel(table_name = ownership)]
#[diesel(check_for_backend(Pg))]
pub struct Ownership {
pub owner_id: String,
pub client_id: String
}
/// Pair Records are used to store attributes of a request. This includes a body record, header, query and cookie record. /// 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. /// Each has a key and value.
#[derive(Queryable, Selectable, Debug, Serialize)] #[derive(Queryable, Selectable, Debug, Serialize)]
@ -104,4 +115,11 @@ pub struct Pair {
#[derive(Serialize)] #[derive(Serialize)]
pub struct ErrorContext { pub struct ErrorContext {
pub error_msg: String pub error_msg: String
}
/// Provides error context for
#[derive(Debug)]
pub enum Error {
TooLarge,
Io(std::io::Error),
} }

View File

@ -8,18 +8,11 @@
</head> </head>
<body class="d-flex flex-column"> <body class="d-flex flex-column">
<nav id="navbar" class="navbar navbar-expand-lg bg-body-tertiary mb-2"> <nav id="navbar" class="navbar navbar-expand-lg bg-body-tertiary mb-5">
<div class="container-fluid"> <div class="container-fluid">
<a class="navbar-brand" href="/">Request Mirror</a> <a class="navbar-brand" href="/">Request Mirror</a>
<button <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
class="navbar-toggler" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbarSupportedContent"> <div class="collapse navbar-collapse" id="navbarSupportedContent">
@ -33,6 +26,9 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/history">History</a> <a class="nav-link" href="/history">History</a>
</li> </li>
<li class="nav-item">
<a class="nav-link" href="/ownership_registration">Ownership Registration</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -12,15 +12,8 @@
<nav id="navbar" class="navbar navbar-expand-lg bg-body-tertiary mb-5"> <nav id="navbar" class="navbar navbar-expand-lg bg-body-tertiary mb-5">
<div class="container-fluid"> <div class="container-fluid">
<a class="navbar-brand" href="/">Request Mirror</a> <a class="navbar-brand" href="/">Request Mirror</a>
<button <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
class="navbar-toggler" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbarSupportedContent"> <div class="collapse navbar-collapse" id="navbarSupportedContent">
@ -34,6 +27,9 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/history">History</a> <a class="nav-link" href="/history">History</a>
</li> </li>
<li class="nav-item">
<a class="nav-link" href="/ownership_registration">Ownership Registration</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>
@ -64,18 +60,18 @@
<script type="text/javascript"> <script type="text/javascript">
let records = [ let records = [
{{#each history_records}} {{#each history_records}}
{ {
id: {{id}}, id: {{id}},
client_id: "{{client_id}}", client_id: "{{client_id}}",
request_type: "{{request_type}}", request_type: "{{request_type}}",
timestamp: "{{timestamp}}" timestamp: "{{timestamp}}"
}, },
{{/each}} {{/each}}
]; ];
function popupRecord(event, id) { function popupRecord(event, id) {
console.log(records.filter(v => v.id == id)); console.log(records.filter(v => v.id == id));
} }
</script> </script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"

View File

@ -11,15 +11,8 @@
<nav id="navbar" class="navbar navbar-expand-lg bg-body-tertiary mb-5"> <nav id="navbar" class="navbar navbar-expand-lg bg-body-tertiary mb-5">
<div class="container-fluid"> <div class="container-fluid">
<a class="navbar-brand" href="/">Request Mirror</a> <a class="navbar-brand" href="/">Request Mirror</a>
<button <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
class="navbar-toggler" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbarSupportedContent"> <div class="collapse navbar-collapse" id="navbarSupportedContent">
@ -33,6 +26,9 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/history">History</a> <a class="nav-link" href="/history">History</a>
</li> </li>
<li class="nav-item">
<a class="nav-link" href="/ownership_registration">Ownership Registration</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body class="d-flex flex-column">
<nav id="navbar" class="navbar navbar-expand-lg bg-body-tertiary mb-5">
<div class="container-fluid">
<a class="navbar-brand" href="/">Request Mirror</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/test">Test Page</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/history">History</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/ownership_registration">Ownership Registration</a>
</li>
</ul>
</div>
</div>
</nav>
<div id="body" class="p-5">
<h3>Your Client ID: {{client_id}}</h3>
<form target="/owner_registration">
<h5>Ownership Registration</h5>
<input type="text" name="owner_id">
</form>
{{#if disp_owner_reg}}
<p>Registered new owner {{owner_id}} for {{client_id}}</p>
{{/if}}
<h3>Owned Clients</h3>
<table>
<tr>
<th>Client ID</th>
</tr>
{{#each ownerships}}
<tr>
<td>
{{client_id}}
</td>
</tr>
{{/each}}
</table>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
crossorigin="anonymous"></script>
</body>
</html>

View File

@ -11,15 +11,8 @@
<nav id="navbar" class="navbar navbar-expand-lg bg-body-tertiary mb-5"> <nav id="navbar" class="navbar navbar-expand-lg bg-body-tertiary mb-5">
<div class="container-fluid"> <div class="container-fluid">
<a class="navbar-brand" href="/">Request Mirror</a> <a class="navbar-brand" href="/">Request Mirror</a>
<button <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
class="navbar-toggler" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbarSupportedContent"> <div class="collapse navbar-collapse" id="navbarSupportedContent">
@ -33,6 +26,9 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/history">History</a> <a class="nav-link" href="/history">History</a>
</li> </li>
<li class="nav-item">
<a class="nav-link" href="/ownership_registration">Ownership Registration</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>