documentation updates

This commit is contained in:
Dietrich 2021-06-17 09:30:39 +02:00 committed by Franz Dietrich
parent 48a376f3bd
commit bf6bac847b
10 changed files with 70 additions and 16 deletions

View File

@ -60,12 +60,14 @@ impl Model {
} }
} }
/// The input fields of the login dialog.
#[derive(Default, Debug)] #[derive(Default, Debug)]
struct LoginForm { struct LoginForm {
username: ElRef<web_sys::HtmlInputElement>, username: ElRef<web_sys::HtmlInputElement>,
password: ElRef<web_sys::HtmlInputElement>, password: ElRef<web_sys::HtmlInputElement>,
} }
/// All information regarding the current location
#[derive(Debug)] #[derive(Debug)]
struct Location { struct Location {
host: String, host: String,
@ -84,6 +86,7 @@ impl Location {
} }
} }
/// Get the url from the address bar.
#[must_use] #[must_use]
pub fn get_host() -> String { pub fn get_host() -> String {
window() window()
@ -92,6 +95,9 @@ pub fn get_host() -> String {
.expect("Failed to extract the host of the url") .expect("Failed to extract the host of the url")
} }
/// The pages:
/// * Home for listing of links
/// * ListUsers for listing of users
#[derive(Debug)] #[derive(Debug)]
enum Page { enum Page {
Home(pages::list_links::Model), Home(pages::list_links::Model),
@ -137,6 +143,8 @@ impl Page {
// ------ ------ // ------ ------
// Update // Update
// ------ ------ // ------ ------
/// The messages regarding authentication and settings.
#[derive(Clone)] #[derive(Clone)]
pub enum Msg { pub enum Msg {
UrlChanged(subs::UrlChanged), UrlChanged(subs::UrlChanged),
@ -154,6 +162,7 @@ pub enum Msg {
LanguageChanged(Lang), LanguageChanged(Lang),
} }
/// react to settings and authentication changes.
fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) { fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
match msg { match msg {
Msg::UrlChanged(url) => { Msg::UrlChanged(url) => {
@ -222,6 +231,7 @@ fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
} }
} }
/// switch the language
fn change_language(l: Lang, orders: &mut impl Orders<Msg>) { fn change_language(l: Lang, orders: &mut impl Orders<Msg>) {
orders.perform_cmd(async move { orders.perform_cmd(async move {
// create request // create request
@ -241,6 +251,7 @@ fn change_language(l: Lang, orders: &mut impl Orders<Msg>) {
}); });
} }
/// logout on the server
fn logout(orders: &mut impl Orders<Msg>) { fn logout(orders: &mut impl Orders<Msg>) {
orders.perform_cmd(async { orders.perform_cmd(async {
let request = Request::new("/admin/logout/"); let request = Request::new("/admin/logout/");
@ -249,6 +260,7 @@ fn logout(orders: &mut impl Orders<Msg>) {
}); });
} }
/// login using username and password
fn login_user(model: &mut Model, orders: &mut impl Orders<Msg>) { fn login_user(model: &mut Model, orders: &mut impl Orders<Msg>) {
model.user = Loadable::Loading; model.user = Loadable::Loading;
let data = model.login_data.clone(); let data = model.login_data.clone();
@ -272,6 +284,7 @@ fn login_user(model: &mut Model, orders: &mut impl Orders<Msg>) {
}); });
} }
/// to create urls for different subpages
pub struct Urls<'a> { pub struct Urls<'a> {
base_url: std::borrow::Cow<'a, Url>, base_url: std::borrow::Cow<'a, Url>,
} }
@ -356,6 +369,7 @@ fn view_content(page: &Page, url: &Url, user: &User) -> Node<Msg> {
] ]
} }
/// If not logged in render the login form
fn view_login(lang: &I18n, model: &Model) -> Node<Msg> { fn view_login(lang: &I18n, model: &Model) -> Node<Msg> {
let t = move |key: &str| lang.translate(key, None); let t = move |key: &str| lang.translate(key, None);

View File

@ -1,6 +0,0 @@
use seed::{div, prelude::*};
#[must_use]
pub fn view<Ms>() -> Node<Ms> {
div!["List Links"]
}

View File

@ -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 enum_map::EnumMap;
use fluent::fluent_args; use fluent::fluent_args;
use image::{DynamicImage, ImageOutputFormat, Luma}; use image::{DynamicImage, ImageOutputFormat, Luma};
@ -60,6 +61,7 @@ impl Model {
} }
} }
/// There can allways be only one dialog.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum Dialog { enum Dialog {
EditLink { EditLink {
@ -71,6 +73,7 @@ enum Dialog {
None, None,
} }
/// A qr-code with `new` for creating a blob url and `Drop` for releasing the blob url.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct QrGuard { pub struct QrGuard {
svg: String, svg: String,
@ -98,17 +101,20 @@ impl QrGuard {
} }
impl Drop for QrGuard { impl Drop for QrGuard {
/// release the blob url
fn drop(&mut self) { fn drop(&mut self) {
web_sys::Url::revoke_object_url(&self.url) web_sys::Url::revoke_object_url(&self.url)
.unwrap_or_else(|_| (log!("Failed to release url!"))); .unwrap_or_else(|_| (log!("Failed to release url!")));
} }
} }
/// Filter one column of the row.
#[derive(Default, Debug, Clone)] #[derive(Default, Debug, Clone)]
struct FilterInput { struct FilterInput {
filter_input: ElRef<web_sys::HtmlInputElement>, filter_input: ElRef<web_sys::HtmlInputElement>,
} }
/// A message can either edit or query. (or set a dialog)
#[derive(Clone)] #[derive(Clone)]
pub enum Msg { pub enum Msg {
Query(QueryMsg), // Messages related to querying links Query(QueryMsg), // Messages related to querying links
@ -117,6 +123,7 @@ pub enum Msg {
SetMessage(String), // Set a message to the user SetMessage(String), // Set a message to the user
} }
/// All the messages related to requesting information from the server.
#[derive(Clone)] #[derive(Clone)]
pub enum QueryMsg { pub enum QueryMsg {
Fetch, Fetch,
@ -127,7 +134,8 @@ pub enum QueryMsg {
TargetFilterChanged(String), TargetFilterChanged(String),
AuthorFilterChanged(String), AuthorFilterChanged(String),
} }
/// All the messages on link editing
/// All the messages on storing information on the server.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum EditMsg { pub enum EditMsg {
EditSelected(LinkDelta), EditSelected(LinkDelta),
@ -151,7 +159,7 @@ fn clear_all(model: &mut Model) {
model.dialog = Dialog::None; 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<Msg>) { pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
match msg { match msg {
Msg::Query(msg) => process_query_messages(msg, model, orders), Msg::Query(msg) => process_query_messages(msg, model, orders),
@ -706,10 +714,12 @@ fn edit_or_create_link<F: Fn(&str) -> String>(
] ]
} }
/// generate a qr-code for a code
fn generate_qr_from_code(code: &str) -> String { fn generate_qr_from_code(code: &str) -> String {
generate_qr_from_link(&format!("https://{}/{}", get_host(), code)) 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 { fn generate_qr_from_link(url: &str) -> String {
if let Ok(qr) = QrCode::with_error_correction_level(&url, qrcode::EcLevel::L) { if let Ok(qr) = QrCode::with_error_correction_level(&url, qrcode::EcLevel::L) {
let svg = qr let svg = qr
@ -720,6 +730,7 @@ fn generate_qr_from_link(url: &str) -> String {
.build(); .build();
svg svg
} else { } else {
// should never (only on very huge codes) happen.
"".to_string() "".to_string()
} }
} }
@ -733,6 +744,7 @@ fn close_button() -> Node<Msg> {
] ]
} }
/// generate a png qr-code for a url
fn generate_qr_png(code: &str) -> Vec<u8> { fn generate_qr_png(code: &str) -> Vec<u8> {
let qr = QrCode::with_error_correction_level( let qr = QrCode::with_error_correction_level(
&format!("http://{}/{}", get_host(), code), &format!("http://{}/{}", get_host(), code),

View File

@ -97,6 +97,8 @@ pub enum UserEditMsg {
/* /*
* update * update
*/ */
/// Split the update to Query updates and Edit updates
pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) { pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
match msg { match msg {
Msg::Query(msg) => process_query_messages(msg, model, orders), 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<Msg>) {
} }
} }
/// 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<Msg>) { pub fn process_query_messages(msg: UserQueryMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
match msg { match msg {
UserQueryMsg::Fetch => { 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<Msg>) { fn load_users(data: UserRequestForm, orders: &mut impl Orders<Msg>) {
orders.perform_cmd(async { orders.perform_cmd(async {
let data = data; let data = data;
@ -200,6 +203,7 @@ fn load_users(data: UserRequestForm, orders: &mut impl Orders<Msg>) {
}); });
} }
/// Process all the messages related to editing users.
pub fn process_user_edit_messages( pub fn process_user_edit_messages(
msg: UserEditMsg, msg: UserEditMsg,
model: &mut Model, model: &mut Model,
@ -233,7 +237,7 @@ pub fn process_user_edit_messages(
let data = model let data = model
.user_edit .user_edit
.take() .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); log!("Saving User: ", &data.username);
save_user(data, orders); 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<Msg>) { fn update_privileges(user: UserDelta, orders: &mut impl Orders<Msg>) {
orders.perform_cmd(async { orders.perform_cmd(async {
let data = user; let data = user;
@ -281,6 +286,7 @@ fn update_privileges(user: UserDelta, orders: &mut impl Orders<Msg>) {
}); });
} }
/// Save a new user or edit an existing user
fn save_user(user: UserDelta, orders: &mut impl Orders<Msg>) { fn save_user(user: UserDelta, orders: &mut impl Orders<Msg>) {
orders.perform_cmd(async { orders.perform_cmd(async {
let data = user; let data = user;
@ -360,6 +366,7 @@ pub fn view(model: &Model, logged_in_user: &User) -> Node<Msg> {
] ]
} }
/// View the headlines of the table
fn view_user_table_head<F: Fn(&str) -> String>(t: F) -> Node<Msg> { fn view_user_table_head<F: Fn(&str) -> String>(t: F) -> Node<Msg> {
tr![ tr![
th![ th![
@ -384,6 +391,7 @@ fn view_user_table_head<F: Fn(&str) -> String>(t: F) -> Node<Msg> {
] ]
} }
/// Display the filterboxes below the headlines
fn view_user_table_filter_input<F: Fn(&str) -> String>(model: &Model, t: F) -> Node<Msg> { fn view_user_table_filter_input<F: Fn(&str) -> String>(model: &Model, t: F) -> Node<Msg> {
tr![ tr![
C!["filters"], C!["filters"],
@ -426,6 +434,7 @@ fn view_user_table_filter_input<F: Fn(&str) -> String>(model: &Model, t: F) -> N
] ]
} }
/// Display one user-line of the table
fn view_user<F: Fn(&str) -> String>(l: &User, logged_in_user: &User, t: F) -> Node<Msg> { fn view_user<F: Fn(&str) -> String>(l: &User, logged_in_user: &User, t: F) -> Node<Msg> {
let user = UserDelta::from(l.clone()); let user = UserDelta::from(l.clone());
tr![ tr![
@ -471,6 +480,7 @@ fn view_user<F: Fn(&str) -> String>(l: &User, logged_in_user: &User, t: F) -> No
] ]
} }
/// display the edit and create dialog
fn edit_or_create_user<F: Fn(&str) -> String>(l: UserDelta, t: F) -> Node<Msg> { fn edit_or_create_user<F: Fn(&str) -> String>(l: UserDelta, t: F) -> Node<Msg> {
let user = l; let user = l;
let headline: Node<Msg> = match &user.role { let headline: Node<Msg> = match &user.role {

View File

@ -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. //! 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_links;
pub mod list_users; pub mod list_users;

View File

@ -1,6 +1,8 @@
//! The more generic request datatypes
use std::ops::Deref; use std::ops::Deref;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Filter one column according to the containing string.
#[derive(Clone, Deserialize, Serialize, Debug, Default)] #[derive(Clone, Deserialize, Serialize, Debug, Default)]
pub struct Filter { pub struct Filter {
pub sieve: String, pub sieve: String,
@ -14,18 +16,21 @@ impl Deref for Filter {
} }
} }
/// Possible order directions
#[derive(Clone, Deserialize, Serialize, PartialEq, Eq, Debug)] #[derive(Clone, Deserialize, Serialize, PartialEq, Eq, Debug)]
pub enum Ordering { pub enum Ordering {
Ascending, Ascending,
Descending, Descending,
} }
/// An operation on a column
#[derive(Clone, Deserialize, Serialize, Debug)] #[derive(Clone, Deserialize, Serialize, Debug)]
pub struct Operation<T, V> { pub struct Operation<T, V> {
pub column: T, pub column: T,
pub value: V, pub value: V,
} }
/// To differentiate between creating a new record and editing.
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq)] #[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq)]
pub enum EditMode { pub enum EditMode {
Create, 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)] #[derive(Clone, Deserialize, Serialize, Debug)]
pub struct Message { pub struct Message {
pub message: String, pub message: String,
} }
/// Send a message on success and also one on error.
#[derive(Clone, Deserialize, Serialize, Debug)] #[derive(Clone, Deserialize, Serialize, Debug)]
pub enum Status { pub enum Status {
Success(Message), Success(Message),

View File

@ -1,3 +1,5 @@
//! types for link requesting and saving.
use enum_map::{Enum, EnumMap}; use enum_map::{Enum, EnumMap};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -5,7 +7,7 @@ use crate::datatypes::{FullLink, Link};
use super::general::{EditMode, Filter, Operation, Ordering}; 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)] #[derive(Clone, Deserialize, Serialize, Debug)]
pub struct LinkRequestForm { pub struct LinkRequestForm {
pub filter: EnumMap<LinkOverviewColumns, Filter>, pub filter: EnumMap<LinkOverviewColumns, Filter>,
@ -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)] #[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct LinkDelta { pub struct LinkDelta {
pub edit: EditMode, pub edit: EditMode,
@ -36,7 +38,7 @@ pub struct LinkDelta {
} }
impl From<Link> for LinkDelta { impl From<Link> for LinkDelta {
/// Automatically create a `UserDelta` from a User. /// Automatically create a `LinkDelta` from a Link.
fn from(l: Link) -> Self { fn from(l: Link) -> Self {
Self { Self {
edit: EditMode::Edit, edit: EditMode::Edit,
@ -51,7 +53,7 @@ impl From<Link> for LinkDelta {
} }
impl From<FullLink> for LinkDelta { impl From<FullLink> for LinkDelta {
/// Automatically create a `UserDelta` from a User. /// Automatically create a `LinkDelta` from a FullLink.
fn from(l: FullLink) -> Self { fn from(l: FullLink) -> Self {
Self { Self {
edit: EditMode::Edit, edit: EditMode::Edit,
@ -65,6 +67,7 @@ impl From<FullLink> for LinkDelta {
} }
} }
/// An enumeration of the filterable columns
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq, Hash, Enum)] #[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq, Hash, Enum)]
pub enum LinkOverviewColumns { pub enum LinkOverviewColumns {
Code, Code,
@ -74,17 +77,20 @@ pub enum LinkOverviewColumns {
Statistics, Statistics,
} }
/// A struct to request a qr-code from the server
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq)] #[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq)]
pub struct QrCodeRequest { pub struct QrCodeRequest {
pub link_id: String, pub link_id: String,
pub format: QrCodeFormat, pub format: QrCodeFormat,
} }
/// The response to a qr-request
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq)] #[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq)]
pub struct SvgQrCodeResponse { pub struct SvgQrCodeResponse {
pub svg: String, pub svg: String,
} }
/// Available formats of qr-codes
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq)] #[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq)]
pub enum QrCodeFormat { pub enum QrCodeFormat {
Svg, Svg,

View File

@ -1,3 +1,4 @@
//! This module contains the structs for api requests.
pub mod general; pub mod general;
pub mod links; pub mod links;
pub mod users; pub mod users;

View File

@ -1,3 +1,4 @@
//! Types for user requesting and saving
use enum_map::{Enum, EnumMap}; use enum_map::{Enum, EnumMap};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -5,6 +6,7 @@ use crate::datatypes::User;
use super::general::{EditMode, Filter, Operation, Ordering}; use super::general::{EditMode, Filter, Operation, Ordering};
/// Request an ordered and filtered list of users from the server.
#[derive(Clone, Deserialize, Serialize, Debug)] #[derive(Clone, Deserialize, Serialize, Debug)]
pub struct UserRequestForm { pub struct UserRequestForm {
// The filters up to one for each column // 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)] #[derive(Debug, Deserialize, Default, Serialize, Clone)]
pub struct LoginUser { pub struct LoginUser {
pub username: String, pub username: String,
@ -65,7 +68,7 @@ pub enum UserOverviewColumns {
Username, 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)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Copy)]
pub enum Role { pub enum Role {
NotAuthenticated, NotAuthenticated,

View File

@ -1,3 +1,4 @@
//! The more generic datatypes used in pslink
use std::ops::Deref; use std::ops::Deref;
use serde::{Deserialize, Serialize, Serializer}; use serde::{Deserialize, Serialize, Serializer};
@ -19,6 +20,7 @@ pub struct FullLink {
pub clicks: Count, pub clicks: Count,
} }
/// A User of the pslink service
#[derive(PartialEq, Serialize, Deserialize, Clone, Debug)] #[derive(PartialEq, Serialize, Deserialize, Clone, Debug)]
pub struct User { pub struct User {
pub id: i64, pub id: i64,
@ -29,6 +31,7 @@ pub struct User {
pub language: Lang, pub language: Lang,
} }
/// A short url of the link service
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Link { pub struct Link {
pub id: i64, pub id: i64,
@ -39,10 +42,13 @@ pub struct Link {
pub created_at: chrono::NaiveDateTime, pub created_at: chrono::NaiveDateTime,
} }
/// When statistics are counted
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Count { pub struct Count {
pub number: i32, pub number: i32,
} }
/// Everytime a shor url is clicked record it for statistical evaluation.
#[derive(Serialize, Debug)] #[derive(Serialize, Debug)]
pub struct Click { pub struct Click {
pub id: i64, pub id: i64,
@ -50,6 +56,7 @@ pub struct Click {
pub created_at: chrono::NaiveDateTime, 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)] #[derive(PartialEq, Clone, Deserialize)]
#[serde(from = "String")] #[serde(from = "String")]
pub struct Secret { 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)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub enum Loadable<T> { pub enum Loadable<T> {
Data(Option<T>), Data(Option<T>),