Ready to implement photos management
This commit is contained in:
@ -141,6 +141,10 @@ pub struct AppConfig {
|
||||
/// S3 skip auto create bucket if not existing
|
||||
#[arg(long, env)]
|
||||
pub s3_skip_auto_create_bucket: bool,
|
||||
|
||||
/// Directory where temporary files are stored
|
||||
#[arg(long, env, default_value = "/tmp")]
|
||||
pub temp_dir: String,
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
|
@ -36,6 +36,13 @@ impl NumberValueConstraint {
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize)]
|
||||
pub struct StaticConstraints {
|
||||
pub date_year: NumberValueConstraint,
|
||||
pub date_month: NumberValueConstraint,
|
||||
pub date_day: NumberValueConstraint,
|
||||
|
||||
pub photo_allowed_types: &'static [&'static str],
|
||||
pub photo_max_size: usize,
|
||||
|
||||
pub mail_len: SizeConstraint,
|
||||
pub user_name_len: SizeConstraint,
|
||||
pub password_len: SizeConstraint,
|
||||
@ -53,15 +60,18 @@ pub struct StaticConstraints {
|
||||
pub member_country: SizeConstraint,
|
||||
pub member_sex: SizeConstraint,
|
||||
pub member_note: SizeConstraint,
|
||||
|
||||
pub date_year: NumberValueConstraint,
|
||||
pub date_month: NumberValueConstraint,
|
||||
pub date_day: NumberValueConstraint,
|
||||
}
|
||||
|
||||
impl Default for StaticConstraints {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
date_year: NumberValueConstraint::new(1, 2050),
|
||||
date_month: NumberValueConstraint::new(1, 12),
|
||||
date_day: NumberValueConstraint::new(1, 31),
|
||||
|
||||
photo_allowed_types: &ALLOWED_PHOTOS_MIMETYPES,
|
||||
photo_max_size: PHOTOS_MAX_SIZE,
|
||||
|
||||
mail_len: SizeConstraint::new(5, 255),
|
||||
user_name_len: SizeConstraint::new(3, 30),
|
||||
password_len: SizeConstraint::new(8, 255),
|
||||
@ -81,9 +91,6 @@ impl Default for StaticConstraints {
|
||||
member_country: SizeConstraint::new(0, 2),
|
||||
member_sex: SizeConstraint::new(0, 1),
|
||||
member_note: SizeConstraint::new(0, 35000),
|
||||
date_year: NumberValueConstraint::new(1, 2050),
|
||||
date_month: NumberValueConstraint::new(1, 12),
|
||||
date_day: NumberValueConstraint::new(1, 31),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -108,3 +115,16 @@ pub const AUTH_TOKEN_MAX_INACTIVITY: Duration = Duration::from_secs(3600);
|
||||
|
||||
/// Length of family invitation code
|
||||
pub const FAMILY_INVITATION_CODE_LEN: usize = 7;
|
||||
|
||||
/// Allowed photos mimetypes
|
||||
pub const ALLOWED_PHOTOS_MIMETYPES: [&str; 6] = [
|
||||
"image/jpeg",
|
||||
"image/png",
|
||||
"image/gif",
|
||||
"image/bmp",
|
||||
"image/webp",
|
||||
"image/vnd.microsoft.icon",
|
||||
];
|
||||
|
||||
/// Uploaded photos max size
|
||||
pub const PHOTOS_MAX_SIZE: usize = 10 * 1000 * 1000;
|
||||
|
@ -5,6 +5,8 @@ use crate::extractors::member_extractor::FamilyAndMemberInPath;
|
||||
use crate::models::{Member, MemberID, Sex};
|
||||
use crate::services::members_service;
|
||||
use crate::utils::countries_utils;
|
||||
use actix_multipart::form::tempfile::TempFile;
|
||||
use actix_multipart::form::MultipartForm;
|
||||
use actix_web::{web, HttpResponse};
|
||||
|
||||
serde_with::with_prefix!(prefix_birth "birth_");
|
||||
@ -292,3 +294,17 @@ pub async fn delete(m: FamilyAndMemberInPath) -> HttpResult {
|
||||
members_service::delete(&m).await?;
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
}
|
||||
|
||||
#[derive(Debug, MultipartForm)]
|
||||
pub struct UploadPhotoForm {
|
||||
#[multipart(rename = "photo")]
|
||||
photo: TempFile,
|
||||
}
|
||||
|
||||
/// Upload a new photo for a user
|
||||
pub async fn set_photo(
|
||||
_m: FamilyAndMemberInPath,
|
||||
MultipartForm(_form): MultipartForm<UploadPhotoForm>,
|
||||
) -> HttpResult {
|
||||
todo!()
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use actix_cors::Cors;
|
||||
use actix_multipart::form::tempfile::TempFileConfig;
|
||||
use actix_remote_ip::RemoteIPConfig;
|
||||
use actix_web::middleware::Logger;
|
||||
use actix_web::{web, App, HttpServer};
|
||||
@ -35,6 +36,8 @@ async fn main() -> std::io::Result<()> {
|
||||
.app_data(web::Data::new(RemoteIPConfig {
|
||||
proxy: AppConfig::get().proxy_ip.clone(),
|
||||
}))
|
||||
// Uploaded files
|
||||
.app_data(TempFileConfig::default().directory(&AppConfig::get().temp_dir))
|
||||
// Config controller
|
||||
.route("/", web::get().to(server_controller::home))
|
||||
.route(
|
||||
@ -150,6 +153,10 @@ async fn main() -> std::io::Result<()> {
|
||||
"/family/{id}/member/{member_id}",
|
||||
web::delete().to(members_controller::delete),
|
||||
)
|
||||
.route(
|
||||
"/family/{id}/member/{member_id}/photo",
|
||||
web::put().to(members_controller::set_photo),
|
||||
)
|
||||
})
|
||||
.bind(AppConfig::get().listen_address.as_str())?
|
||||
.run()
|
||||
|
@ -117,6 +117,10 @@ pub struct FamilyMembership {
|
||||
pub count_admins: i64,
|
||||
}
|
||||
|
||||
/// Photo ID holder
|
||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash)]
|
||||
pub struct PhotoID(pub i32);
|
||||
|
||||
/// Member ID holder
|
||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash)]
|
||||
pub struct MemberID(pub i32);
|
||||
@ -153,7 +157,7 @@ pub struct Member {
|
||||
pub first_name: Option<String>,
|
||||
pub last_name: Option<String>,
|
||||
pub birth_last_name: Option<String>,
|
||||
pub photo_id: Option<String>,
|
||||
photo_id: Option<i32>,
|
||||
pub email: Option<String>,
|
||||
pub phone: Option<String>,
|
||||
pub address: Option<String>,
|
||||
@ -183,6 +187,10 @@ impl Member {
|
||||
FamilyID(self.family_id)
|
||||
}
|
||||
|
||||
pub fn photo_id(&self) -> Option<PhotoID> {
|
||||
self.photo_id.map(PhotoID)
|
||||
}
|
||||
|
||||
pub fn sex(&self) -> Option<Sex> {
|
||||
self.sex.as_deref().map(Sex::parse_str).unwrap_or_default()
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ diesel::table! {
|
||||
couples (wife, husband) {
|
||||
wife -> Int4,
|
||||
husband -> Int4,
|
||||
photo_id -> Nullable<Varchar>,
|
||||
photo_id -> Nullable<Int4>,
|
||||
wedding_year -> Nullable<Int2>,
|
||||
wedding_month -> Nullable<Int2>,
|
||||
wedding_day -> Nullable<Int2>,
|
||||
@ -30,7 +30,7 @@ diesel::table! {
|
||||
first_name -> Nullable<Varchar>,
|
||||
last_name -> Nullable<Varchar>,
|
||||
birth_last_name -> Nullable<Varchar>,
|
||||
photo_id -> Nullable<Varchar>,
|
||||
photo_id -> Nullable<Int4>,
|
||||
email -> Nullable<Varchar>,
|
||||
phone -> Nullable<Varchar>,
|
||||
address -> Nullable<Varchar>,
|
||||
@ -61,6 +61,17 @@ diesel::table! {
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
photos (id) {
|
||||
id -> Int4,
|
||||
time_create -> Varchar,
|
||||
mime_type -> Varchar,
|
||||
sha512 -> Varchar,
|
||||
file_size -> Int4,
|
||||
thumb_sha512 -> Varchar,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
users (id) {
|
||||
id -> Int4,
|
||||
@ -78,8 +89,17 @@ diesel::table! {
|
||||
}
|
||||
}
|
||||
|
||||
diesel::joinable!(couples -> photos (photo_id));
|
||||
diesel::joinable!(members -> families (family_id));
|
||||
diesel::joinable!(members -> photos (photo_id));
|
||||
diesel::joinable!(memberships -> families (family_id));
|
||||
diesel::joinable!(memberships -> users (user_id));
|
||||
|
||||
diesel::allow_tables_to_appear_in_same_query!(couples, families, members, memberships, users,);
|
||||
diesel::allow_tables_to_appear_in_same_query!(
|
||||
couples,
|
||||
families,
|
||||
members,
|
||||
memberships,
|
||||
photos,
|
||||
users,
|
||||
);
|
||||
|
@ -60,7 +60,7 @@ pub async fn update(member: &mut Member) -> anyhow::Result<()> {
|
||||
members::dsl::first_name.eq(member.first_name.clone()),
|
||||
members::dsl::last_name.eq(member.last_name.clone()),
|
||||
members::dsl::birth_last_name.eq(member.birth_last_name.clone()),
|
||||
members::dsl::photo_id.eq(member.photo_id.clone()),
|
||||
members::dsl::photo_id.eq(member.photo_id().map(|p| p.0)),
|
||||
members::dsl::email.eq(member.email.clone()),
|
||||
members::dsl::phone.eq(member.phone.clone()),
|
||||
members::dsl::address.eq(member.address.clone()),
|
||||
|
Reference in New Issue
Block a user