diff --git a/src/cli.rs b/src/bin/pslink/cli.rs similarity index 99% rename from src/cli.rs rename to src/bin/pslink/cli.rs index 12f189d..e8703dd 100644 --- a/src/cli.rs +++ b/src/bin/pslink/cli.rs @@ -10,7 +10,7 @@ use std::{ path::PathBuf, }; -use crate::{models::NewUser, models::User, ServerConfig, ServerError}; +use pslink::{models::NewUser, models::User, ServerConfig, ServerError}; use slog::{Drain, Logger}; @@ -192,7 +192,7 @@ async fn parse_args_to_config(config: ArgMatches<'_>, log: Logger) -> ServerConf let protocol = config .value_of("protocol") .expect("Failed to read the protocol value") - .parse::() + .parse::() .expect("Failed to parse the protocol"); let log = log.new(slog_o!("host" => public_url.clone())); diff --git a/src/main.rs b/src/bin/pslink/main.rs similarity index 53% rename from src/main.rs rename to src/bin/pslink/main.rs index c5faa48..ac78b8a 100644 --- a/src/main.rs +++ b/src/bin/pslink/main.rs @@ -15,179 +15,15 @@ extern crate slog; extern crate slog_async; mod cli; -mod forms; -pub mod models; -mod queries; mod views; - -use std::{fmt::Display, path::PathBuf, str::FromStr}; +use pslink::ServerConfig; use actix_identity::{CookieIdentityPolicy, IdentityService}; -use actix_web::{web, App, HttpResponse, HttpServer}; +use actix_web::{web, App, HttpServer}; use fluent_templates::{static_loader, FluentLoader}; -use qrcode::types::QrError; -use sqlx::{Pool, Sqlite}; use tera::Tera; -#[derive(Debug)] -pub enum ServerError { - Argonautic, - Database(sqlx::Error), - DatabaseMigration(sqlx::migrate::MigrateError), - Environment, - Template(tera::Error), - Qr(QrError), - Io(std::io::Error), - User(String), -} - -impl std::fmt::Display for ServerError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Argonautic => write!(f, "Argonautica Error"), - Self::Database(e) => write!(f, "Database Error: {}", e), - Self::DatabaseMigration(e) => { - write!(f, "Migration Error: {}", e) - } - Self::Environment => write!(f, "Environment Error"), - Self::Template(e) => write!(f, "Template Error: {:?}", e), - Self::Qr(e) => write!(f, "Qr Code Error: {:?}", e), - Self::Io(e) => write!(f, "IO Error: {:?}", e), - Self::User(data) => write!(f, "{}", data), - } - } -} - -impl actix_web::error::ResponseError for ServerError { - fn error_response(&self) -> HttpResponse { - match self { - Self::Argonautic => HttpResponse::InternalServerError().json("Argonautica Error"), - Self::Database(e) => { - HttpResponse::InternalServerError().json(format!("Database Error: {:?}", e)) - } - Self::DatabaseMigration(_) => { - unimplemented!("A migration error should never be rendered") - } - Self::Environment => HttpResponse::InternalServerError().json("Environment Error"), - Self::Template(e) => { - HttpResponse::InternalServerError().json(format!("Template Error: {:?}", e)) - } - Self::Qr(e) => { - HttpResponse::InternalServerError().json(format!("Qr Code Error: {:?}", e)) - } - Self::Io(e) => HttpResponse::InternalServerError().json(format!("IO Error: {:?}", e)), - Self::User(data) => HttpResponse::InternalServerError().json(data), - } - } -} - -impl From for ServerError { - fn from(e: std::env::VarError) -> Self { - eprintln!("Environment error {:?}", e); - Self::Environment - } -} - -impl From for ServerError { - fn from(err: sqlx::Error) -> Self { - eprintln!("Database error {:?}", err); - Self::Database(err) - } -} -impl From for ServerError { - fn from(err: sqlx::migrate::MigrateError) -> Self { - eprintln!("Database error {:?}", err); - Self::DatabaseMigration(err) - } -} - -impl From for ServerError { - fn from(e: argonautica::Error) -> Self { - eprintln!("Authentication error {:?}", e); - Self::Argonautic - } -} -impl From for ServerError { - fn from(e: tera::Error) -> Self { - eprintln!("Template error {:?}", e); - Self::Template(e) - } -} -impl From for ServerError { - fn from(e: QrError) -> Self { - eprintln!("Template error {:?}", e); - Self::Qr(e) - } -} -impl From for ServerError { - fn from(e: std::io::Error) -> Self { - eprintln!("IO error {:?}", e); - Self::Io(e) - } -} - -#[derive(Debug, Clone)] -enum Protocol { - Http, - Https, -} - -impl Display for Protocol { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Http => f.write_str("http"), - Self::Https => f.write_str("https"), - } - } -} - -impl FromStr for Protocol { - type Err = ServerError; - - fn from_str(s: &str) -> Result { - match s { - "http" => Ok(Self::Http), - "https" => Ok(Self::Https), - _ => Err(ServerError::User("Failed to parse Protocol".to_owned())), - } - } -} - -#[derive(Debug, Clone)] -pub(crate) struct ServerConfig { - secret: String, - db: PathBuf, - db_pool: Pool, - public_url: String, - internal_ip: String, - port: u32, - protocol: Protocol, - log: slog::Logger, - empty_forward_url: String, - brand_name: String, -} - -impl ServerConfig { - pub fn to_env_strings(&self) -> Vec { - vec![ - format!("PSLINK_DATABASE=\"{}\"\n", self.db.display()), - format!("PSLINK_PORT={}\n", self.port), - format!("PSLINK_PUBLIC_URL=\"{}\"\n", self.public_url), - format!("PSLINK_EMPTY_FORWARD_URL=\"{}\"\n", self.empty_forward_url), - format!("PSLINK_BRAND_NAME=\"{}\"\n", self.brand_name), - format!("PSLINK_IP=\"{}\"\n", self.internal_ip), - format!("PSLINK_PROTOCOL=\"{}\"\n", self.protocol), - concat!( - "# The SECRET_KEY variable is used for password encryption.\n", - "# If it is changed all existing passwords are invalid.\n" - ) - .to_owned(), - format!("PSLINK_SECRET=\"{}\"\n", self.secret), - ] - } -} - include!(concat!(env!("OUT_DIR"), "/generated.rs")); static_loader! { @@ -204,38 +40,41 @@ fn build_tera() -> Tera { tera.register_function("fluent", FluentLoader::new(&*LOCALES)); tera.add_raw_templates(vec![ - ("admin.html", include_str!("../templates/admin.html")), - ("base.html", include_str!("../templates/base.html")), + ("admin.html", include_str!("../../../templates/admin.html")), + ("base.html", include_str!("../../../templates/base.html")), ( "edit_link.html", - include_str!("../templates/edit_link.html"), + include_str!("../../../templates/edit_link.html"), ), ( "edit_profile.html", - include_str!("../templates/edit_profile.html"), + include_str!("../../../templates/edit_profile.html"), ), ( "index_users.html", - include_str!("../templates/index_users.html"), + include_str!("../../../templates/index_users.html"), ), - ("index.html", include_str!("../templates/index.html")), - ("login.html", include_str!("../templates/login.html")), + ("index.html", include_str!("../../../templates/index.html")), + ("login.html", include_str!("../../../templates/login.html")), ( "not_found.html", - include_str!("../templates/not_found.html"), + include_str!("../../../templates/not_found.html"), + ), + ( + "signup.html", + include_str!("../../../templates/signup.html"), ), - ("signup.html", include_str!("../templates/signup.html")), ( "submission.html", - include_str!("../templates/submission.html"), + include_str!("../../../templates/submission.html"), ), ( "view_link.html", - include_str!("../templates/view_link.html"), + include_str!("../../../templates/view_link.html"), ), ( "view_profile.html", - include_str!("../templates/view_profile.html"), + include_str!("../../../templates/view_profile.html"), ), ]) .expect("failed to parse templates"); diff --git a/src/views.rs b/src/bin/pslink/views.rs similarity index 94% rename from src/views.rs rename to src/bin/pslink/views.rs index e0b0573..c2d51f7 100644 --- a/src/views.rs +++ b/src/bin/pslink/views.rs @@ -15,10 +15,10 @@ use image::{DynamicImage, ImageOutputFormat, Luma}; use qrcode::{render::svg, QrCode}; use tera::{Context, Tera}; -use super::forms::LinkForm; -use super::models::{LoginUser, NewUser}; -use crate::queries; -use crate::ServerError; +use pslink::forms::LinkForm; +use pslink::models::{LoginUser, NewUser}; +use pslink::queries; +use pslink::ServerError; fn redirect_builder(target: &str) -> HttpResponse { HttpResponse::SeeOther() @@ -62,7 +62,7 @@ fn detect_language(request: &HttpRequest) -> Result { } /// Show the list of all available links if a user is authenticated -pub(crate) async fn index( +pub async fn index( tera: web::Data, config: web::Data, id: Identity, @@ -80,7 +80,7 @@ pub(crate) async fn index( } /// Show the list of all available links if a user is authenticated -pub(crate) async fn index_users( +pub async fn index_users( tera: web::Data, config: web::Data, id: Identity, @@ -97,7 +97,7 @@ pub(crate) async fn index_users( Ok(redirect_builder("/admin/login")) } } -pub(crate) async fn view_link_empty( +pub async fn view_link_empty( tera: web::Data, config: web::Data, id: Identity, @@ -105,7 +105,7 @@ pub(crate) async fn view_link_empty( view_link(tera, config, id, web::Path::from("".to_owned())).await } -pub(crate) async fn view_link( +pub async fn view_link( tera: web::Data, config: web::Data, id: Identity, @@ -144,7 +144,7 @@ pub(crate) async fn view_link( } } -pub(crate) async fn view_profile( +pub async fn view_profile( tera: web::Data, config: web::Data, id: Identity, @@ -171,7 +171,7 @@ pub(crate) async fn view_profile( } } -pub(crate) async fn edit_profile( +pub async fn edit_profile( tera: web::Data, config: web::Data, id: Identity, @@ -197,7 +197,7 @@ pub(crate) async fn edit_profile( } } -pub(crate) async fn process_edit_profile( +pub async fn process_edit_profile( data: web::Form, config: web::Data, id: Identity, @@ -213,7 +213,7 @@ pub(crate) async fn process_edit_profile( } } -pub(crate) async fn download_png( +pub async fn download_png( id: Identity, config: web::Data, link_code: web::Path, @@ -237,7 +237,7 @@ pub(crate) async fn download_png( } } -pub(crate) async fn signup( +pub async fn signup( tera: web::Data, config: web::Data, id: Identity, @@ -257,7 +257,7 @@ pub(crate) async fn signup( } } -pub(crate) async fn process_signup( +pub async fn process_signup( data: web::Form, config: web::Data, id: Identity, @@ -270,7 +270,7 @@ pub(crate) async fn process_signup( } } -pub(crate) async fn toggle_admin( +pub async fn toggle_admin( data: web::Path, config: web::Data, id: Identity, @@ -282,7 +282,7 @@ pub(crate) async fn toggle_admin( ))) } -pub(crate) async fn set_language( +pub async fn set_language( data: web::Path, config: web::Data, id: Identity, @@ -291,7 +291,7 @@ pub(crate) async fn set_language( Ok(redirect_builder("/admin/index/")) } -pub(crate) async fn login( +pub async fn login( tera: web::Data, id: Identity, config: web::Data, @@ -311,7 +311,7 @@ pub(crate) async fn login( Ok(HttpResponse::Ok().body(rendered)) } -pub(crate) async fn process_login( +pub async fn process_login( data: web::Form, config: web::Data, id: Identity, @@ -343,12 +343,12 @@ pub(crate) async fn process_login( } } -pub(crate) async fn logout(id: Identity) -> Result { +pub async fn logout(id: Identity) -> Result { id.forget(); Ok(redirect_builder("/admin/login/")) } -pub(crate) async fn redirect( +pub async fn redirect( tera: web::Data, config: web::Data, data: web::Path, @@ -381,13 +381,13 @@ pub(crate) async fn redirect( } } -pub(crate) async fn redirect_empty( +pub async fn redirect_empty( config: web::Data, ) -> Result { Ok(redirect_builder(&config.empty_forward_url)) } -pub(crate) async fn create_link( +pub async fn create_link( tera: web::Data, config: web::Data, id: Identity, @@ -407,7 +407,7 @@ pub(crate) async fn create_link( } } -pub(crate) async fn process_link_creation( +pub async fn process_link_creation( data: web::Form, config: web::Data, id: Identity, @@ -419,7 +419,7 @@ pub(crate) async fn process_link_creation( ))) } -pub(crate) async fn edit_link( +pub async fn edit_link( tera: web::Data, config: web::Data, id: Identity, @@ -436,7 +436,7 @@ pub(crate) async fn edit_link( } Ok(redirect_builder("/admin/login/")) } -pub(crate) async fn process_link_edit( +pub async fn process_link_edit( data: web::Form, config: web::Data, id: Identity, @@ -451,7 +451,7 @@ pub(crate) async fn process_link_edit( } } -pub(crate) async fn process_link_delete( +pub async fn process_link_delete( id: Identity, config: web::Data, link_code: web::Path, diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..62c0edf --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,185 @@ +extern crate sqlx; +#[allow(unused_imports)] +#[macro_use( + slog_o, + slog_info, + slog_warn, + slog_error, + slog_log, + slog_record, + slog_record_static, + slog_b, + slog_kv +)] +extern crate slog; +extern crate slog_async; + +pub mod forms; +pub mod models; +pub mod queries; + +use std::{fmt::Display, path::PathBuf, str::FromStr}; + +use actix_web::HttpResponse; + +use qrcode::types::QrError; +use sqlx::{Pool, Sqlite}; + +#[derive(Debug)] +pub enum ServerError { + Argonautic, + Database(sqlx::Error), + DatabaseMigration(sqlx::migrate::MigrateError), + Environment, + Template(tera::Error), + Qr(QrError), + Io(std::io::Error), + User(String), +} + +impl std::fmt::Display for ServerError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Argonautic => write!(f, "Argonautica Error"), + Self::Database(e) => write!(f, "Database Error: {}", e), + Self::DatabaseMigration(e) => { + write!(f, "Migration Error: {}", e) + } + Self::Environment => write!(f, "Environment Error"), + Self::Template(e) => write!(f, "Template Error: {:?}", e), + Self::Qr(e) => write!(f, "Qr Code Error: {:?}", e), + Self::Io(e) => write!(f, "IO Error: {:?}", e), + Self::User(data) => write!(f, "{}", data), + } + } +} + +impl actix_web::error::ResponseError for ServerError { + fn error_response(&self) -> HttpResponse { + match self { + Self::Argonautic => HttpResponse::InternalServerError().json("Argonautica Error"), + Self::Database(e) => { + HttpResponse::InternalServerError().json(format!("Database Error: {:?}", e)) + } + Self::DatabaseMigration(_) => { + unimplemented!("A migration error should never be rendered") + } + Self::Environment => HttpResponse::InternalServerError().json("Environment Error"), + Self::Template(e) => { + HttpResponse::InternalServerError().json(format!("Template Error: {:?}", e)) + } + Self::Qr(e) => { + HttpResponse::InternalServerError().json(format!("Qr Code Error: {:?}", e)) + } + Self::Io(e) => HttpResponse::InternalServerError().json(format!("IO Error: {:?}", e)), + Self::User(data) => HttpResponse::InternalServerError().json(data), + } + } +} + +impl From for ServerError { + fn from(e: std::env::VarError) -> Self { + eprintln!("Environment error {:?}", e); + Self::Environment + } +} + +impl From for ServerError { + fn from(err: sqlx::Error) -> Self { + eprintln!("Database error {:?}", err); + Self::Database(err) + } +} +impl From for ServerError { + fn from(err: sqlx::migrate::MigrateError) -> Self { + eprintln!("Database error {:?}", err); + Self::DatabaseMigration(err) + } +} + +impl From for ServerError { + fn from(e: argonautica::Error) -> Self { + eprintln!("Authentication error {:?}", e); + Self::Argonautic + } +} +impl From for ServerError { + fn from(e: tera::Error) -> Self { + eprintln!("Template error {:?}", e); + Self::Template(e) + } +} +impl From for ServerError { + fn from(e: QrError) -> Self { + eprintln!("Template error {:?}", e); + Self::Qr(e) + } +} +impl From for ServerError { + fn from(e: std::io::Error) -> Self { + eprintln!("IO error {:?}", e); + Self::Io(e) + } +} + +#[derive(Debug, Clone)] +pub enum Protocol { + Http, + Https, +} + +impl Display for Protocol { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Http => f.write_str("http"), + Self::Https => f.write_str("https"), + } + } +} + +impl FromStr for Protocol { + type Err = ServerError; + + fn from_str(s: &str) -> Result { + match s { + "http" => Ok(Self::Http), + "https" => Ok(Self::Https), + _ => Err(ServerError::User("Failed to parse Protocol".to_owned())), + } + } +} + +#[derive(Debug, Clone)] +pub struct ServerConfig { + pub secret: String, + pub db: PathBuf, + pub db_pool: Pool, + pub public_url: String, + pub internal_ip: String, + pub port: u32, + pub protocol: Protocol, + pub log: slog::Logger, + pub empty_forward_url: String, + pub brand_name: String, +} + +impl ServerConfig { + #[must_use] + pub fn to_env_strings(&self) -> Vec { + vec![ + format!("PSLINK_DATABASE=\"{}\"\n", self.db.display()), + format!("PSLINK_PORT={}\n", self.port), + format!("PSLINK_PUBLIC_URL=\"{}\"\n", self.public_url), + format!("PSLINK_EMPTY_FORWARD_URL=\"{}\"\n", self.empty_forward_url), + format!("PSLINK_BRAND_NAME=\"{}\"\n", self.brand_name), + format!("PSLINK_IP=\"{}\"\n", self.internal_ip), + format!("PSLINK_PROTOCOL=\"{}\"\n", self.protocol), + concat!( + "# The SECRET_KEY variable is used for password encryption.\n", + "# If it is changed all existing passwords are invalid.\n" + ) + .to_owned(), + format!("PSLINK_SECRET=\"{}\"\n", self.secret), + ] + } +} diff --git a/src/models.rs b/src/models.rs index ba7bbb4..6d43ab6 100644 --- a/src/models.rs +++ b/src/models.rs @@ -24,7 +24,12 @@ impl User { .await; user.map_err(ServerError::Database) } - pub(crate) async fn get_user_by_name( + + /// get a user by its username + /// + /// # Errors + /// fails with [`ServerError`] if the user does not exist or the database cannot be acessed. + pub async fn get_user_by_name( name: &str, server_config: &ServerConfig, ) -> Result { @@ -63,11 +68,11 @@ impl User { .await?; Ok(()) } - - pub(crate) async fn toggle_admin( - self, - server_config: &ServerConfig, - ) -> Result<(), ServerError> { + /// Change an admin user to normal user and a normal user to admin + /// + /// # Errors + /// fails with [`ServerError`] if the database cannot be acessed. (the user should exist) + pub async fn toggle_admin(self, server_config: &ServerConfig) -> Result<(), ServerError> { let new_role = 2 - (self.role + 1) % 2; sqlx::query!("UPDATE users SET role = ? where id = ?", new_role, self.id) .execute(&server_config.db_pool) @@ -90,7 +95,13 @@ impl User { Ok(()) } - pub(crate) async fn count_admins(server_config: &ServerConfig) -> Result { + /// Count the admin accounts + /// + /// this is usefull for determining if any admins exist at all. + /// + /// # Errors + /// fails with [`ServerError`] if the database cannot be acessed. + pub async fn count_admins(server_config: &ServerConfig) -> Result { let num = sqlx::query_as!(Count, "select count(*) as number from users where role = 2") .fetch_one(&server_config.db_pool) .await?; @@ -106,7 +117,11 @@ pub struct NewUser { } impl NewUser { - pub(crate) fn new( + /// Create a new user that can then be inserted in the database + /// + /// # Errors + /// fails with [`ServerError`] if the password could not be encrypted. + pub fn new( username: String, email: String, password: &str, @@ -136,10 +151,11 @@ impl NewUser { Ok(hash) } - pub(crate) async fn insert_user( - &self, - server_config: &ServerConfig, - ) -> Result<(), ServerError> { + /// Insert this user into the database + /// + /// # Errors + /// fails with [`ServerError`] if the database cannot be acessed. + pub async fn insert_user(&self, server_config: &ServerConfig) -> Result<(), ServerError> { sqlx::query!( "Insert into users ( username, diff --git a/src/queries.rs b/src/queries.rs index c30c348..f40dc20 100644 --- a/src/queries.rs +++ b/src/queries.rs @@ -30,7 +30,10 @@ impl Role { } /// queries the user matching the given [`actix_identity::Identity`] and determins its authentication and permission level. Returns a [`Role`] containing the user if it is authenticated. -pub(crate) async fn authenticate( +/// +/// # Errors +/// Fails only if there are issues using the database. +pub async fn authenticate( id: &Identity, server_config: &ServerConfig, ) -> Result { @@ -62,7 +65,10 @@ pub struct FullLink { } /// Returns a List of `FullLink` meaning `Links` enriched by their author and statistics. This returns all links if the user is either Admin or Regular user. -pub(crate) async fn list_all_allowed( +/// +/// # Errors +/// Fails with [`ServerError`] if access to the database fails. +pub async fn list_all_allowed( id: &Identity, server_config: &ServerConfig, ) -> Result, ServerError> { @@ -126,7 +132,10 @@ pub(crate) async fn list_all_allowed( } /// Only admins can list all users -pub(crate) async fn list_users( +/// +/// # Errors +/// Fails with [`ServerError`] if access to the database fails or this user does not have permissions. +pub async fn list_users( id: &Identity, server_config: &ServerConfig, ) -> Result, ServerError> { @@ -151,7 +160,11 @@ pub struct Item { } /// Get a user if permissions are accordingly -pub(crate) async fn get_user( +/// +/// # Errors +/// Fails with [`ServerError`] if access to the database fails or this user does not have permissions. +#[allow(clippy::clippy::missing_panics_doc)] +pub async fn get_user( id: &Identity, user_id: &str, server_config: &ServerConfig, @@ -181,7 +194,10 @@ pub(crate) async fn get_user( } /// Get a user **without permission checks** (needed for login) -pub(crate) async fn get_user_by_name( +/// +/// # Errors +/// Fails with [`ServerError`] if access to the database fails. +pub async fn get_user_by_name( username: &str, server_config: &ServerConfig, ) -> Result { @@ -189,7 +205,11 @@ pub(crate) async fn get_user_by_name( Ok(user) } -pub(crate) async fn create_user( +/// Create a new user and save it to the database +/// +/// # Errors +/// Fails with [`ServerError`] if access to the database fails, this user does not have permissions or the user already exists. +pub async fn create_user( id: &Identity, data: &web::Form, server_config: &ServerConfig, @@ -222,7 +242,11 @@ pub(crate) async fn create_user( /// Take a [`actix_web::web::Form`] and update the corresponding entry in the database. /// The password is only updated if a new password of at least 4 characters is provided. /// The `user_id` is never changed. -pub(crate) async fn update_user( +/// +/// # Errors +/// Fails with [`ServerError`] if access to the database fails, this user does not have permissions, or the given data is malformed. +#[allow(clippy::clippy::missing_panics_doc)] +pub async fn update_user( id: &Identity, user_id: &str, server_config: &ServerConfig, @@ -266,8 +290,11 @@ pub(crate) async fn update_user( Err(ServerError::User("Permission denied".to_owned())) } } - -pub(crate) async fn toggle_admin( +/// Demote an admin user to a normal user or promote a normal user to admin privileges. +/// +/// # Errors +/// Fails with [`ServerError`] if access to the database fails, this user does not have permissions or the user does not exist. +pub async fn toggle_admin( id: &Identity, user_id: &str, server_config: &ServerConfig, @@ -305,7 +332,11 @@ pub(crate) async fn toggle_admin( } } -pub(crate) async fn set_language( +/// Set the language of a given user +/// +/// # Errors +/// Fails with [`ServerError`] if access to the database fails, this user does not have permissions or the language given is invalid. +pub async fn set_language( id: &Identity, lang_code: &str, server_config: &ServerConfig, @@ -326,7 +357,10 @@ pub(crate) async fn set_language( } /// Get one link if permissions are accordingly. -pub(crate) async fn get_link( +/// +/// # Errors +/// Fails with [`ServerError`] if access to the database fails or this user does not have permissions. +pub async fn get_link( id: &Identity, link_code: &str, server_config: &ServerConfig, @@ -341,7 +375,10 @@ pub(crate) async fn get_link( } /// Get link **without authentication** -pub(crate) async fn get_link_simple( +/// +/// # Errors +/// Fails with [`ServerError`] if access to the database fails. +pub async fn get_link_simple( link_code: &str, server_config: &ServerConfig, ) -> Result { @@ -351,19 +388,23 @@ pub(crate) async fn get_link_simple( slog_info!(server_config.log, "Foun d link for {:?}", link); Ok(link) } + /// Click on a link -pub(crate) async fn click_link( - link_id: i64, - server_config: &ServerConfig, -) -> Result<(), ServerError> { +/// +/// # Errors +/// Fails with [`ServerError`] if access to the database fails. +pub async fn click_link(link_id: i64, server_config: &ServerConfig) -> Result<(), ServerError> { slog_info!(server_config.log, "Clicking on {:?}", link_id); let new_click = NewClick::new(link_id); new_click.insert_click(server_config).await?; Ok(()) } -/// Click on a link -pub(crate) async fn delete_link( +/// Delete a link +/// +/// # Errors +/// Fails with [`ServerError`] if access to the database fails or this user does not have permissions. +pub async fn delete_link( id: &Identity, link_code: &str, server_config: &ServerConfig, @@ -379,7 +420,10 @@ pub(crate) async fn delete_link( } /// Update a link if the user is admin or it is its own link. -pub(crate) async fn update_link( +/// +/// # Errors +/// Fails with [`ServerError`] if access to the database fails or this user does not have permissions. +pub async fn update_link( id: &Identity, link_code: &str, data: web::Form, @@ -415,7 +459,11 @@ pub(crate) async fn update_link( } } -pub(crate) async fn create_link( +/// Create a new link +/// +/// # Errors +/// Fails with [`ServerError`] if access to the database fails or this user does not have permissions. +pub async fn create_link( id: &Identity, data: web::Form, server_config: &ServerConfig,