parent
0ed010b78f
commit
34b957332e
@ -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::<crate::Protocol>()
|
||||
.parse::<pslink::Protocol>()
|
||||
.expect("Failed to parse the protocol");
|
||||
|
||||
let log = log.new(slog_o!("host" => public_url.clone()));
|
@ -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<std::env::VarError> for ServerError {
|
||||
fn from(e: std::env::VarError) -> Self {
|
||||
eprintln!("Environment error {:?}", e);
|
||||
Self::Environment
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sqlx::Error> for ServerError {
|
||||
fn from(err: sqlx::Error) -> Self {
|
||||
eprintln!("Database error {:?}", err);
|
||||
Self::Database(err)
|
||||
}
|
||||
}
|
||||
impl From<sqlx::migrate::MigrateError> for ServerError {
|
||||
fn from(err: sqlx::migrate::MigrateError) -> Self {
|
||||
eprintln!("Database error {:?}", err);
|
||||
Self::DatabaseMigration(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<argonautica::Error> for ServerError {
|
||||
fn from(e: argonautica::Error) -> Self {
|
||||
eprintln!("Authentication error {:?}", e);
|
||||
Self::Argonautic
|
||||
}
|
||||
}
|
||||
impl From<tera::Error> for ServerError {
|
||||
fn from(e: tera::Error) -> Self {
|
||||
eprintln!("Template error {:?}", e);
|
||||
Self::Template(e)
|
||||
}
|
||||
}
|
||||
impl From<QrError> for ServerError {
|
||||
fn from(e: QrError) -> Self {
|
||||
eprintln!("Template error {:?}", e);
|
||||
Self::Qr(e)
|
||||
}
|
||||
}
|
||||
impl From<std::io::Error> 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<Self, Self::Err> {
|
||||
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<Sqlite>,
|
||||
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<String> {
|
||||
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");
|
@ -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<String, ServerError> {
|
||||
}
|
||||
|
||||
/// Show the list of all available links if a user is authenticated
|
||||
pub(crate) async fn index(
|
||||
pub async fn index(
|
||||
tera: web::Data<Tera>,
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
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<Tera>,
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
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<Tera>,
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
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<Tera>,
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
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<Tera>,
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
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<Tera>,
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
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<NewUser>,
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
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<crate::ServerConfig>,
|
||||
link_code: web::Path<String>,
|
||||
@ -237,7 +237,7 @@ pub(crate) async fn download_png(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn signup(
|
||||
pub async fn signup(
|
||||
tera: web::Data<Tera>,
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
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<NewUser>,
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
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<String>,
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
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<String>,
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
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<Tera>,
|
||||
id: Identity,
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
@ -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<LoginUser>,
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
id: Identity,
|
||||
@ -343,12 +343,12 @@ pub(crate) async fn process_login(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn logout(id: Identity) -> Result<HttpResponse, ServerError> {
|
||||
pub async fn logout(id: Identity) -> Result<HttpResponse, ServerError> {
|
||||
id.forget();
|
||||
Ok(redirect_builder("/admin/login/"))
|
||||
}
|
||||
|
||||
pub(crate) async fn redirect(
|
||||
pub async fn redirect(
|
||||
tera: web::Data<Tera>,
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
data: web::Path<String>,
|
||||
@ -381,13 +381,13 @@ pub(crate) async fn redirect(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn redirect_empty(
|
||||
pub async fn redirect_empty(
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
) -> Result<HttpResponse, ServerError> {
|
||||
Ok(redirect_builder(&config.empty_forward_url))
|
||||
}
|
||||
|
||||
pub(crate) async fn create_link(
|
||||
pub async fn create_link(
|
||||
tera: web::Data<Tera>,
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
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<LinkForm>,
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
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<Tera>,
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
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<LinkForm>,
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
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<crate::ServerConfig>,
|
||||
link_code: web::Path<String>,
|
185
src/lib.rs
Normal file
185
src/lib.rs
Normal file
@ -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<std::env::VarError> for ServerError {
|
||||
fn from(e: std::env::VarError) -> Self {
|
||||
eprintln!("Environment error {:?}", e);
|
||||
Self::Environment
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sqlx::Error> for ServerError {
|
||||
fn from(err: sqlx::Error) -> Self {
|
||||
eprintln!("Database error {:?}", err);
|
||||
Self::Database(err)
|
||||
}
|
||||
}
|
||||
impl From<sqlx::migrate::MigrateError> for ServerError {
|
||||
fn from(err: sqlx::migrate::MigrateError) -> Self {
|
||||
eprintln!("Database error {:?}", err);
|
||||
Self::DatabaseMigration(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<argonautica::Error> for ServerError {
|
||||
fn from(e: argonautica::Error) -> Self {
|
||||
eprintln!("Authentication error {:?}", e);
|
||||
Self::Argonautic
|
||||
}
|
||||
}
|
||||
impl From<tera::Error> for ServerError {
|
||||
fn from(e: tera::Error) -> Self {
|
||||
eprintln!("Template error {:?}", e);
|
||||
Self::Template(e)
|
||||
}
|
||||
}
|
||||
impl From<QrError> for ServerError {
|
||||
fn from(e: QrError) -> Self {
|
||||
eprintln!("Template error {:?}", e);
|
||||
Self::Qr(e)
|
||||
}
|
||||
}
|
||||
impl From<std::io::Error> 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<Self, Self::Err> {
|
||||
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<Sqlite>,
|
||||
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<String> {
|
||||
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),
|
||||
]
|
||||
}
|
||||
}
|
@ -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<Self, ServerError> {
|
||||
@ -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, ServerError> {
|
||||
/// 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<Count, ServerError> {
|
||||
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,
|
||||
|
@ -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<Role, ServerError> {
|
||||
@ -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<List<FullLink>, 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<List<User>, ServerError> {
|
||||
@ -151,7 +160,11 @@ pub struct Item<T> {
|
||||
}
|
||||
|
||||
/// 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<User, ServerError> {
|
||||
@ -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<NewUser>,
|
||||
server_config: &ServerConfig,
|
||||
@ -222,7 +242,11 @@ pub(crate) async fn create_user(
|
||||
/// Take a [`actix_web::web::Form<NewUser>`] 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<Link, ServerError> {
|
||||
@ -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<LinkForm>,
|
||||
@ -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<LinkForm>,
|
||||
server_config: &ServerConfig,
|
||||
|
Loading…
Reference in New Issue
Block a user