diff --git a/app/src/lib.rs b/app/src/lib.rs index 731c6e8..0f8827a 100644 --- a/app/src/lib.rs +++ b/app/src/lib.rs @@ -60,12 +60,14 @@ impl Model { } } +/// The input fields of the login dialog. #[derive(Default, Debug)] struct LoginForm { username: ElRef, password: ElRef, } +/// All information regarding the current location #[derive(Debug)] struct Location { host: String, @@ -84,6 +86,7 @@ impl Location { } } +/// Get the url from the address bar. #[must_use] pub fn get_host() -> String { window() @@ -92,6 +95,9 @@ pub fn get_host() -> String { .expect("Failed to extract the host of the url") } +/// The pages: +/// * Home for listing of links +/// * ListUsers for listing of users #[derive(Debug)] enum Page { Home(pages::list_links::Model), @@ -137,6 +143,8 @@ impl Page { // ------ ------ // Update // ------ ------ + +/// The messages regarding authentication and settings. #[derive(Clone)] pub enum Msg { UrlChanged(subs::UrlChanged), @@ -154,6 +162,7 @@ pub enum Msg { LanguageChanged(Lang), } +/// react to settings and authentication changes. fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders) { match msg { Msg::UrlChanged(url) => { @@ -222,6 +231,7 @@ fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders) { } } +/// switch the language fn change_language(l: Lang, orders: &mut impl Orders) { orders.perform_cmd(async move { // create request @@ -241,6 +251,7 @@ fn change_language(l: Lang, orders: &mut impl Orders) { }); } +/// logout on the server fn logout(orders: &mut impl Orders) { orders.perform_cmd(async { let request = Request::new("/admin/logout/"); @@ -249,6 +260,7 @@ fn logout(orders: &mut impl Orders) { }); } +/// login using username and password fn login_user(model: &mut Model, orders: &mut impl Orders) { model.user = Loadable::Loading; let data = model.login_data.clone(); @@ -272,6 +284,7 @@ fn login_user(model: &mut Model, orders: &mut impl Orders) { }); } +/// to create urls for different subpages pub struct Urls<'a> { base_url: std::borrow::Cow<'a, Url>, } @@ -356,6 +369,7 @@ fn view_content(page: &Page, url: &Url, user: &User) -> Node { ] } +/// If not logged in render the login form fn view_login(lang: &I18n, model: &Model) -> Node { let t = move |key: &str| lang.translate(key, None); diff --git a/app/src/pages/home.rs b/app/src/pages/home.rs deleted file mode 100644 index 8ff0e27..0000000 --- a/app/src/pages/home.rs +++ /dev/null @@ -1,6 +0,0 @@ -use seed::{div, prelude::*}; - -#[must_use] -pub fn view() -> Node { - div!["List Links"] -} diff --git a/app/src/pages/list_links.rs b/app/src/pages/list_links.rs index a560566..c6638c5 100644 --- a/app/src/pages/list_links.rs +++ b/app/src/pages/list_links.rs @@ -1,3 +1,4 @@ +//! List all the links the own links editable or if an admin is logged in all links editable. use enum_map::EnumMap; use fluent::fluent_args; use image::{DynamicImage, ImageOutputFormat, Luma}; @@ -60,6 +61,7 @@ impl Model { } } +/// There can allways be only one dialog. #[derive(Debug, Clone)] enum Dialog { EditLink { @@ -71,6 +73,7 @@ enum Dialog { None, } +/// A qr-code with `new` for creating a blob url and `Drop` for releasing the blob url. #[derive(Debug, Clone)] pub struct QrGuard { svg: String, @@ -98,17 +101,20 @@ impl QrGuard { } impl Drop for QrGuard { + /// release the blob url fn drop(&mut self) { web_sys::Url::revoke_object_url(&self.url) .unwrap_or_else(|_| (log!("Failed to release url!"))); } } +/// Filter one column of the row. #[derive(Default, Debug, Clone)] struct FilterInput { filter_input: ElRef, } +/// A message can either edit or query. (or set a dialog) #[derive(Clone)] pub enum Msg { Query(QueryMsg), // Messages related to querying links @@ -117,6 +123,7 @@ pub enum Msg { SetMessage(String), // Set a message to the user } +/// All the messages related to requesting information from the server. #[derive(Clone)] pub enum QueryMsg { Fetch, @@ -127,7 +134,8 @@ pub enum QueryMsg { TargetFilterChanged(String), AuthorFilterChanged(String), } -/// All the messages on link editing + +/// All the messages on storing information on the server. #[derive(Debug, Clone)] pub enum EditMsg { EditSelected(LinkDelta), @@ -151,7 +159,7 @@ fn clear_all(model: &mut Model) { model.dialog = Dialog::None; } -/// React to environment changes +/// Split the update to Query updates and Edit updates. pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders) { match msg { Msg::Query(msg) => process_query_messages(msg, model, orders), @@ -706,10 +714,12 @@ fn edit_or_create_link String>( ] } +/// generate a qr-code for a code fn generate_qr_from_code(code: &str) -> String { generate_qr_from_link(&format!("https://{}/{}", get_host(), code)) } +/// generate a svg qr-code for a url fn generate_qr_from_link(url: &str) -> String { if let Ok(qr) = QrCode::with_error_correction_level(&url, qrcode::EcLevel::L) { let svg = qr @@ -720,6 +730,7 @@ fn generate_qr_from_link(url: &str) -> String { .build(); svg } else { + // should never (only on very huge codes) happen. "".to_string() } } @@ -733,6 +744,7 @@ fn close_button() -> Node { ] } +/// generate a png qr-code for a url fn generate_qr_png(code: &str) -> Vec { let qr = QrCode::with_error_correction_level( &format!("http://{}/{}", get_host(), code), diff --git a/app/src/pages/list_users.rs b/app/src/pages/list_users.rs index 55d9408..3dcb313 100644 --- a/app/src/pages/list_users.rs +++ b/app/src/pages/list_users.rs @@ -97,6 +97,8 @@ pub enum UserEditMsg { /* * update */ + +/// Split the update to Query updates and Edit updates pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders) { match msg { Msg::Query(msg) => process_query_messages(msg, model, orders), @@ -107,7 +109,7 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders) { } } -/// Update all +/// Process all updates related to getting data from the server. pub fn process_query_messages(msg: UserQueryMsg, model: &mut Model, orders: &mut impl Orders) { match msg { UserQueryMsg::Fetch => { @@ -170,6 +172,7 @@ pub fn process_query_messages(msg: UserQueryMsg, model: &mut Model, orders: &mut } } +/// Load the list of users from the server. fn load_users(data: UserRequestForm, orders: &mut impl Orders) { orders.perform_cmd(async { let data = data; @@ -200,6 +203,7 @@ fn load_users(data: UserRequestForm, orders: &mut impl Orders) { }); } +/// Process all the messages related to editing users. pub fn process_user_edit_messages( msg: UserEditMsg, model: &mut Model, @@ -233,7 +237,7 @@ pub fn process_user_edit_messages( let data = model .user_edit .take() - .expect("A user should allways be there on save"); // complicated way to move into the closure + .expect("A user should allways be there on save"); log!("Saving User: ", &data.username); save_user(data, orders); } @@ -251,6 +255,7 @@ pub fn process_user_edit_messages( } } +/// Update the role of a user - this toggles between admin and regular. fn update_privileges(user: UserDelta, orders: &mut impl Orders) { orders.perform_cmd(async { let data = user; @@ -281,6 +286,7 @@ fn update_privileges(user: UserDelta, orders: &mut impl Orders) { }); } +/// Save a new user or edit an existing user fn save_user(user: UserDelta, orders: &mut impl Orders) { orders.perform_cmd(async { let data = user; @@ -360,6 +366,7 @@ pub fn view(model: &Model, logged_in_user: &User) -> Node { ] } +/// View the headlines of the table fn view_user_table_head String>(t: F) -> Node { tr![ th![ @@ -384,6 +391,7 @@ fn view_user_table_head String>(t: F) -> Node { ] } +/// Display the filterboxes below the headlines fn view_user_table_filter_input String>(model: &Model, t: F) -> Node { tr![ C!["filters"], @@ -426,6 +434,7 @@ fn view_user_table_filter_input String>(model: &Model, t: F) -> N ] } +/// Display one user-line of the table fn view_user String>(l: &User, logged_in_user: &User, t: F) -> Node { let user = UserDelta::from(l.clone()); tr![ @@ -471,6 +480,7 @@ fn view_user String>(l: &User, logged_in_user: &User, t: F) -> No ] } +/// display the edit and create dialog fn edit_or_create_user String>(l: UserDelta, t: F) -> Node { let user = l; let headline: Node = match &user.role { diff --git a/app/src/pages/mod.rs b/app/src/pages/mod.rs index 2db85a5..c1273c2 100644 --- a/app/src/pages/mod.rs +++ b/app/src/pages/mod.rs @@ -1,5 +1,4 @@ //! Containing the individual pages for the admin app so far one to list the links and one to list the users. -pub mod home; pub mod list_links; pub mod list_users; diff --git a/shared/src/apirequests/general.rs b/shared/src/apirequests/general.rs index 4a3a4b4..0e0a230 100644 --- a/shared/src/apirequests/general.rs +++ b/shared/src/apirequests/general.rs @@ -1,6 +1,8 @@ +//! The more generic request datatypes use std::ops::Deref; use serde::{Deserialize, Serialize}; +/// Filter one column according to the containing string. #[derive(Clone, Deserialize, Serialize, Debug, Default)] pub struct Filter { pub sieve: String, @@ -14,18 +16,21 @@ impl Deref for Filter { } } +/// Possible order directions #[derive(Clone, Deserialize, Serialize, PartialEq, Eq, Debug)] pub enum Ordering { Ascending, Descending, } +/// An operation on a column #[derive(Clone, Deserialize, Serialize, Debug)] pub struct Operation { pub column: T, pub value: V, } +/// To differentiate between creating a new record and editing. #[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq)] pub enum EditMode { Create, @@ -38,11 +43,13 @@ impl Default for EditMode { } } +/// When a message is sent between client and server (like for a dialog). #[derive(Clone, Deserialize, Serialize, Debug)] pub struct Message { pub message: String, } +/// Send a message on success and also one on error. #[derive(Clone, Deserialize, Serialize, Debug)] pub enum Status { Success(Message), diff --git a/shared/src/apirequests/links.rs b/shared/src/apirequests/links.rs index 23b9892..6c6f2b1 100644 --- a/shared/src/apirequests/links.rs +++ b/shared/src/apirequests/links.rs @@ -1,3 +1,5 @@ +//! types for link requesting and saving. + use enum_map::{Enum, EnumMap}; use serde::{Deserialize, Serialize}; @@ -5,7 +7,7 @@ use crate::datatypes::{FullLink, Link}; use super::general::{EditMode, Filter, Operation, Ordering}; -/// A generic list returntype containing the User and a Vec containing e.g. Links or Users +/// Request a list of users respecting the filter and ordering. #[derive(Clone, Deserialize, Serialize, Debug)] pub struct LinkRequestForm { pub filter: EnumMap, @@ -23,7 +25,7 @@ impl Default for LinkRequestForm { } } -/// The Struct that is responsible for creating and editing users. +/// The Struct that is responsible for creating and editing links. #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct LinkDelta { pub edit: EditMode, @@ -36,7 +38,7 @@ pub struct LinkDelta { } impl From for LinkDelta { - /// Automatically create a `UserDelta` from a User. + /// Automatically create a `LinkDelta` from a Link. fn from(l: Link) -> Self { Self { edit: EditMode::Edit, @@ -51,7 +53,7 @@ impl From for LinkDelta { } impl From for LinkDelta { - /// Automatically create a `UserDelta` from a User. + /// Automatically create a `LinkDelta` from a FullLink. fn from(l: FullLink) -> Self { Self { edit: EditMode::Edit, @@ -65,6 +67,7 @@ impl From for LinkDelta { } } +/// An enumeration of the filterable columns #[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq, Hash, Enum)] pub enum LinkOverviewColumns { Code, @@ -74,17 +77,20 @@ pub enum LinkOverviewColumns { Statistics, } +/// A struct to request a qr-code from the server #[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq)] pub struct QrCodeRequest { pub link_id: String, pub format: QrCodeFormat, } +/// The response to a qr-request #[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq)] pub struct SvgQrCodeResponse { pub svg: String, } +/// Available formats of qr-codes #[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq)] pub enum QrCodeFormat { Svg, diff --git a/shared/src/apirequests/mod.rs b/shared/src/apirequests/mod.rs index a8f4714..6c3c869 100644 --- a/shared/src/apirequests/mod.rs +++ b/shared/src/apirequests/mod.rs @@ -1,3 +1,4 @@ +//! This module contains the structs for api requests. pub mod general; pub mod links; pub mod users; diff --git a/shared/src/apirequests/users.rs b/shared/src/apirequests/users.rs index 5bef4ce..e77143a 100644 --- a/shared/src/apirequests/users.rs +++ b/shared/src/apirequests/users.rs @@ -1,3 +1,4 @@ +//! Types for user requesting and saving use enum_map::{Enum, EnumMap}; use serde::{Deserialize, Serialize}; @@ -5,6 +6,7 @@ use crate::datatypes::User; use super::general::{EditMode, Filter, Operation, Ordering}; +/// Request an ordered and filtered list of users from the server. #[derive(Clone, Deserialize, Serialize, Debug)] pub struct UserRequestForm { // The filters up to one for each column @@ -25,6 +27,7 @@ impl Default for UserRequestForm { } } +/// Data to login. #[derive(Debug, Deserialize, Default, Serialize, Clone)] pub struct LoginUser { pub username: String, @@ -65,7 +68,7 @@ pub enum UserOverviewColumns { Username, } -/// The possible roles a user could have. +/// The possible roles a user could have. They are stored as i64 in the database #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Copy)] pub enum Role { NotAuthenticated, diff --git a/shared/src/datatypes.rs b/shared/src/datatypes.rs index aa250da..9a67d5e 100644 --- a/shared/src/datatypes.rs +++ b/shared/src/datatypes.rs @@ -1,3 +1,4 @@ +//! The more generic datatypes used in pslink use std::ops::Deref; use serde::{Deserialize, Serialize, Serializer}; @@ -19,6 +20,7 @@ pub struct FullLink { pub clicks: Count, } +/// A User of the pslink service #[derive(PartialEq, Serialize, Deserialize, Clone, Debug)] pub struct User { pub id: i64, @@ -29,6 +31,7 @@ pub struct User { pub language: Lang, } +/// A short url of the link service #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Link { pub id: i64, @@ -39,10 +42,13 @@ pub struct Link { pub created_at: chrono::NaiveDateTime, } +/// When statistics are counted #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Count { pub number: i32, } + +/// Everytime a shor url is clicked record it for statistical evaluation. #[derive(Serialize, Debug)] pub struct Click { pub id: i64, @@ -50,6 +56,7 @@ pub struct Click { pub created_at: chrono::NaiveDateTime, } +/// The Password: Display, Debug and serialize do not include the Password to prevent leaks of sensible information in logs or similar. #[derive(PartialEq, Clone, Deserialize)] #[serde(from = "String")] pub struct Secret { @@ -92,6 +99,7 @@ impl std::fmt::Display for Secret { } } +/// Loadable is a type that has not been loaded but will be in future. It can be used to indicate the loading process. #[derive(Debug, Serialize, Deserialize, Clone)] pub enum Loadable { Data(Option),