Compare commits
	
		
			1 Commits
		
	
	
		
			renovate/m
			...
			e7899b780d
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e7899b780d | 
@@ -14,7 +14,7 @@ steps:
 | 
			
		||||
  - cargo test
 | 
			
		||||
 | 
			
		||||
- name: app_deploy
 | 
			
		||||
  image: node:24
 | 
			
		||||
  image: node:23
 | 
			
		||||
  environment:
 | 
			
		||||
    AWS_ACCESS_KEY_ID:
 | 
			
		||||
      from_secret: AWS_ACCESS_KEY_ID
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1370
									
								
								geneit_app/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1370
									
								
								geneit_app/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -11,46 +11,46 @@
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@emotion/react": "^11.14.0",
 | 
			
		||||
    "@emotion/styled": "^11.14.1",
 | 
			
		||||
    "@fontsource/roboto": "^5.2.8",
 | 
			
		||||
    "@fullcalendar/core": "^6.1.19",
 | 
			
		||||
    "@fullcalendar/daygrid": "^6.1.19",
 | 
			
		||||
    "@fullcalendar/interaction": "^6.1.19",
 | 
			
		||||
    "@fullcalendar/list": "^6.1.19",
 | 
			
		||||
    "@fullcalendar/react": "^6.1.19",
 | 
			
		||||
    "@emotion/styled": "^11.14.0",
 | 
			
		||||
    "@fontsource/roboto": "^5.2.5",
 | 
			
		||||
    "@fullcalendar/core": "^6.1.17",
 | 
			
		||||
    "@fullcalendar/daygrid": "^6.1.17",
 | 
			
		||||
    "@fullcalendar/interaction": "^6.1.17",
 | 
			
		||||
    "@fullcalendar/list": "^6.1.17",
 | 
			
		||||
    "@fullcalendar/react": "^6.1.17",
 | 
			
		||||
    "@mdi/js": "^7.4.47",
 | 
			
		||||
    "@mdi/react": "^1.6.1",
 | 
			
		||||
    "@mui/icons-material": "^7.2.0",
 | 
			
		||||
    "@mui/lab": "^7.0.0-beta.17",
 | 
			
		||||
    "@mui/material": "^7.2.0",
 | 
			
		||||
    "@mui/x-data-grid": "^8.16.0",
 | 
			
		||||
    "@mui/x-date-pickers": "^8.16.0",
 | 
			
		||||
    "@mui/x-tree-view": "^8.15.0",
 | 
			
		||||
    "@mui/icons-material": "^7.0.2",
 | 
			
		||||
    "@mui/lab": "^7.0.0-beta.12",
 | 
			
		||||
    "@mui/material": "^7.0.2",
 | 
			
		||||
    "@mui/x-data-grid": "^7.29.3",
 | 
			
		||||
    "@mui/x-date-pickers": "^7.29.3",
 | 
			
		||||
    "@mui/x-tree-view": "^7.29.1",
 | 
			
		||||
    "date-and-time": "^3.6.0",
 | 
			
		||||
    "dayjs": "^1.11.19",
 | 
			
		||||
    "dayjs": "^1.11.13",
 | 
			
		||||
    "email-validator": "^2.0.4",
 | 
			
		||||
    "filesize": "^11.0.13",
 | 
			
		||||
    "jspdf": "^3.0.3",
 | 
			
		||||
    "filesize": "^10.1.6",
 | 
			
		||||
    "jspdf": "^3.0.1",
 | 
			
		||||
    "mui-color-input": "^7.0.0",
 | 
			
		||||
    "react": "^19.2.0",
 | 
			
		||||
    "react-dom": "^19.2.0",
 | 
			
		||||
    "react-easy-crop": "^5.5.3",
 | 
			
		||||
    "react-qr-code": "^2.0.18",
 | 
			
		||||
    "react": "^19.0.0",
 | 
			
		||||
    "react-dom": "^19.0.0",
 | 
			
		||||
    "react-easy-crop": "^5.4.1",
 | 
			
		||||
    "react-qr-code": "^2.0.15",
 | 
			
		||||
    "react-router-dom": "^7.4.0",
 | 
			
		||||
    "react-zoom-pan-pinch": "^3.7.0",
 | 
			
		||||
    "svg2pdf.js": "^2.6.0"
 | 
			
		||||
    "svg2pdf.js": "^2.5.0"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@eslint/js": "^9.38.0",
 | 
			
		||||
    "@types/react": "^19.2.2",
 | 
			
		||||
    "@types/react-dom": "^19.2.2",
 | 
			
		||||
    "@vitejs/plugin-react": "^4.7.0",
 | 
			
		||||
    "eslint": "^9.38.0",
 | 
			
		||||
    "@eslint/js": "^9.26.0",
 | 
			
		||||
    "@types/react": "^19.0.12",
 | 
			
		||||
    "@types/react-dom": "^19.0.4",
 | 
			
		||||
    "@vitejs/plugin-react": "^4.4.1",
 | 
			
		||||
    "eslint": "^9.25.1",
 | 
			
		||||
    "eslint-plugin-react-hooks": "^5.2.0",
 | 
			
		||||
    "eslint-plugin-react-refresh": "^0.4.24",
 | 
			
		||||
    "globals": "^16.4.0",
 | 
			
		||||
    "typescript": "^5.9.3",
 | 
			
		||||
    "typescript-eslint": "^8.46.2",
 | 
			
		||||
    "vite": "^7.0.6"
 | 
			
		||||
    "eslint-plugin-react-refresh": "^0.4.20",
 | 
			
		||||
    "globals": "^16.2.0",
 | 
			
		||||
    "typescript": "^5.8.3",
 | 
			
		||||
    "typescript-eslint": "^8.24.1",
 | 
			
		||||
    "vite": "^6.2.3"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -137,10 +137,8 @@ function UsersTable(p: {
 | 
			
		||||
            <GridActionsCellItem
 | 
			
		||||
              icon={<SaveIcon />}
 | 
			
		||||
              label="Save"
 | 
			
		||||
              material={{
 | 
			
		||||
                sx: {
 | 
			
		||||
                  color: 'primary.main',
 | 
			
		||||
                },
 | 
			
		||||
              sx={{
 | 
			
		||||
                color: "primary.main",
 | 
			
		||||
              }}
 | 
			
		||||
              onClick={handleSaveClick(id)}
 | 
			
		||||
            />,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										485
									
								
								geneit_backend/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										485
									
								
								geneit_backend/Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -6,38 +6,38 @@ edition = "2024"
 | 
			
		||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
log = "0.4.28"
 | 
			
		||||
log = "0.4.27"
 | 
			
		||||
env_logger = "0.11.8"
 | 
			
		||||
clap = { version = "4.5.51", features = ["derive", "env"] }
 | 
			
		||||
clap = { version = "4.5.38", features = ["derive", "env"] }
 | 
			
		||||
lazy_static = "1.5.0"
 | 
			
		||||
lazy-regex = "3.4.2"
 | 
			
		||||
anyhow = "1.0.100"
 | 
			
		||||
actix-web = "4.11.0"
 | 
			
		||||
lazy-regex = "3.4.1"
 | 
			
		||||
anyhow = "1.0.98"
 | 
			
		||||
actix-web = "4.10.2"
 | 
			
		||||
actix-cors = "0.7.1"
 | 
			
		||||
actix-multipart = "0.7.2"
 | 
			
		||||
actix-remote-ip = "0.1.0"
 | 
			
		||||
futures-util = "0.3.31"
 | 
			
		||||
diesel = { version = "2.2.12", features = ["postgres"] }
 | 
			
		||||
futures-util = "0.3.30"
 | 
			
		||||
diesel = { version = "2.2.10", features = ["postgres"] }
 | 
			
		||||
diesel_migrations = "2.2.0"
 | 
			
		||||
serde = { version = "1.0.228", features = ["derive"] }
 | 
			
		||||
serde_json = "1.0.145"
 | 
			
		||||
mailchecker = "6.0.19"
 | 
			
		||||
redis = "0.32.7"
 | 
			
		||||
lettre = "0.11.19"
 | 
			
		||||
rand = "0.9.2"
 | 
			
		||||
bcrypt = "0.17.1"
 | 
			
		||||
serde = { version = "1.0.219", features = ["derive"] }
 | 
			
		||||
serde_json = "1.0.140"
 | 
			
		||||
mailchecker = "6.0.17"
 | 
			
		||||
redis = "0.29.5"
 | 
			
		||||
lettre = "0.11.16"
 | 
			
		||||
rand = "0.9.1"
 | 
			
		||||
bcrypt = "0.17.0"
 | 
			
		||||
light-openid = "1.0.4"
 | 
			
		||||
thiserror = "2.0.17"
 | 
			
		||||
serde_with = "3.14.0"
 | 
			
		||||
thiserror = "2.0.12"
 | 
			
		||||
serde_with = "3.12.0"
 | 
			
		||||
rust_iso3166 = "0.1.14"
 | 
			
		||||
rust-s3 = "0.35.1"
 | 
			
		||||
sha2 = "0.10.9"
 | 
			
		||||
image = "0.25.8"
 | 
			
		||||
uuid = { version = "1.17.0", features = ["v4"] }
 | 
			
		||||
image = "0.25.6"
 | 
			
		||||
uuid = { version = "1.16.0", features = ["v4"] }
 | 
			
		||||
httpdate = "1.0.3"
 | 
			
		||||
zip = "4.3.0"
 | 
			
		||||
zip = "2.5.0"
 | 
			
		||||
mime_guess = "2.0.5"
 | 
			
		||||
tempfile = "3.20.0"
 | 
			
		||||
tempfile = "3.19.1"
 | 
			
		||||
base64 = "0.22.1"
 | 
			
		||||
ical = { version = "0.11.0", features = ["generator", "ical", "vcard"] }
 | 
			
		||||
chrono = "0.4.42"
 | 
			
		||||
chrono = "0.4.41"
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ where
 | 
			
		||||
    if POSTGRES_CONNECTION.with(|i| i.borrow().is_none()) {
 | 
			
		||||
        let database_url = AppConfig::get().db_connection_chain();
 | 
			
		||||
        let conn = PgConnection::establish(&database_url)
 | 
			
		||||
            .unwrap_or_else(|_| panic!("Error connecting to {database_url}"));
 | 
			
		||||
            .unwrap_or_else(|_| panic!("Error connecting to {}", database_url));
 | 
			
		||||
 | 
			
		||||
        POSTGRES_CONNECTION.with(|i| *i.borrow_mut() = Some(conn))
 | 
			
		||||
    }
 | 
			
		||||
@@ -38,7 +38,7 @@ where
 | 
			
		||||
                POSTGRES_CONNECTION.with(|i| *i.borrow_mut() = None)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            log::error!("Database query error! {e:?}");
 | 
			
		||||
            log::error!("Database query error! {:?}", e);
 | 
			
		||||
            Err(e.into())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ pub async fn create_bucket_if_required() -> anyhow::Result<()> {
 | 
			
		||||
            log::warn!("The bucket does not seem to exists, trying to create it!")
 | 
			
		||||
        }
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            log::error!("Got unexpected error when querying bucket info: {e}");
 | 
			
		||||
            log::error!("Got unexpected error when querying bucket info: {}", e);
 | 
			
		||||
            return Err(BucketServiceError::FailedFetchBucketInfo.into());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -34,16 +34,18 @@ impl AccommodationRequest {
 | 
			
		||||
        }
 | 
			
		||||
        accommodation.name = self.name;
 | 
			
		||||
 | 
			
		||||
        if let Some(d) = &self.description
 | 
			
		||||
            && !c.accommodation_description_len.validate(d) {
 | 
			
		||||
        if let Some(d) = &self.description {
 | 
			
		||||
            if !c.accommodation_description_len.validate(d) {
 | 
			
		||||
                return Err(AccommodationListControllerErr::InvalidDescriptionLength.into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        accommodation.description.clone_from(&self.description);
 | 
			
		||||
 | 
			
		||||
        if let Some(c) = &self.color
 | 
			
		||||
            && !lazy_regex::regex!("[a-fA-F0-9]{6}").is_match(c) {
 | 
			
		||||
        if let Some(c) = &self.color {
 | 
			
		||||
            if !lazy_regex::regex!("[a-fA-F0-9]{6}").is_match(c) {
 | 
			
		||||
                return Err(AccommodationListControllerErr::MalformedColor.into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        accommodation.color.clone_from(&self.color);
 | 
			
		||||
 | 
			
		||||
        accommodation.need_validation = self.need_validation;
 | 
			
		||||
 
 | 
			
		||||
@@ -79,7 +79,10 @@ pub async fn request_reset_password(
 | 
			
		||||
    match users_service::get_by_mail(&req.mail).await {
 | 
			
		||||
        Ok(mut user) => users_service::request_reset_password(&mut user).await?,
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            log::error!("Could not locate user account {e}! (error silently ignored)");
 | 
			
		||||
            log::error!(
 | 
			
		||||
                "Could not locate user account {}! (error silently ignored)",
 | 
			
		||||
                e
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -119,7 +122,7 @@ pub async fn check_reset_password_token(
 | 
			
		||||
                RatedAction::CheckResetPasswordTokenFailed,
 | 
			
		||||
            )
 | 
			
		||||
            .await?;
 | 
			
		||||
            log::error!("Password reset token could not be used: {e}");
 | 
			
		||||
            log::error!("Password reset token could not be used: {}", e);
 | 
			
		||||
            return Ok(HttpResponse::NotFound().finish());
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
@@ -153,7 +156,7 @@ pub async fn reset_password(remote_ip: RemoteIP, req: web::Json<ResetPasswordBod
 | 
			
		||||
                RatedAction::CheckResetPasswordTokenFailed,
 | 
			
		||||
            )
 | 
			
		||||
            .await?;
 | 
			
		||||
            log::error!("Password reset token could not be used: {e}");
 | 
			
		||||
            log::error!("Password reset token could not be used: {}", e);
 | 
			
		||||
            return Ok(HttpResponse::NotFound().finish());
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
@@ -193,7 +196,7 @@ pub async fn password_login(remote_ip: RemoteIP, req: web::Json<PasswordLoginQue
 | 
			
		||||
    let user = match users_service::get_by_mail(&req.mail).await {
 | 
			
		||||
        Ok(u) => u,
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            log::error!("Auth failed: could not find account by mail! {e}");
 | 
			
		||||
            log::error!("Auth failed: could not find account by mail! {}", e);
 | 
			
		||||
            rate_limiter_service::record_action(remote_ip.0, RatedAction::FailedPasswordLogin)
 | 
			
		||||
                .await?;
 | 
			
		||||
            return Ok(HttpResponse::Unauthorized().json("Invalid credentials"));
 | 
			
		||||
 
 | 
			
		||||
@@ -48,20 +48,23 @@ impl CoupleRequest {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(husband) = self.husband
 | 
			
		||||
            && !members_service::exists(couple.family_id(), husband).await? {
 | 
			
		||||
        if let Some(husband) = self.husband {
 | 
			
		||||
            if !members_service::exists(couple.family_id(), husband).await? {
 | 
			
		||||
                return Err(CoupleControllerErr::HusbandNotExisting.into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(d) = &self.wedding
 | 
			
		||||
            && !d.check() {
 | 
			
		||||
        if let Some(d) = &self.wedding {
 | 
			
		||||
            if !d.check() {
 | 
			
		||||
                return Err(CoupleControllerErr::MalformedDateOfWedding.into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(d) = &self.divorce
 | 
			
		||||
            && !d.check() {
 | 
			
		||||
        if let Some(d) = &self.divorce {
 | 
			
		||||
            if !d.check() {
 | 
			
		||||
                return Err(CoupleControllerErr::MalformedDateOfDivorce.into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        couple.set_wife(self.wife);
 | 
			
		||||
        couple.set_husband(self.husband);
 | 
			
		||||
 
 | 
			
		||||
@@ -183,9 +183,9 @@ pub async fn import_family(
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Err(e) = req_member_data.to_member(member).await {
 | 
			
		||||
            log::error!("Error while processing import (member {req_id:?}) - {e}");
 | 
			
		||||
            log::error!("Error while processing import (member {:?}) - {e}", req_id);
 | 
			
		||||
            return Ok(
 | 
			
		||||
                HttpResponse::BadRequest().json(format!("Failed to validate member {req_id:?}!"))
 | 
			
		||||
                HttpResponse::BadRequest().json(format!("Failed to validate member {:?}!", req_id))
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -95,10 +95,11 @@ fn check_opt_str_val(
 | 
			
		||||
    c: SizeConstraint,
 | 
			
		||||
    err: MemberControllerErr,
 | 
			
		||||
) -> anyhow::Result<()> {
 | 
			
		||||
    if let Some(v) = val
 | 
			
		||||
        && !c.validate(v) {
 | 
			
		||||
    if let Some(v) = val {
 | 
			
		||||
        if !c.validate(v) {
 | 
			
		||||
            return Err(err.into());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -150,10 +151,11 @@ impl MemberRequest {
 | 
			
		||||
            MemberControllerErr::MalformedEmailAddress,
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        if let Some(mail) = &self.email
 | 
			
		||||
            && !mailchecker::is_valid(mail) {
 | 
			
		||||
        if let Some(mail) = &self.email {
 | 
			
		||||
            if !mailchecker::is_valid(mail) {
 | 
			
		||||
                return Err(MemberControllerErr::InvalidEmailAddress.into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        check_opt_str_val(
 | 
			
		||||
            &self.phone,
 | 
			
		||||
@@ -185,20 +187,23 @@ impl MemberRequest {
 | 
			
		||||
            MemberControllerErr::MalformedCountry,
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        if let Some(c) = &self.country
 | 
			
		||||
            && !countries_utils::is_code_valid(c) {
 | 
			
		||||
        if let Some(c) = &self.country {
 | 
			
		||||
            if !countries_utils::is_code_valid(c) {
 | 
			
		||||
                return Err(MemberControllerErr::InvalidCountryCode.into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(d) = &self.birth
 | 
			
		||||
            && !d.check() {
 | 
			
		||||
        if let Some(d) = &self.birth {
 | 
			
		||||
            if !d.check() {
 | 
			
		||||
                return Err(MemberControllerErr::MalformedDateOfBirth.into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(d) = &self.death
 | 
			
		||||
            && !d.check() {
 | 
			
		||||
        if let Some(d) = &self.death {
 | 
			
		||||
            if !d.check() {
 | 
			
		||||
                return Err(MemberControllerErr::MalformedDateOfDeath.into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        check_opt_str_val(
 | 
			
		||||
            &self.note,
 | 
			
		||||
@@ -216,10 +221,11 @@ impl MemberRequest {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(father) = self.father
 | 
			
		||||
            && !members_service::exists(member.family_id(), father).await? {
 | 
			
		||||
        if let Some(father) = self.father {
 | 
			
		||||
            if !members_service::exists(member.family_id(), father).await? {
 | 
			
		||||
                return Err(MemberControllerErr::FatherNotExisting.into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        member.first_name = self.first_name;
 | 
			
		||||
        member.last_name = self.last_name;
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,7 @@ impl Display for HttpErr {
 | 
			
		||||
 | 
			
		||||
impl actix_web::error::ResponseError for HttpErr {
 | 
			
		||||
    fn error_response(&self) -> HttpResponse<BoxBody> {
 | 
			
		||||
        log::error!("Error while processing request! {self}");
 | 
			
		||||
        log::error!("Error while processing request! {}", self);
 | 
			
		||||
        HttpResponse::InternalServerError().body("Failed to execute request!")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,15 +36,16 @@ async fn get_photo(id: &PhotoIdPath, full_size: bool, req: HttpRequest) -> HttpR
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Check if an upload is un-necessary
 | 
			
		||||
    if let Some(c) = req.headers().get(header::IF_NONE_MATCH)
 | 
			
		||||
        && c.to_str().unwrap_or("") == hash {
 | 
			
		||||
    if let Some(c) = req.headers().get(header::IF_NONE_MATCH) {
 | 
			
		||||
        if c.to_str().unwrap_or("") == hash {
 | 
			
		||||
            return Ok(HttpResponse::NotModified().finish());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if let Some(c) = req.headers().get(header::IF_MODIFIED_SINCE) {
 | 
			
		||||
        let date_str = c.to_str().unwrap_or("");
 | 
			
		||||
        if let Ok(date) = httpdate::parse_http_date(date_str)
 | 
			
		||||
            && date
 | 
			
		||||
        if let Ok(date) = httpdate::parse_http_date(date_str) {
 | 
			
		||||
            if date
 | 
			
		||||
                .add(Duration::from_secs(1))
 | 
			
		||||
                .duration_since(UNIX_EPOCH)
 | 
			
		||||
                .unwrap()
 | 
			
		||||
@@ -53,6 +54,7 @@ async fn get_photo(id: &PhotoIdPath, full_size: bool, req: HttpRequest) -> HttpR
 | 
			
		||||
            {
 | 
			
		||||
                return Ok(HttpResponse::NotModified().finish());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let bytes = s3_connection::get_file(&match full_size {
 | 
			
		||||
 
 | 
			
		||||
@@ -75,7 +75,7 @@ impl FromRequest for FamilyAndAccommodationInPath {
 | 
			
		||||
            Self::load_accommodation_from_path(family, accommodation_id)
 | 
			
		||||
                .await
 | 
			
		||||
                .map_err(|e| {
 | 
			
		||||
                    log::error!("Failed to extract accommodation ID from URL! {e}");
 | 
			
		||||
                    log::error!("Failed to extract accommodation ID from URL! {}", e);
 | 
			
		||||
                    actix_web::error::ErrorNotFound("Could not fetch accommodation information!")
 | 
			
		||||
                })
 | 
			
		||||
        })
 | 
			
		||||
 
 | 
			
		||||
@@ -85,7 +85,7 @@ impl FromRequest for FamilyAndAccommodationReservationCalendarInPath {
 | 
			
		||||
            Self::load_calendar_from_path(family, accommodation_id)
 | 
			
		||||
                .await
 | 
			
		||||
                .map_err(|e| {
 | 
			
		||||
                    log::error!("Failed to extract calendar ID from URL! {e}");
 | 
			
		||||
                    log::error!("Failed to extract calendar ID from URL! {}", e);
 | 
			
		||||
                    actix_web::error::ErrorNotFound("Could not fetch calendar information!")
 | 
			
		||||
                })
 | 
			
		||||
        })
 | 
			
		||||
 
 | 
			
		||||
@@ -95,7 +95,7 @@ impl FromRequest for FamilyAndAccommodationReservationInPath {
 | 
			
		||||
            Self::load_accommodation_reservation_from_path(family, reservation_id)
 | 
			
		||||
                .await
 | 
			
		||||
                .map_err(|e| {
 | 
			
		||||
                    log::error!("Failed to extract accommodation ID from URL! {e}");
 | 
			
		||||
                    log::error!("Failed to extract accommodation ID from URL! {}", e);
 | 
			
		||||
                    actix_web::error::ErrorNotFound("Could not fetch accommodation information!")
 | 
			
		||||
                })
 | 
			
		||||
        })
 | 
			
		||||
 
 | 
			
		||||
@@ -71,7 +71,7 @@ impl FromRequest for FamilyAndCoupleInPath {
 | 
			
		||||
            FamilyAndCoupleInPath::load_couple_from_path(family, couple_id)
 | 
			
		||||
                .await
 | 
			
		||||
                .map_err(|e| {
 | 
			
		||||
                    log::error!("Failed to extract couple ID from URL! {e}");
 | 
			
		||||
                    log::error!("Failed to extract couple ID from URL! {}", e);
 | 
			
		||||
                    actix_web::error::ErrorNotFound("Could not fetch couple information!")
 | 
			
		||||
                })
 | 
			
		||||
        })
 | 
			
		||||
 
 | 
			
		||||
@@ -62,7 +62,7 @@ impl FromRequest for FamilyInPath {
 | 
			
		||||
            FamilyInPath::load_family_from_path(&token, family_id)
 | 
			
		||||
                .await
 | 
			
		||||
                .map_err(|e| {
 | 
			
		||||
                    log::error!("Failed to extract family ID from URL! {e}");
 | 
			
		||||
                    log::error!("Failed to extract family ID from URL! {}", e);
 | 
			
		||||
                    actix_web::error::ErrorNotFound("Could not fetch family information!")
 | 
			
		||||
                })
 | 
			
		||||
        })
 | 
			
		||||
 
 | 
			
		||||
@@ -71,7 +71,7 @@ impl FromRequest for FamilyAndMemberInPath {
 | 
			
		||||
            FamilyAndMemberInPath::load_member_from_path(family, member_id)
 | 
			
		||||
                .await
 | 
			
		||||
                .map_err(|e| {
 | 
			
		||||
                    log::error!("Failed to extract member ID from URL! {e}");
 | 
			
		||||
                    log::error!("Failed to extract member ID from URL! {}", e);
 | 
			
		||||
                    actix_web::error::ErrorNotFound("Could not fetch member information!")
 | 
			
		||||
                })
 | 
			
		||||
        })
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ impl User {
 | 
			
		||||
            .as_deref()
 | 
			
		||||
            .map(|hash| {
 | 
			
		||||
                bcrypt::verify(password, hash).unwrap_or_else(|e| {
 | 
			
		||||
                    log::error!("Failed to validate password! {e}");
 | 
			
		||||
                    log::error!("Failed to validate password! {}", e);
 | 
			
		||||
                    false
 | 
			
		||||
                })
 | 
			
		||||
            })
 | 
			
		||||
 
 | 
			
		||||
@@ -125,13 +125,13 @@ async fn load_token_info(token: &LoginTokenValue) -> anyhow::Result<Option<Login
 | 
			
		||||
    let token = match user_tokens.iter_mut().find(|t| t.key == key) {
 | 
			
		||||
        Some(t) => t,
 | 
			
		||||
        None => {
 | 
			
		||||
            log::error!("Could not find token for key '{key}' (missing token)");
 | 
			
		||||
            log::error!("Could not find token for key '{}' (missing token)", key);
 | 
			
		||||
            return Ok(None);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if token.is_expired() {
 | 
			
		||||
        log::error!("Could not find token for key '{key}' (token expired)");
 | 
			
		||||
        log::error!("Could not find token for key '{}' (token expired)", key);
 | 
			
		||||
        return Ok(None);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -169,7 +169,7 @@ impl FromRequest for LoginToken {
 | 
			
		||||
 | 
			
		||||
            let token = match load_token_info(&token).await {
 | 
			
		||||
                Err(e) => {
 | 
			
		||||
                    log::error!("Failed to load auth token! {e}");
 | 
			
		||||
                    log::error!("Failed to load auth token! {}", e);
 | 
			
		||||
                    return Err(actix_web::error::ErrorPreconditionFailed(
 | 
			
		||||
                        "Failed to check auth token!",
 | 
			
		||||
                    ));
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ pub async fn send_mail<D: Display>(to: &str, subject: &str, body: D) -> anyhow::
 | 
			
		||||
    let mailer = mailer.build();
 | 
			
		||||
 | 
			
		||||
    mailer.send(&email)?;
 | 
			
		||||
    log::debug!("A mail was sent to {to} (subject = {subject})");
 | 
			
		||||
    log::debug!("A mail was sent to {} (subject = {})", to, subject);
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -149,10 +149,11 @@ pub mod loop_detection {
 | 
			
		||||
 | 
			
		||||
    impl LoopStack<'_> {
 | 
			
		||||
        pub fn contains(&self, id: MemberID) -> bool {
 | 
			
		||||
            if let Some(ls) = &self.prev
 | 
			
		||||
                && ls.contains(id) {
 | 
			
		||||
            if let Some(ls) = &self.prev {
 | 
			
		||||
                if ls.contains(id) {
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            self.curr == id
 | 
			
		||||
        }
 | 
			
		||||
@@ -167,7 +168,7 @@ pub mod loop_detection {
 | 
			
		||||
            None => false,
 | 
			
		||||
            Some(id) => {
 | 
			
		||||
                if curr_stack.contains(id) {
 | 
			
		||||
                    log::debug!("Loop detected! {curr_stack:?}");
 | 
			
		||||
                    log::debug!("Loop detected! {:?}", curr_stack);
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,7 @@ fn redis_key(state: &str) -> String {
 | 
			
		||||
    format!("oidc-state-{state}")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn load_provider_info(prov_id: &str) -> anyhow::Result<OpenIDClient<'_>> {
 | 
			
		||||
async fn load_provider_info(prov_id: &str) -> anyhow::Result<OpenIDClient> {
 | 
			
		||||
    let prov = AppConfig::get()
 | 
			
		||||
        .openid_providers()
 | 
			
		||||
        .into_iter()
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ use crate::utils::string_utils::rand_str;
 | 
			
		||||
use crate::utils::time_utils::time;
 | 
			
		||||
use bcrypt::DEFAULT_COST;
 | 
			
		||||
use diesel::prelude::*;
 | 
			
		||||
use std::io::ErrorKind;
 | 
			
		||||
 | 
			
		||||
/// Get the information of a user, by its id
 | 
			
		||||
pub async fn get_by_id(id: UserID) -> anyhow::Result<User> {
 | 
			
		||||
@@ -24,7 +25,8 @@ pub async fn get_by_mail(mail: &str) -> anyhow::Result<User> {
 | 
			
		||||
/// Get the information of a user, by its password reset token
 | 
			
		||||
pub async fn get_by_pwd_reset_token(token: &str) -> anyhow::Result<User> {
 | 
			
		||||
    if token.is_empty() {
 | 
			
		||||
        return Err(anyhow::Error::from(std::io::Error::other(
 | 
			
		||||
        return Err(anyhow::Error::from(std::io::Error::new(
 | 
			
		||||
            ErrorKind::Other,
 | 
			
		||||
            "Token is empty!",
 | 
			
		||||
        )));
 | 
			
		||||
    }
 | 
			
		||||
@@ -44,7 +46,8 @@ pub async fn get_by_pwd_reset_token(token: &str) -> anyhow::Result<User> {
 | 
			
		||||
/// Get the information of a user, by its account deletion token
 | 
			
		||||
pub async fn get_by_account_delete_token(token: &str) -> anyhow::Result<User> {
 | 
			
		||||
    if token.is_empty() {
 | 
			
		||||
        return Err(anyhow::Error::from(std::io::Error::other(
 | 
			
		||||
        return Err(anyhow::Error::from(std::io::Error::new(
 | 
			
		||||
            ErrorKind::Other,
 | 
			
		||||
            "Token is empty!",
 | 
			
		||||
        )));
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ pub fn sha256(bytes: &[u8]) -> String {
 | 
			
		||||
    let mut hasher = Sha256::new();
 | 
			
		||||
    hasher.update(bytes);
 | 
			
		||||
    let h = hasher.finalize();
 | 
			
		||||
    format!("{h:x}")
 | 
			
		||||
    format!("{:x}", h)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute hash of a slice of bytes (sha512)
 | 
			
		||||
@@ -13,5 +13,5 @@ pub fn sha512(bytes: &[u8]) -> String {
 | 
			
		||||
    let mut hasher = Sha512::new();
 | 
			
		||||
    hasher.update(bytes);
 | 
			
		||||
    let h = hasher.finalize();
 | 
			
		||||
    format!("{h:x}")
 | 
			
		||||
    format!("{:x}", h)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user