|
1 |
| -use std::sync::Arc; |
2 |
| -use diesel::{Connection, PgConnection}; |
3 |
| - |
| 1 | +use crate::models::ApiToken; |
| 2 | +use crate::{email::Email, models::User, worker::Environment, Emails}; |
| 3 | +use anyhow::anyhow; |
4 | 4 | use crates_io_worker::BackgroundJob;
|
5 |
| -use crate::Emails; |
6 |
| -use crate::worker::Environment; |
| 5 | +use diesel::{ |
| 6 | + dsl::now, Connection, ExpressionMethods, NullableExpressionMethods, PgConnection, RunQueryDsl, |
| 7 | +}; |
| 8 | +use std::sync::Arc; |
7 | 9 |
|
8 | 10 | /// The threshold in days for the expiry notification.
|
9 | 11 | const EXPIRY_THRESHOLD: i64 = 3;
|
@@ -34,18 +36,47 @@ impl BackgroundJob for CheckAboutToExpireToken {
|
34 | 36 | // Check if the token is about to expire and send a notification if it is.
|
35 | 37 | fn check(emails: &Emails, conn: &mut PgConnection) -> anyhow::Result<()> {
|
36 | 38 | info!("Checking if tokens are about to expire");
|
37 |
| - let expired_tokens = |
38 |
| - crate::models::token::ApiToken::find_tokens_expiring_within_days(conn, EXPIRY_THRESHOLD)?; |
| 39 | + let expired_tokens = ApiToken::find_tokens_expiring_within_days(conn, EXPIRY_THRESHOLD)?; |
39 | 40 | // Batch send notifications in transactions.
|
40 | 41 | const BATCH_SIZE: usize = 100;
|
41 | 42 | for chunk in expired_tokens.chunks(BATCH_SIZE) {
|
42 | 43 | conn.transaction(|conn| {
|
43 | 44 | for token in chunk {
|
44 | 45 | // Send notification.
|
| 46 | + let user = User::find(conn, token.user_id)?; |
| 47 | + let Some(recipient) = user.email(conn)? else { |
| 48 | + return Err(anyhow!("No address found")); |
| 49 | + }; |
| 50 | + let email = ExpiryNotificationEmail { |
| 51 | + token_name: token.name.clone(), |
| 52 | + expiry_date: token.expired_at.unwrap().date().to_string(), |
| 53 | + }; |
| 54 | + emails.send(&recipient, email)?; |
| 55 | + // Also update the token to prevent duplicate notifications. |
| 56 | + diesel::update(token) |
| 57 | + .set(crate::schema::api_tokens::expiry_notification_at.eq(now.nullable())) |
| 58 | + .execute(conn)?; |
45 | 59 | }
|
46 | 60 | Ok::<_, anyhow::Error>(())
|
47 | 61 | })?;
|
48 | 62 | }
|
49 | 63 |
|
50 | 64 | Ok(())
|
51 | 65 | }
|
| 66 | + |
| 67 | +#[derive(Debug, Clone)] |
| 68 | +struct ExpiryNotificationEmail { |
| 69 | + token_name: String, |
| 70 | + expiry_date: String, |
| 71 | +} |
| 72 | + |
| 73 | +impl Email for ExpiryNotificationEmail { |
| 74 | + const SUBJECT: &'static str = "Token Expiry Notification"; |
| 75 | + |
| 76 | + fn body(&self) -> String { |
| 77 | + format!( |
| 78 | + "The token {} is about to expire on {}. Please take action.", |
| 79 | + self.token_name, self.expiry_date |
| 80 | + ) |
| 81 | + } |
| 82 | +} |
0 commit comments