diff --git a/inventur/inventur_db/migrations/.keep b/inventur/inventur_db/migrations/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/inventur_db/src/lib.rs b/inventur_db/src/lib.rs
index 74dba12..715d87b 100644
--- a/inventur_db/src/lib.rs
+++ b/inventur_db/src/lib.rs
@@ -12,10 +12,13 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see Option Tbl {
let mut rows = tbl.rows;
if sort_field == 0 {
@@ -196,6 +205,10 @@ pub fn sort_table(tbl: Tbl, sort_field: usize, sort_dir: u8) -> Tbl {
Tbl { tblid: tbl.tblid, name: tbl.name, column_names: tbl.column_names, column_types: tbl.column_types, rows: rows.clone() }
}
+/// Take a Tbl object, find all rows matching the search queries and return a new Tbl object containing only those.
+/// search_fields contains a Vec of (1-indexed) column positions to be included in the search
+/// search_value is a string to search for in the search_fields.
+/// Returned Tbl is not sorted and should be according to user preferences before being displayed.
pub fn search_table(tbl: Tbl, search_fields: Vec, search_value: String) -> Tbl {
let mut rows = tbl.rows;
let mut field_sets = HashSet::new();
@@ -209,7 +222,7 @@ pub fn search_table(tbl: Tbl, search_fields: Vec, search_value: String) ->
Tbl { tblid: tbl.tblid, name: tbl.name, column_names: tbl.column_names, column_types: tbl.column_types, rows: Vec::from_iter(field_sets) }
}
-
+/// For a Vec a database ids of tables, return a Vec of their names in the same order.
pub fn get_tblnames(conn: &mut MysqlConnection, tblids: Vec) -> Option> {
let mut tblnames = Vec::new();
for tblid in tblids {
@@ -308,6 +321,9 @@ pub fn register_or_login(conn: &mut MysqlConnection, uname: String, mail: String
Some(User { uid: uid, uname: uname, email: mail })
}
+/// Take a user id (database representation, not username) and delete it from the database.
+/// All its tables are cascadingly deleted.
+/// Return Some(true) if successful, None otherwise
pub fn delete_user(conn: &mut MysqlConnection, uid: i32) -> Option {
if users::delete_user(conn, uid).is_err() {
return None;
@@ -315,6 +331,9 @@ pub fn delete_user(conn: &mut MysqlConnection, uid: i32) -> Option {
Some(true)
}
+/// For a given id of a table and a vec of string values, create a new jrentry (table row) and all its jrcells (table cells) filled with the string values.
+/// Only works if uid corresponds to the user id of the table's owner.
+/// Return the id of the newly created row if successful, None otherwise.
pub fn add_row(conn: &mut MysqlConnection, tblid: i32, values: Vec, uid: i32) -> Option {
let owner = jrtables::get_owner_id(conn, tblid);
let nrows = jrtables::get_nrows(conn, tblid);
@@ -343,6 +362,8 @@ pub fn add_row(conn: &mut MysqlConnection, tblid: i32, values: Vec, uid:
Some(entryid)
}
+/// Delete a jrentry (table row) identified by the id of its table and the current position wihthin it
+/// Return Some(true) if successful, None otherwise
pub fn delete_row(conn: &mut MysqlConnection, tblid: i32, row_pos: i32, uid: i32) -> Option {
let owner = jrtables::get_owner_id(conn, tblid);
if owner.is_err() || owner.unwrap() != uid || jrentries::delete_jrentry_relative(conn, tblid, row_pos).is_err() {
@@ -351,6 +372,9 @@ pub fn delete_row(conn: &mut MysqlConnection, tblid: i32, row_pos: i32, uid: i32
Some(true)
}
+/// Change a jrentry's (table row's) current position within its table, given the table's id, the current position and the new position it should be moved to.
+/// Only works if uid equals to the owner's user id.
+/// Returns Some(true) if successful, None otherwise.
pub fn move_row(conn: &mut MysqlConnection, tblid: i32, rowpos: i32, newpos: i32, uid: i32) -> Option {
let owner = jrtables::get_owner_id(conn, tblid);
if owner.is_err() || owner.unwrap() != uid || jrentries::move_jrentry(conn, tblid, rowpos, newpos).is_err() {
@@ -359,6 +383,9 @@ pub fn move_row(conn: &mut MysqlConnection, tblid: i32, rowpos: i32, newpos: i32
Some(true)
}
+/// Update a jrentry (table row) with a new set of string values, given those new values and the row identified by its table and its position within the table.
+/// Only works if uid equals to the table's owner's user id.
+/// Returns Some(true) if successful, None otherwise.
pub fn edit_row(conn: &mut MysqlConnection, tblid: i32, cells: Vec, row_pos: i32, uid: i32) -> Option {
let owner = jrtables::get_owner_id(conn, tblid);
if owner.is_err() || owner.unwrap() != uid {
@@ -372,7 +399,9 @@ pub fn edit_row(conn: &mut MysqlConnection, tblid: i32, cells: Vec, row_
Some(true)
}
-
+/// Edit the content of a single jrcell (table cell), given its table id, the position of its row and column within the table and the new value.
+/// Only works if uid equals to the table's owner's user id.
+/// Returns Some(true) if successful, None otherwise.
pub fn edit_cell(conn: &mut MysqlConnection, tblid: i32, row_pos: i32, column_pos: i32, new_value: &String, uid: i32) -> Option {
let owner = jrtables::get_owner_id(conn, tblid);
if owner.is_err() ||
@@ -384,6 +413,9 @@ pub fn edit_cell(conn: &mut MysqlConnection, tblid: i32, row_pos: i32, column_po
Some(true)
}
+/// Change a jrcolumn's (table column's) current position within its table, given the table's id, the current position and the new position it should be moved to.
+/// Only works if uid equals to the owner's user id.
+/// Returns Some(true) if successful, None otherwise.
pub fn move_column(conn: &mut MysqlConnection, tblid:i32, column_pos: i32, new_column_pos: i32, uid: i32) -> Option {
let owner = jrtables::get_owner_id(conn, tblid);
if owner.is_err() ||
@@ -395,6 +427,10 @@ pub fn move_column(conn: &mut MysqlConnection, tblid:i32, column_pos: i32, new_c
Some(true)
}
+/// Add a new jrcolumn (table column) to an existing table.
+/// Required are the table's id, the column's name, the column's FIELDTYPE and the id of the logged in user.
+/// Only works if uid equals to the table's owner's user id.
+/// Returns the id of the newly created column if successful, None otherwise.
pub fn add_column(conn: &mut MysqlConnection, tblid: i32, name: String, clmtype: FIELDTYPE, uid: i32) -> Option {
let owner = jrtables::get_owner_id(conn, tblid);
if owner.is_err() ||
@@ -410,6 +446,9 @@ pub fn add_column(conn: &mut MysqlConnection, tblid: i32, name: String, clmtype:
Some(clmid.unwrap())
}
+/// Delete a column identified by its table's id and its position within the table.
+/// Only works if uid equals to the table's owner's id.
+/// Returns Some(true) if successful, None otherwise.
pub fn delete_column(conn: &mut MysqlConnection, tblid: i32, column_pos: i32, uid: i32) -> Option {
let owner = jrtables::get_owner_id(conn, tblid);
if owner.is_err() ||
@@ -421,6 +460,9 @@ pub fn delete_column(conn: &mut MysqlConnection, tblid: i32, column_pos: i32, ui
Some(true)
}
+/// Update a jrcolumn (table column) with a given new name, and new type. The column is identified by its table's id and its position within it.
+/// Only works if uid equals to the table's owner's id.
+/// Returns Some(true) if successful, None otherwise.
pub fn edit_column(conn: &mut MysqlConnection, tblid: i32, column_pos: i32, new_name: &String, new_type: FIELDTYPE, uid: i32) -> Option {
let owner = jrtables::get_owner_id(conn, tblid);
if owner.is_err() ||
diff --git a/src/auth.rs b/src/auth.rs
index ab5a5d9..a25f82a 100644
--- a/src/auth.rs
+++ b/src/auth.rs
@@ -15,6 +15,8 @@
* along with this program. If not, see .
*/
+//! Module responsible for handling user authentication
+
use crate::Db;
use rocket::response::Redirect;
@@ -25,9 +27,10 @@ use rocket_oauth2::{OAuth2, TokenResponse};
use reqwest::Client;
use rocket::serde::{Deserialize, json::Json};
+/// OAuth provider
pub struct RanderathIdentity;
-
+/// User attempting to log in. Username and email provided by the oauth provider.
#[derive(Deserialize)]
#[serde(crate = "rocket::serde")]
struct UnAuthUser {
@@ -35,12 +38,15 @@ struct UnAuthUser {
email: String,
}
+/// User currently logged in and authenticated.
pub struct AuthUser {
pub uid: i32,
pub uname: String,
pub email: String,
}
+/// Using the client browser's private cookies, check if the user is logged in.
+/// If yes, calculate the corresponding AuthUser, else Forward to Unauthorized.
#[rocket::async_trait]
impl<'r> request::FromRequest<'r> for AuthUser {
type Error = ();
@@ -63,6 +69,8 @@ impl<'r> request::FromRequest<'r> for AuthUser {
}
}
+/// Utility function invoked when trying to log in to fetch user data from the database, given an oauth access token.
+/// Fetch user info from oauth provider using the access token, calculate the UnAuthUser from the Json response, query the database and return the user if it exists or create it if it doesn't.
pub async fn login_or_register(conn: Db, access_token: &str) -> Option {
let ui = Client::new()
.get("https://ldap.randerath.eu/realms/master/protocol/openid-connect/userinfo")
@@ -85,16 +93,26 @@ pub async fn login_or_register(conn: Db, access_token: &str) -> Option Redirect {
+ Redirect::to(uri!(oauth_login))
+}
+
+/// Unauthorized requests are sent to the oauth provider in order for the user to authenticate.
#[catch(403)]
pub async fn redirect_to_login() -> Redirect {
Redirect::to(uri!(oauth_login()))
}
+/// Redirect to oauth provider, fetching the correct link.
#[get("/login")]
pub fn oauth_login(oauth2: OAuth2, cookies: &CookieJar<'_>) -> Redirect {
oauth2.get_redirect(cookies, &["openid"]).unwrap()
}
+/// Callback called by the oauth provider
+/// Process TokenResponse, set login cookies and log the user in.
#[get("/auth")]
pub async fn oauth_callback(conn: Db, token: TokenResponse, cookies: &CookieJar<'_>) -> Result {
let at = token.access_token().to_string();
diff --git a/src/main.rs b/src/main.rs
index dfa02b1..a51973a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -15,10 +15,17 @@
* along with this program. If not, see .
*/
+//! Rocket app to set up a platform of cloud-stored tables for keeping track of data records.
+//! Basically inventur tries to combine the ease of use of spreadsheets with the entry-based system of databases.
+//! Oauth2 is used as the authentication system, diesel mysql and the subcrate inventur_db are used for database handling.
+
#[macro_use] extern crate rocket;
+/// Views, utility methods and data structures related to user authentication
mod auth;
+/// Views, utility mehtods and data structures, related to displaying and modifying tables (everything the user sees under /table).
mod table;
+/// Data structure representing an authenticated, logged in user.
use auth::AuthUser;
use rocket::fs::{FileServer, relative};
@@ -34,10 +41,12 @@ use dotenvy::dotenv;
use rocket::Config;
use rocket::figment::providers::{Toml, Env, Format};
+/// Database connection using diesel and rocket_sync_db_pools
#[database("inventur")]
pub struct Db(diesel::MysqlConnection);
-
+/// Home page featuring a preview view of all the user's tables
+/// Needs an authenticated user.
#[get("/")]
async fn home(conn: Db, user: AuthUser) -> Template {
let uid = user.uid;
@@ -63,11 +72,13 @@ async fn home(conn: Db, user: AuthUser) -> Template {
)
}
+/// If no user is authenticated, redirect the user to authenticate with the oauth identity provider.
#[get("/", rank=2)]
async fn login_home() -> Redirect {
Redirect::to(uri!(auth::oauth_login()))
}
+/// Serve the favicon as if it were in /favicon.ico
#[get("/favicon.ico")]
async fn favicon() -> Redirect {
Redirect::to(uri!("/img/favicon.ico"))
@@ -75,6 +86,7 @@ async fn favicon() -> Redirect {
#[launch]
async fn rocket() -> _ {
+ /// Load configuration from a file called .env in the project's root.
dotenv().ok();
let cfg = Config::figment()
@@ -82,14 +94,23 @@ async fn rocket() -> _ {
rocket::custom(cfg)
+ /// Use tera templates
.attach(Template::fairing())
+ /// Connect to mysql db
.attach(Db::fairing())
+ /// Set up oauth
.attach(OAuth2::::fairing("oauth"))
+ /// Everything related to the home page and login
.mount("/", routes![auth::oauth_login, auth::oauth_callback, home, login_home, favicon])
+ /// Everything related to the table view and modifying the table as an object (as opposed to its rows and columns).
.mount("/table", routes![table::table, table::table_sec, table::edit_tname, table::create_table, table::import_table, table::delete_table])
+ /// Modify table rows and their contents
.mount("/row", routes![table::new_entry, table::edit_entry, table::delete_entry])
+ /// Modify the table's columns, their names and types.
.mount("/column", routes![table::delete_column, table::edit_column])
+ /// If not logged in, redirect to oauth login
.register("/", catchers![auth::redirect_to_login])
+ /// Serve static files in the corresponding subdirs of /static.
.mount("/img", FileServer::from(relative!("static/img")))
.mount("/css", FileServer::from(relative!("static/css")))
.mount("/js", FileServer::from(relative!("static/js")))
diff --git a/src/table.rs b/src/table.rs
index 592cf9f..23f4cb9 100644
--- a/src/table.rs
+++ b/src/table.rs
@@ -16,10 +16,17 @@
* along with this program. If not, see .
*/
+//! Module responsible for handling display, query and modification of tables, rows, columns and cells.
+
+/// Everything related to displaying the tables
pub mod table_view;
+/// Everything related to manipulating table objects (as opposed to its member).
pub mod table_manipulate_table;
+/// Everything related to manipulating table columns and their types.
pub mod table_manipulate_column;
+/// Everything related to manipulating table rows and cells.
pub mod table_manipulate_entry;
+/// Structs used to handle form data for manipulating the database.
pub mod forms;
pub use self::table_view::*;
diff --git a/src/table/forms.rs b/src/table/forms.rs
index d804f87..533fdf6 100644
--- a/src/table/forms.rs
+++ b/src/table/forms.rs
@@ -16,6 +16,8 @@
* along with this program. If not, see .
*/
+//! Submodule holding structs relevant to handle form data.
+
use rocket::form::Form;
use rocket::serde::{Serialize, Deserialize};
@@ -59,6 +61,7 @@ pub struct DeleteEntry {
pub row_pos: i32,
}
+/// Edit a table's name
#[derive(FromForm)]
pub struct EditTname {
pub tblid: i32,
@@ -85,8 +88,4 @@ pub struct DeleteTable {
pub tblid: i32,
}
-#[derive(Serialize)]
-#[serde(crate = "rocket::serde")]
-pub struct JRows {
- pub rows: Vec>,
-}
+
diff --git a/src/table/table_manipulate_column.rs b/src/table/table_manipulate_column.rs
index 3076c14..015e217 100644
--- a/src/table/table_manipulate_column.rs
+++ b/src/table/table_manipulate_column.rs
@@ -16,6 +16,9 @@
* along with this program. If not, see .
*/
+//! Submodule responsible for manipulating and creating a table's column.
+//! For all methods in this module, the authenticated user must be the table's owner.
+
use crate::auth;
use crate::Db;
use crate::table::forms;
@@ -33,6 +36,7 @@ use auth::AuthUser;
use self::forms::{NewColumn, EditColumn, DeleteColumn};
use self::table_view::rocket_uri_macro_table_sec;
+/// Add a new column to an existing table
#[post("/create", data="")]
pub async fn create_column(conn: Db, data: Form, user: AuthUser) -> Result {
let uid = user.uid;
@@ -44,6 +48,7 @@ pub async fn create_column(conn: Db, data: Form, user: AuthUser) -> R
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
}
+/// Edit a column (its name and type)
#[post("/edit", data="")]
pub async fn edit_column(conn: Db, data: Form, user: AuthUser) -> Result {
let uid = user.uid;
@@ -56,6 +61,7 @@ pub async fn edit_column(conn: Db, data: Form, user: AuthUser) -> Re
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
}
+/// Delete a column
#[post("/delete", data="")]
pub async fn delete_column(conn: Db, data: Form, user: AuthUser) -> Result {
let uid = user.uid;
diff --git a/src/table/table_manipulate_entry.rs b/src/table/table_manipulate_entry.rs
index 649c0fa..7fd5999 100644
--- a/src/table/table_manipulate_entry.rs
+++ b/src/table/table_manipulate_entry.rs
@@ -16,6 +16,9 @@
* along with this program. If not, see .
*/
+//! Submodule responsible for manipulating and creating rows and cells to existing tables
+//! For all methods in this module, the authenticated user has to be the table's owner
+
use crate::auth;
use crate::Db;
use crate::table::forms;
@@ -32,6 +35,7 @@ use auth::AuthUser;
use self::forms::{NewEntry, EditEntry, DeleteEntry};
use self::table_view::rocket_uri_macro_table_sec;
+/// Create a new a new row to an existing table, filling all its columns with the supplied values.
#[post("/new", data="")]
pub async fn new_entry(conn: Db, data: Form, user: AuthUser) -> Result {
let uid = user.uid;
@@ -43,6 +47,7 @@ pub async fn new_entry(conn: Db, data: Form, user: AuthUser) -> Result
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
}
+/// Edit a row's cell contents, the row being identified by its table and its position within the table.
#[post("/edit", data="")]
pub async fn edit_entry(conn: Db, data: Form, user: AuthUser) -> Result {
let uid = user.uid;
@@ -55,6 +60,7 @@ pub async fn edit_entry(conn: Db, data: Form, user: AuthUser) -> Resu
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
}
+/// Delete a row identified by its table and its position within the table.
#[post("/delete", data="")]
pub async fn delete_entry(conn: Db, data: Form, user: AuthUser) -> Result {
let uid = user.uid;
diff --git a/src/table/table_manipulate_table.rs b/src/table/table_manipulate_table.rs
index 6e08bc2..b3b690a 100644
--- a/src/table/table_manipulate_table.rs
+++ b/src/table/table_manipulate_table.rs
@@ -16,6 +16,8 @@
* along with this program. If not, see .
*/
+//! Submodule responsible for manipulating or creating table objects (not individual rows, columns or entries).
+
use crate::auth;
use crate::Db;
use crate::table::forms;
@@ -34,6 +36,8 @@ use inventur_db::FIELDTYPE;
use self::forms::{NewTable, DeleteTable, ImportTable, EditTname};
use self::table_view::rocket_uri_macro_table_sec;
+/// Delete a table.
+/// The authenticated user must be the table's owner (ensured by inventur_db).
#[post("/delete", data="")]
pub async fn delete_table(conn: Db, data: Form, user: AuthUser) -> Result {
let uid = user.uid;
@@ -44,6 +48,7 @@ pub async fn delete_table(conn: Db, data: Form, user: AuthUser) ->
Ok(Redirect::to(uri!("/")))
}
+/// Create a new table with the given fields and name, make the authenticated user the new table's owner.
#[post("/create", data="")]
pub async fn create_table(conn: Db, data: Form, user: AuthUser) -> Result {
let uid = user.uid;
@@ -54,6 +59,8 @@ pub async fn create_table(conn: Db, data: Form, user: AuthUser) -> Res
Ok(Redirect::to(uri!("/table", table_sec(tblid.unwrap()))))
}
+/// Create a new table including rows by importing a csv file.
+/// Table's column types default to text.
#[post("/import", data="")]
pub async fn import_table(conn: Db, data: Json, user: AuthUser) -> Result {
let uid = user.uid;
@@ -70,6 +77,7 @@ pub async fn import_table(conn: Db, data: Json, user: AuthUser) ->
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
}
+/// Edit a table object (the table's name).
#[post("/name/edit", data="")]
pub async fn edit_tname(conn: Db, data: Form, user: AuthUser) -> Result {
let uid = user.uid;
diff --git a/src/table/table_view.rs b/src/table/table_view.rs
index 968eb6f..50ca862 100644
--- a/src/table/table_view.rs
+++ b/src/table/table_view.rs
@@ -16,6 +16,8 @@
* along with this program. If not, see .
*/
+//! Submodule responsible for handling fetching and displaying a table on the /table view.
+
use crate::auth;
use crate::Db;
use inventur_db;
@@ -24,6 +26,9 @@ use auth::AuthUser;
use rocket_dyn_templates::{Template, context};
use rocket::response::Redirect;
+/// View an authenticated user sees visiting the /table/ page
+/// Optional get parametes are the field to sort by, the direction of the sort, the fields to search in and the value to search for.
+/// Returns None if the table does not exist, could not be fetched or if the user is not the table's owner.
#[get("/?&&&")]
pub async fn table(conn: Db, tid: i32, sort_dir: Option, sort_field: Option, search_fields: Option>, search_value: Option, user: AuthUser) -> Option {
let uid = user.uid;
@@ -91,6 +96,8 @@ pub async fn table(conn: Db, tid: i32, sort_dir: Option, sort_field: Option<
)
}
+/// View to redirect a post request handled to manipulate a table or its display representation back to the (new) table view.
+/// Also uses table() but nulls all optional fields.
#[get("/", rank=2)]
pub async fn table_sec(conn: Db, tid: i32, user: AuthUser) -> Redirect {
let nus : Option = None;
@@ -100,6 +107,7 @@ pub async fn table_sec(conn: Db, tid: i32, user: AuthUser) -> Redirect {
Redirect::to(uri!("/table", table(tid, nu8, nus, nvi32, ns)))
}
+/// Utility function to get all a user's tables as their ids.
pub async fn get_tids(conn: &Db, uid: i32) -> (Vec, Vec) {
let mut tids = conn.run(move |c| inventur_db::get_user_tblids(c, uid)).await;
let tnames;