For now? I'm experimenting on an old concept.
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: Update error handling

+139 -76
+11 -10
server/src/client_communication.rs
··· 28 28 */ 29 29 30 30 extern crate rocket; 31 + use crate::errors::LuminaDbError; 31 32 use crate::rate_limiter::RateLimit; 32 33 use crate::timeline::fetch_timeline_post_ids_by_timeline_name; 33 34 use crate::user::User; ··· 107 108 } 108 109 Err(e) => { 109 110 match e { 110 - LuminaError::Postgres(postgres_error) => { 111 - // Check if it's a "no rows returned" type error 112 - if postgres_error.to_string().contains("no rows") || postgres_error.to_string().contains("RowCount") { 113 - info_elog!( ev_log,"Session revival failed: token not found or expired."); 114 - } else { 115 - info_elog!(ev_log,"Session revival failed: database error: {:?}", postgres_error); 111 + LuminaError::DbError(LuminaDbError::Postgres(postgres_error)) => { 112 + // Check if it's a "no rows returned" type error 113 + if postgres_error.to_string().contains("no rows") || postgres_error.to_string().contains("RowCount") { 114 + info_elog!( ev_log,"Session revival failed: token not found or expired."); 115 + } else { 116 + info_elog!(ev_log,"Session revival failed: database error: {:?}", postgres_error); 117 + } 116 118 } 117 - } 118 - 119 + 119 120 _ => { 120 121 info_elog!(ev_log,"Session revival failed: {:?}", e); 121 122 } ··· 177 178 } 178 179 Err(e) => { 179 180 match e { 180 - LuminaError::Postgres(e) => 181 + LuminaError::DbError(crate::errors::LuminaDbError::Postgres(e)) => 181 182 error_elog!(ev_log,"While creating session token: {:?}", e), 182 - LuminaError::Bb8Pool(e) => 183 + LuminaError::Bb8RunErrorPg(e) => 183 184 warn_elog!(ev_log,"There was an error creating session token: {}", e), 184 185 _ => {} 185 186 }
+15 -21
server/src/database.rs
··· 29 29 use bb8_redis::RedisConnectionManager; 30 30 use cynthia_con::{CynthiaColors, CynthiaStyles}; 31 31 use std::time::Duration; 32 - use tokio_postgres as postgres; 32 + use crate::postgres; 33 33 use tokio_postgres::NoTls; 34 34 35 35 pub(crate) async fn setup() -> Result<PgConn, LuminaError> { ··· 38 38 std::env::var("LUMINA_REDIS_URL").unwrap_or_else(|_| "redis://127.0.0.1/".into()); 39 39 let redis_pool = { 40 40 info_elog!(ev_log, "Setting up Redis connection to {}...", redis_url); 41 - let manager = RedisConnectionManager::new(redis_url.clone()).map_err(LuminaError::Redis)?; 41 + let manager = RedisConnectionManager::new(redis_url.clone())?; 42 42 // Configure pool sizes 43 43 let redis_pool = Pool::builder() 44 44 .max_size(50) ··· 127 127 let pg_manager = PostgresConnectionManager::new(pg_config.clone(), NoTls); 128 128 let pg_pool = Pool::builder() 129 129 .build(pg_manager) 130 - .await 131 - .map_err(|e| LuminaError::Bb8Pool(e.to_string()))?; 130 + .await?; 132 131 { 133 132 let pg_conn = pg_pool 134 133 .get() 135 - .await 136 - .map_err(|e| LuminaError::Bb8Pool(e.to_string()))?; 134 + .await?; 137 135 pg_conn 138 136 .batch_execute(include_str!("../../SQL/create_pg.sql")) 139 - .await 140 - .map_err(LuminaError::Postgres)?; 141 - 137 + .await?; 142 138 // Populate bloom filters 143 139 let mut redis_conn = redis_pool 144 140 .get() 145 - .await 146 - .map_err(|e| LuminaError::Bb8Pool(e.to_string()))?; 141 + .await?; 147 142 let email_key = "bloom:email"; 148 143 let username_key = "bloom:username"; 149 144 150 145 let rows = pg_conn 151 146 .query("SELECT email, username FROM users", &[]) 152 - .await 153 - .map_err(LuminaError::Postgres)?; 147 + .await?; 154 148 for row in rows { 155 149 let email: String = row.get(0); 156 150 let username: String = row.get(1); ··· 159 153 .arg(email) 160 154 .query_async(&mut *redis_conn) 161 155 .await 162 - .map_err(LuminaError::Redis)?; 156 + ?; 163 157 let _: () = redis::cmd("BF.ADD") 164 158 .arg(username_key) 165 159 .arg(username) 166 160 .query_async(&mut *redis_conn) 167 161 .await 168 - .map_err(LuminaError::Redis)?; 162 + ?; 169 163 } 170 164 info_elog!(ev_log, "Bloom filters populated from PostgreSQL.",); 171 165 }; ··· 325 319 .arg(pattern) 326 320 .query_async(&mut **redis_conn) 327 321 .await 328 - .map_err(LuminaError::Redis)?; 322 + ?; 329 323 330 324 cursor = result.0; 331 325 let keys = result.1; ··· 338 332 .arg(&key) 339 333 .query_async(&mut **redis_conn) 340 334 .await 341 - .map_err(LuminaError::Redis)?; 335 + ?; 342 336 if ttl == -1 || ttl == 0 { 343 337 expired_keys.push(key); 344 338 } ··· 349 343 .arg(&expired_keys) 350 344 .query_async(&mut **redis_conn) 351 345 .await 352 - .map_err(LuminaError::Redis)?; 346 + ?; 353 347 } 354 348 355 349 if cursor == 0 { ··· 390 384 ) 391 385 .query_async(&mut **redis_conn) 392 386 .await 393 - .map_err(LuminaError::Redis)?; 387 + ?; 394 388 return Ok(()); 395 389 }; 396 390 ··· 411 405 ) 412 406 .query_async(&mut **redis_conn) 413 407 .await 414 - .map_err(LuminaError::Redis)?; 408 + ?; 415 409 } 416 410 Err(_) => { 417 411 // If query fails, just update timestamp to avoid repeated failures ··· 424 418 ) 425 419 .query_async(&mut **redis_conn) 426 420 .await 427 - .map_err(LuminaError::Redis)?; 421 + ?; 428 422 } 429 423 } 430 424
+54 -5
server/src/errors.rs
··· 23 23 #[derive(Debug)] 24 24 pub(crate) enum LuminaError { 25 25 ConfInvalid(String), 26 - Bb8Pool(String), 27 - Postgres(crate::postgres::Error), 26 + DbError(LuminaDbError), 27 + Bb8RunErrorPg(bb8::RunError<crate::postgres::Error>), 28 + Bb8RunErrorRedis(bb8::RunError<redis::RedisError>), 28 29 Unknown, 29 30 RocketFaillure(Box<rocket::Error>), 30 31 BcryptError, ··· 36 37 AuthenticationWrongPassword, 37 38 UUidError, 38 39 RegexError, 39 - Redis(redis::RedisError), 40 40 SerializationError(String), 41 41 JoinFaillure, 42 42 } 43 + 44 + impl From<LuminaDbError> for LuminaError { 45 + fn from(v: LuminaDbError) -> Self { 46 + Self::DbError(v) 47 + } 48 + } 49 + 50 + #[derive(Debug)] 51 + pub(crate) enum LuminaDbError { 52 + Redis(redis::RedisError), 53 + Postgres(crate::postgres::Error), 54 + } 55 + 43 56 impl From<rocket::Error> for LuminaError { 44 57 fn from(err: rocket::Error) -> Self { 45 58 LuminaError::RocketFaillure(Box::new(err)) ··· 48 61 49 62 impl From<crate::postgres::Error> for LuminaError { 50 63 fn from(err: crate::postgres::Error) -> Self { 51 - LuminaError::Postgres(err) 64 + LuminaError::DbError(LuminaDbError::Postgres(err)) 52 65 } 53 66 } 54 67 55 68 impl From<redis::RedisError> for LuminaError { 56 69 fn from(err: redis::RedisError) -> Self { 57 - LuminaError::Redis(err) 70 + LuminaError::DbError(LuminaDbError::Redis(err)) 71 + } 72 + } 73 + impl From<bb8::RunError<crate::postgres::Error>> for LuminaError { 74 + fn from(err: bb8::RunError<crate::postgres::Error>) -> Self { 75 + LuminaError::Bb8RunErrorPg(err) 76 + } 77 + } 78 + impl From<bb8::RunError<redis::RedisError>> for LuminaError { 79 + fn from(err: bb8::RunError<redis::RedisError>) -> Self { 80 + LuminaError::Bb8RunErrorRedis(err) 58 81 } 59 82 } 83 + impl ToString for LuminaError { 84 + fn to_string(&self) -> String { 85 + match self { 86 + LuminaError::ConfInvalid(s) => format!("Configuration invalid: {}", s), 87 + LuminaError::DbError(e) => match e { 88 + LuminaDbError::Redis(re) => format!("Redis error: {}", re), 89 + LuminaDbError::Postgres(pe) => format!("Postgres error: {}", pe), 90 + }, 91 + LuminaError::Bb8RunErrorPg(e) => format!("Postgres connection pool error: {}", e), 92 + LuminaError::Bb8RunErrorRedis(e) => format!("Redis connection pool error: {}", e), 93 + LuminaError::RocketFaillure(e) => format!("Rocket error: {}", e), 94 + LuminaError::BcryptError => "Bcrypt error".to_string(), 95 + LuminaError::RegisterEmailInUse => "Email already in use".to_string(), 96 + LuminaError::RegisterUsernameInUse => "Username already in use".to_string(), 97 + LuminaError::RegisterEmailNotValid => "Email not valid".to_string(), 98 + LuminaError::RegisterUsernameInvalid(s) => format!("Username invalid: {}", s), 99 + LuminaError::RegisterPasswordNotValid(s) => format!("Password not valid: {}", s), 100 + LuminaError::AuthenticationWrongPassword => "Wrong password".to_string(), 101 + LuminaError::UUidError => "UUID error".to_string(), 102 + LuminaError::RegexError => "Regex error".to_string(), 103 + LuminaError::SerializationError(s) => format!("Serialization error: {}", s), 104 + LuminaError::JoinFaillure => "Process join failure".to_string(), 105 + LuminaError::Unknown => "Unknown error".to_string(), 106 + } 107 + } 108 + }
+7 -3
server/src/main.rs
··· 128 128 None 129 129 } 130 130 131 - Err(LuminaError::Postgres(a)) => { 131 + Err(LuminaError::DbError(crate::errors::LuminaDbError::Postgres(a))) => { 132 132 error_elog!(ev_log, "While connecting to postgres database: {}", a); 133 133 None 134 134 } 135 - Err(LuminaError::Bb8Pool(a)) => { 135 + Err(LuminaError::Bb8RunErrorPg(a)) => { 136 136 error_elog!(ev_log, "While setting up database pool: {}", a); 137 137 None 138 138 } 139 - Err(LuminaError::Redis(a)) => { 139 + Err(LuminaError::DbError(crate::errors::LuminaDbError::Redis(a))) => { 140 + error_elog!(ev_log, "While connecting to Redis: {}", a); 141 + None 142 + } 143 + Err(LuminaError::Bb8RunErrorRedis(a)) => { 140 144 error_elog!(ev_log, "While connecting to Redis: {}", a); 141 145 None 142 146 }
+15
server/src/tests.rs
··· 16 16 * along with this program. If not, see <https://www.gnu.org/licenses/>. 17 17 */ 18 18 19 + use std::mem; 19 20 use crate::database::{self, DatabaseConnections}; 20 21 use crate::timeline; 22 + use crate::errors::LuminaError; 21 23 22 24 #[tokio::test] 23 25 async fn test_database_setup() { ··· 89 91 90 92 assert!(result.is_none(), "Cache should be invalidated"); 91 93 } 94 + 95 + 96 + #[test] 97 + fn print_sizes() { 98 + println!("Size of LuminaError: {} bytes", mem::size_of::<LuminaError>()); 99 + println!("Size of Rocket Error: {} bytes", mem::size_of::<rocket::Error>()); 100 + println!("Size of Postgres Error: {} bytes", mem::size_of::<crate::postgres::Error>()); 101 + println!("Size of Redis Error: {} bytes", mem::size_of::<redis::RedisError>()); 102 + println!("Size of DbError: {} bytes", mem::size_of::<crate::errors::LuminaDbError>()); 103 + println!("Size of bb8 RunError Postgres: {} bytes", mem::size_of::<bb8::RunError<crate::postgres::Error>>()); 104 + println!("Size of bb8 RunError Redis: {} bytes", mem::size_of::<bb8::RunError<redis::RedisError>>()); 105 + println!("Size of String: {} bytes", mem::size_of::<String>()); 106 + }
+19 -19
server/src/timeline.rs
··· 20 20 * along with this program. If not, see <https://www.gnu.org/licenses/>. 21 21 */ 22 22 23 - use crate::errors::LuminaError; 23 + use crate::errors::{LuminaDbError, LuminaError}; 24 24 use crate::helpers::events::EventLogger; 25 25 use crate::{DbConn, error_elog, info_elog, user}; 26 26 use serde::{Deserialize, Serialize}; ··· 101 101 .arg(serialized) 102 102 .query_async(&mut **redis_conn) 103 103 .await 104 - .map_err(LuminaError::Redis)?; 104 + ?; 105 105 106 106 // Also cache metadata 107 107 let meta_key = get_cache_meta_key(timeline_id); ··· 111 111 .arg(total_count) 112 112 .query_async(&mut **redis_conn) 113 113 .await 114 - .map_err(LuminaError::Redis)?; 114 + ?; 115 115 116 116 Ok(()) 117 117 } ··· 128 128 .arg(cache_key) 129 129 .query_async(&mut **redis_conn) 130 130 .await 131 - .map_err(LuminaError::Redis)?; 131 + ?; 132 132 133 133 match cached_data { 134 134 Some(data) => { ··· 156 156 .arg(&pattern) 157 157 .query_async(&mut **redis_conn) 158 158 .await 159 - .map_err(LuminaError::Redis)?; 159 + ?; 160 160 161 161 cursor = result.0; 162 162 let keys = result.1; ··· 166 166 .arg(&keys) 167 167 .query_async(&mut **redis_conn) 168 168 .await 169 - .map_err(LuminaError::Redis)?; 169 + ?; 170 170 } 171 171 172 172 if cursor == 0 { ··· 184 184 let client = pg_pool 185 185 .get() 186 186 .await 187 - .map_err(|e| LuminaError::Bb8Pool(e.to_string()))?; 187 + ?; 188 188 let timeline_uuid = Uuid::parse_str(timeline_id).map_err(|_| LuminaError::UUidError)?; 189 189 let row = client 190 190 .query_one( ··· 192 192 &[&timeline_uuid], 193 193 ) 194 194 .await 195 - .map_err(LuminaError::Postgres)?; 195 + ?; 196 196 197 197 let count: i64 = row.get(0); 198 198 Ok(count as usize) ··· 212 212 let client = pg_pool 213 213 .get() 214 214 .await 215 - .map_err(|e| LuminaError::Bb8Pool(e.to_string()))?; 215 + ?; 216 216 let timeline_uuid = Uuid::parse_str(timeline_id).map_err(|_| LuminaError::UUidError)?; 217 217 let rows = client 218 218 .query( ··· 220 220 &[&timeline_uuid, &(limit as i64), &(offset as i64)], 221 221 ) 222 222 .await 223 - .map_err(LuminaError::Postgres)?; 223 + ?; 224 224 225 225 let post_ids = rows 226 226 .into_iter() ··· 247 247 DbConn::PgsqlConnection(_, redis_pool) => redis_pool 248 248 .get() 249 249 .await 250 - .map_err(|e| LuminaError::Bb8Pool(e.to_string()))?, 250 + ?, 251 251 }; 252 252 253 253 // Log the requested timeline id for tracking ··· 255 255 .arg(format!("timeline_lookup:{}", timeline_id)) 256 256 .query_async(&mut *redis_conn) 257 257 .await 258 - .map_err(LuminaError::Redis)?; 258 + ?; 259 259 260 260 // Check if this timeline should be cached 261 261 let should_cache = is_high_traffic_timeline(&mut redis_conn, timeline_id).await?; ··· 297 297 page, 298 298 s 299 299 ), 300 - LuminaError::Redis(redis_err) => error_elog!( 300 + LuminaError::DbError(LuminaDbError::Redis(redis_err)) => error_elog!( 301 301 event_logger, 302 302 "Failed to cache timeline {} page {}: {:?}", 303 303 timeline_id, ··· 370 370 let client = pg_pool 371 371 .get() 372 372 .await 373 - .map_err(|e| LuminaError::Bb8Pool(e.to_string()))?; 373 + ?; 374 374 let timeline_uuid = Uuid::parse_str(timeline_id).map_err(|_| LuminaError::UUidError)?; 375 375 let item_uuid = Uuid::parse_str(item_id).map_err(|_| LuminaError::UUidError)?; 376 376 client ··· 379 379 &[&timeline_uuid, &item_uuid], 380 380 ) 381 381 .await 382 - .map_err(LuminaError::Postgres)?; 382 + ?; 383 383 384 384 // Invalidate cache 385 385 let mut redis_conn = redis_pool 386 386 .get() 387 387 .await 388 - .map_err(|e| LuminaError::Bb8Pool(e.to_string()))?; 388 + ?; 389 389 if let Err(e) = invalidate_timeline_cache(&mut redis_conn, timeline_id).await { 390 390 error_elog!( 391 391 event_logger, ··· 414 414 let client = pg_pool 415 415 .get() 416 416 .await 417 - .map_err(|e| LuminaError::Bb8Pool(e.to_string()))?; 417 + ?; 418 418 let timeline_uuid = Uuid::parse_str(timeline_id).map_err(|_| LuminaError::UUidError)?; 419 419 let item_uuid = Uuid::parse_str(item_id).map_err(|_| LuminaError::UUidError)?; 420 420 client ··· 423 423 &[&timeline_uuid, &item_uuid], 424 424 ) 425 425 .await 426 - .map_err(LuminaError::Postgres)?; 426 + ?; 427 427 428 428 // Invalidate cache 429 429 let mut redis_conn = redis_pool 430 430 .get() 431 431 .await 432 - .map_err(|e| LuminaError::Bb8Pool(e.to_string()))?; 432 + ?; 433 433 if let Err(e) = invalidate_timeline_cache(&mut redis_conn, timeline_id).await { 434 434 error_elog!( 435 435 event_logger,
+18 -18
server/src/user.rs
··· 65 65 let client = pg_pool 66 66 .get() 67 67 .await 68 - .map_err(|e| LuminaError::Bb8Pool(e.to_string()))?; 68 + ?; 69 69 let row = client 70 70 .query_one("SELECT password FROM users WHERE id = $1", &[&self.id]) 71 71 .await 72 - .map_err(LuminaError::Postgres)?; 72 + ?; 73 73 let password: String = row.get(0); 74 74 Ok(password) 75 75 } ··· 90 90 let client = pg_pool 91 91 .get() 92 92 .await 93 - .map_err(|e| LuminaError::Bb8Pool(e.to_string()))?; 93 + ?; 94 94 // Some username and email validation should be done here 95 95 // Check if the email is already in use 96 96 let email_exists = client 97 97 .query("SELECT * FROM users WHERE email = $1", &[&email]) 98 98 .await 99 - .map_err(LuminaError::Postgres)?; 99 + ?; 100 100 if !email_exists.is_empty() { 101 101 return Err(LuminaError::RegisterEmailInUse); 102 102 } ··· 104 104 let username_exists = client 105 105 .query("SELECT * FROM users WHERE username = $1", &[&username]) 106 106 .await 107 - .map_err(LuminaError::Postgres)?; 107 + ?; 108 108 if !username_exists.is_empty() { 109 109 return Err(LuminaError::RegisterUsernameInUse); 110 110 } ··· 112 112 let id = client 113 113 .query_one("INSERT INTO users (email, username, password) VALUES ($1, $2, $3) RETURNING id", &[&email, &username, &password]) 114 114 .await 115 - .map_err(LuminaError::Postgres)?; 115 + ?; 116 116 Ok(User { 117 117 id: id.get(0), 118 118 email, ··· 136 136 let client = pg_pool 137 137 .get() 138 138 .await 139 - .map_err(|e| LuminaError::Bb8Pool(e.to_string()))?; 139 + ?; 140 140 let user = client 141 141 .query_one( 142 142 &format!("SELECT id, email, username, COALESCE(foreign_instance_id, '') FROM users WHERE {} = $1", identifyer_type), 143 143 &[&identifier], 144 144 ) 145 145 .await 146 - .map_err(LuminaError::Postgres)?; 146 + ?; 147 147 Ok(User { 148 148 id: user.get(0), 149 149 email: user.get(1), ··· 166 166 let client = pg_pool 167 167 .get() 168 168 .await 169 - .map_err(|e| LuminaError::Bb8Pool(e.to_string()))?; 169 + ?; 170 170 let session_key = Uuid::new_v4().to_string(); 171 171 let id = client 172 172 .query_one( ··· 174 174 &[&user_id, &session_key], 175 175 ) 176 176 .await 177 - .map_err(LuminaError::Postgres)?; 177 + ?; 178 178 info_elog!( 179 179 ev_log, 180 180 "New session created by {}", ··· 200 200 let client = pg_pool 201 201 .get() 202 202 .await 203 - .map_err(|e| LuminaError::Bb8Pool(e.to_string()))?; 203 + ?; 204 204 let user = client 205 205 .query_one("SELECT users.id, users.email, users.username FROM users JOIN sessions ON users.id = sessions.user_id WHERE sessions.session_key = $1", &[&token]) 206 206 .await 207 - .map_err(LuminaError::Postgres)?; 207 + ?; 208 208 Ok(User { 209 209 id: user.get(0), 210 210 email: user.get(1), ··· 229 229 let client = pg_pool 230 230 .get() 231 231 .await 232 - .map_err(|e| LuminaError::Bb8Pool(e.to_string()))?; 232 + ?; 233 233 let mut redis_conn = redis_pool 234 234 .get() 235 235 .await 236 - .map_err(|e| LuminaError::Bb8Pool(e.to_string()))?; 236 + ?; 237 237 // fastbloom_rs expects bytes, so we use the string as bytes 238 238 let email_key = String::from("bloom:email"); 239 239 let username_key = String::from("bloom:username"); ··· 248 248 let email_db = client 249 249 .query("SELECT * FROM users WHERE email = $1", &[&email]) 250 250 .await 251 - .map_err(LuminaError::Postgres)?; 251 + ?; 252 252 if !email_db.is_empty() { 253 253 return Err(LuminaError::RegisterEmailInUse); 254 254 } ··· 264 264 let username_db = client 265 265 .query("SELECT * FROM users WHERE username = $1", &[&username]) 266 266 .await 267 - .map_err(LuminaError::Postgres)?; 267 + ?; 268 268 if !username_db.is_empty() { 269 269 return Err(LuminaError::RegisterUsernameInUse); 270 270 } ··· 273 273 let email_db = client 274 274 .query("SELECT * FROM users WHERE email = $1", &[&email]) 275 275 .await 276 - .map_err(LuminaError::Postgres)?; 276 + ?; 277 277 if !email_db.is_empty() { 278 278 // Update bloom filter after DB check 279 279 let _: () = redis::cmd("BF.ADD") ··· 287 287 let username_db = client 288 288 .query("SELECT * FROM users WHERE username = $1", &[&username]) 289 289 .await 290 - .map_err(LuminaError::Postgres)?; 290 + ?; 291 291 if !username_db.is_empty() { 292 292 let _: () = redis::cmd("BF.ADD") 293 293 .arg(&username_key)