···1616 * along with this program. If not, see <https://www.gnu.org/licenses/>.
1717 */
18181919-use std::mem;
2019use crate::database::{self, DatabaseConnections};
2020+use crate::errors::LuminaError;
2121use crate::timeline;
2222-use crate::errors::LuminaError;
2222+use std::mem;
23232424#[tokio::test]
2525async fn test_database_setup() {
2626- let result = database::setup().await.expect("Database setup should succeed.");
2727- assert!(result.get_postgres_pool().get().await.is_ok(), "Should get Postgres connection");
2828- assert!(result.get_redis_pool().get().await.is_ok(), "Should get Redis connection");
2626+ let result = database::setup()
2727+ .await
2828+ .expect("Database setup should succeed.");
2929+ assert!(
3030+ result.get_postgres_pool().get().await.is_ok(),
3131+ "Should get Postgres connection"
3232+ );
3333+ assert!(
3434+ result.get_redis_pool().get().await.is_ok(),
3535+ "Should get Redis connection"
3636+ );
2937}
30383139#[tokio::test]
···93101 assert!(result.is_none(), "Cache should be invalidated");
94102}
951039696-97104#[test]
98105fn print_sizes() {
9999- println!("Size of LuminaError: {} bytes", mem::size_of::<LuminaError>());
100100- println!("Size of errors::LuminaDbError: {} bytes", mem::size_of::<crate::errors::LuminaDbError>());
106106+ println!(
107107+ "Size of LuminaError: {} bytes",
108108+ mem::size_of::<LuminaError>()
109109+ );
110110+ println!(
111111+ "Size of errors::LuminaDbError: {} bytes",
112112+ mem::size_of::<crate::errors::LuminaDbError>()
113113+ );
101114 println!("Size of EnvVar: {} bytes", mem::size_of::<crate::EnvVar>());
102102- println!("Size of InnerAppState: {} bytes", mem::size_of::<crate::InnerAppState>());
115115+ println!(
116116+ "Size of InnerAppState: {} bytes",
117117+ mem::size_of::<crate::InnerAppState>()
118118+ );
103119}
104120105121#[test]
106122fn test_error_sizes() {
107123 // We want to keep our error types small to minimize overhead when passing them around.
108108- assert!(mem::size_of::<LuminaError>() <= 16, "LuminaError should be 16 bytes or less");
109109- assert!(mem::size_of::<crate::errors::LuminaDbError>() <= 16, "LuminaDbError should be 16 bytes or less");
124124+ assert!(
125125+ mem::size_of::<LuminaError>() <= 16,
126126+ "LuminaError should be 16 bytes or less"
127127+ );
128128+ assert!(
129129+ mem::size_of::<crate::errors::LuminaDbError>() <= 16,
130130+ "LuminaDbError should be 16 bytes or less"
131131+ );
110132}
111133112134#[test]
113135fn test_appstate_size() {
114136 // Appstate is moved around a lot, so we want to keep it small, which is pretty easy since it just holds a single Arc pointer to a InnerAppState.
115115- assert!(mem::size_of::<crate::AppState>() <= 8, "AppState should be 8 bytes or less");
137137+ assert!(
138138+ mem::size_of::<crate::AppState>() <= 8,
139139+ "AppState should be 8 bytes or less"
140140+ );
116141 // This constraint should lower over time as we optimize InnerAppState
117117- assert!(mem::size_of::<crate::InnerAppState>() <= 88, "InnerAppState should be 88 bytes or less");
118118-}142142+ assert!(
143143+ mem::size_of::<crate::InnerAppState>() <= 88,
144144+ "InnerAppState should be 88 bytes or less"
145145+ );
146146+}
+17-48
server/src/timeline.rs
···9292 };
93939494 let cache_key = get_cache_key(timeline_id, page);
9595- let serialized = serde_json::to_string(&cached_page)
9696- ?;
9595+ let serialized = serde_json::to_string(&cached_page)?;
97969897 let _: () = redis::cmd("SETEX")
9998 .arg(cache_key)
10099 .arg(CACHE_TTL)
101100 .arg(serialized)
102101 .query_async(&mut **redis_conn)
103103- .await
104104- ?;
102102+ .await?;
105103106104 // Also cache metadata
107105 let meta_key = get_cache_meta_key(timeline_id);
···110108 .arg(CACHE_TTL)
111109 .arg(total_count)
112110 .query_async(&mut **redis_conn)
113113- .await
114114- ?;
111111+ .await?;
115112116113 Ok(())
117114}
···127124 let cached_data: Option<String> = redis::cmd("GET")
128125 .arg(cache_key)
129126 .query_async(&mut **redis_conn)
130130- .await
131131- ?;
127127+ .await?;
132128133129 match cached_data {
134130 Some(data) => {
···154150 .arg("MATCH")
155151 .arg(&pattern)
156152 .query_async(&mut **redis_conn)
157157- .await
158158- ?;
153153+ .await?;
159154160155 cursor = result.0;
161156 let keys = result.1;
···164159 let _: () = redis::cmd("DEL")
165160 .arg(&keys)
166161 .query_async(&mut **redis_conn)
167167- .await
168168- ?;
162162+ .await?;
169163 }
170164171165 if cursor == 0 {
···180174async fn fetch_timeline_total_count(db: &DbConn, timeline_id: &str) -> Result<usize, LuminaError> {
181175 match db {
182176 DbConn::PgsqlConnection(pg_pool, _redis_pool) => {
183183- let client = pg_pool
184184- .get()
185185- .await
186186- ?;
177177+ let client = pg_pool.get().await?;
187178 let timeline_uuid = Uuid::parse_str(timeline_id).map_err(|_| LuminaError::UUidError)?;
188179 let row = client
189180 .query_one(
190181 "SELECT COUNT(*) FROM timelines WHERE tlid = $1",
191182 &[&timeline_uuid],
192183 )
193193- .await
194194- ?;
184184+ .await?;
195185196186 let count: i64 = row.get(0);
197187 Ok(count as usize)
···208198) -> Result<Vec<String>, LuminaError> {
209199 match db {
210200 DbConn::PgsqlConnection(pg_pool, _redis_pool) => {
211211- let client = pg_pool
212212- .get()
213213- .await
214214- ?;
201201+ let client = pg_pool.get().await?;
215202 let timeline_uuid = Uuid::parse_str(timeline_id).map_err(|_| LuminaError::UUidError)?;
216203 let rows = client
217204 .query(
···243230244231 // Get Redis connection
245232 let mut redis_conn = match db {
246246- DbConn::PgsqlConnection(_, redis_pool) => redis_pool
247247- .get()
248248- .await
249249- ?,
233233+ DbConn::PgsqlConnection(_, redis_pool) => redis_pool.get().await?,
250234 };
251235252236 // Log the requested timeline id for tracking
253237 let _: () = redis::cmd("INCR")
254238 .arg(format!("timeline_lookup:{}", timeline_id))
255239 .query_async(&mut *redis_conn)
256256- .await
257257- ?;
240240+ .await?;
258241259242 // Check if this timeline should be cached
260243 let should_cache = is_high_traffic_timeline(&mut redis_conn, timeline_id).await?;
···366349 // Add to database
367350 match db {
368351 DbConn::PgsqlConnection(pg_pool, redis_pool) => {
369369- let client = pg_pool
370370- .get()
371371- .await
372372- ?;
352352+ let client = pg_pool.get().await?;
373353 let timeline_uuid = Uuid::parse_str(timeline_id).map_err(|_| LuminaError::UUidError)?;
374354 let item_uuid = Uuid::parse_str(item_id).map_err(|_| LuminaError::UUidError)?;
375355 client
···377357 "INSERT INTO timelines (tlid, item_id, timestamp) VALUES ($1, $2, NOW())",
378358 &[&timeline_uuid, &item_uuid],
379359 )
380380- .await
381381- ?;
360360+ .await?;
382361383362 // Invalidate cache
384384- let mut redis_conn = redis_pool
385385- .get()
386386- .await
387387- ?;
363363+ let mut redis_conn = redis_pool.get().await?;
388364 if let Err(e) = invalidate_timeline_cache(&mut redis_conn, timeline_id).await {
389365 error_elog!(
390366 event_logger,
···410386 // Remove from database
411387 match db {
412388 DbConn::PgsqlConnection(pg_pool, redis_pool) => {
413413- let client = pg_pool
414414- .get()
415415- .await
416416- ?;
389389+ let client = pg_pool.get().await?;
417390 let timeline_uuid = Uuid::parse_str(timeline_id).map_err(|_| LuminaError::UUidError)?;
418391 let item_uuid = Uuid::parse_str(item_id).map_err(|_| LuminaError::UUidError)?;
419392 client
···421394 "DELETE FROM timelines WHERE tlid = $1 AND item_id = $2",
422395 &[&timeline_uuid, &item_uuid],
423396 )
424424- .await
425425- ?;
397397+ .await?;
426398427399 // Invalidate cache
428428- let mut redis_conn = redis_pool
429429- .get()
430430- .await
431431- ?;
400400+ let mut redis_conn = redis_pool.get().await?;
432401 if let Err(e) = invalidate_timeline_cache(&mut redis_conn, timeline_id).await {
433402 error_elog!(
434403 event_logger,
+49-70
server/src/user.rs
···6262 async fn get_hashed_password(self, database: &DbConn) -> Result<String, LuminaError> {
6363 match database {
6464 DbConn::PgsqlConnection(pg_pool, _) => {
6565- let client = pg_pool
6666- .get()
6767- .await
6868- ?;
6565+ let client = pg_pool.get().await?;
6966 let row = client
7067 .query_one("SELECT password FROM users WHERE id = $1", &[&self.id])
7171- .await
7272- ?;
6868+ .await?;
7369 let password: String = row.get(0);
7470 Ok(password)
7571 }
···8783 bcrypt::hash(password, bcrypt::DEFAULT_COST).map_err(|_| LuminaError::BcryptError)?;
8884 match db {
8985 DbConn::PgsqlConnection(pg_pool, _) => {
9090- let client = pg_pool
9191- .get()
9292- .await
9393- ?;
8686+ let client = pg_pool.get().await?;
9487 // Some username and email validation should be done here
9588 // Check if the email is already in use
9689 let email_exists = client
9790 .query("SELECT * FROM users WHERE email = $1", &[&email])
9898- .await
9999- ?;
9191+ .await?;
10092 if !email_exists.is_empty() {
10193 return Err(LuminaError::RegisterEmailInUse);
10294 }
10395 // Check if the username is already in use
10496 let username_exists = client
10597 .query("SELECT * FROM users WHERE username = $1", &[&username])
106106- .await
107107- ?;
9898+ .await?;
10899 if !username_exists.is_empty() {
109100 return Err(LuminaError::RegisterUsernameInUse);
110101 }
···133124 };
134125 match db {
135126 DbConn::PgsqlConnection(pg_pool, _) => {
136136- let client = pg_pool
137137- .get()
138138- .await
139139- ?;
127127+ let client = pg_pool.get().await?;
140128 let user = client
141129 .query_one(
142130 &format!("SELECT id, email, username, COALESCE(foreign_instance_id, '') FROM users WHERE {} = $1", identifyer_type),
···163151 let user_id = user.id;
164152 match db {
165153 DbConn::PgsqlConnection(pg_pool, _) => {
166166- let client = pg_pool
167167- .get()
168168- .await
169169- ?;
154154+ let client = pg_pool.get().await?;
170155 let session_key = Uuid::new_v4().to_string();
171156 let id = client
172157 .query_one(
173158 "INSERT INTO sessions (user_id, session_key) VALUES ($1, $2) RETURNING id",
174159 &[&user_id, &session_key],
175160 )
176176- .await
177177- ?;
161161+ .await?;
178162 info_elog!(
179163 ev_log,
180164 "New session created by {}",
···197181 ) -> Result<User, LuminaError> {
198182 match db {
199183 DbConn::PgsqlConnection(pg_pool, _) => {
200200- let client = pg_pool
201201- .get()
202202- .await
203203- ?;
184184+ let client = pg_pool.get().await?;
204185 let user = client
205186 .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])
206187 .await
···226207 // Check if the email or username is already in use using fastbloom algorithm with Redis, and fallback to DB check if not found. If not in either, we can go on.
227208 match db {
228209 DbConn::PgsqlConnection(pg_pool, redis_pool) => {
229229- let client = pg_pool
230230- .get()
231231- .await
232232- ?;
233233- let mut redis_conn = redis_pool
234234- .get()
235235- .await
236236- ?;
210210+ let client = pg_pool.get().await?;
211211+ let mut redis_conn = redis_pool.get().await?;
237212 // fastbloom_rs expects bytes, so we use the string as bytes
238213 let email_key = String::from("bloom:email");
239214 let username_key = String::from("bloom:username");
···247222 // Fallback to DB check if in bloom filter
248223 let email_db = client
249224 .query("SELECT * FROM users WHERE email = $1", &[&email])
250250- .await
251251- ?;
225225+ .await?;
252226 if !email_db.is_empty() {
253227 return Err(LuminaError::RegisterEmailInUse);
254228 }
···263237 // Fallback to DB check if in bloom filter
264238 let username_db = client
265239 .query("SELECT * FROM users WHERE username = $1", &[&username])
266266- .await
267267- ?;
240240+ .await?;
268241 if !username_db.is_empty() {
269242 return Err(LuminaError::RegisterUsernameInUse);
270243 }
···272245 // Fallback to DB check if not in bloom filter
273246 let email_db = client
274247 .query("SELECT * FROM users WHERE email = $1", &[&email])
275275- .await
276276- ?;
248248+ .await?;
277249 if !email_db.is_empty() {
278250 // Update bloom filter after DB check
279251 let _: () = redis::cmd("BF.ADD")
···286258 }
287259 let username_db = client
288260 .query("SELECT * FROM users WHERE username = $1", &[&username])
289289- .await
290290- ?;
261261+ .await?;
291262 if !username_db.is_empty() {
292263 let _: () = redis::cmd("BF.ADD")
293264 .arg(&username_key)
···381352 }
382353 if password.len() > 100 {
383354 return Err(LuminaError::RegisterPasswordNotValid(
384384- OnRegisterPasswordNotValid::TooLong
355355+ OnRegisterPasswordNotValid::TooLong,
385356 ));
386357 }
387358 if !password.chars().any(char::is_uppercase) {
388388- return Err(LuminaError::RegisterPasswordNotValid(
389389- OnRegisterPasswordNotValid::MissingUppercase
359359+ return Err(LuminaError::RegisterPasswordNotValid(
360360+ OnRegisterPasswordNotValid::MissingUppercase,
390361 ));
391362 }
392363 if !password.chars().any(char::is_lowercase) {
393364 return Err(LuminaError::RegisterPasswordNotValid(
394394- OnRegisterPasswordNotValid::MissingLowercase
365365+ OnRegisterPasswordNotValid::MissingLowercase,
395366 ));
396367 }
397368 if !password.chars().any(char::is_numeric) {
398398- return Err(LuminaError::RegisterPasswordNotValid(OnRegisterPasswordNotValid::MissingNumber
369369+ return Err(LuminaError::RegisterPasswordNotValid(
370370+ OnRegisterPasswordNotValid::MissingNumber,
399371 ));
400372 }
401373 }
402374 Ok(())
403375}
404376405405-406377#[derive(Debug)]
407378pub(crate) enum OnRegisterUsernameInvalid {
408379 TooLong,
409380 TooShort,
410410- InvalidCharacters
381381+ InvalidCharacters,
411382}
412383impl std::fmt::Display for OnRegisterUsernameInvalid {
413384 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
414414- write!(f, "{}", match self {
415415- OnRegisterUsernameInvalid::TooLong => "Username too long",
416416- OnRegisterUsernameInvalid::TooShort => "Username too short",
417417- OnRegisterUsernameInvalid::InvalidCharacters => {
418418- "Username contains invalid characters"
385385+ write!(
386386+ f,
387387+ "{}",
388388+ match self {
389389+ OnRegisterUsernameInvalid::TooLong => "Username too long",
390390+ OnRegisterUsernameInvalid::TooShort => "Username too short",
391391+ OnRegisterUsernameInvalid::InvalidCharacters => {
392392+ "Username contains invalid characters"
393393+ }
419394 }
420420- })
395395+ )
421396 }
422397}
423398#[derive(Debug)]
···430405}
431406impl std::fmt::Display for OnRegisterPasswordNotValid {
432407 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
433433- write!(f, "{}", match self {
434434- OnRegisterPasswordNotValid::TooShort => "Password too short",
435435- OnRegisterPasswordNotValid::TooLong => "Password too long",
436436- OnRegisterPasswordNotValid::MissingUppercase => {
437437- "Password must contain at least one uppercase letter"
438438- }
439439- OnRegisterPasswordNotValid::MissingLowercase => {
440440- "Password must contain at least one lowercase letter"
408408+ write!(
409409+ f,
410410+ "{}",
411411+ match self {
412412+ OnRegisterPasswordNotValid::TooShort => "Password too short",
413413+ OnRegisterPasswordNotValid::TooLong => "Password too long",
414414+ OnRegisterPasswordNotValid::MissingUppercase => {
415415+ "Password must contain at least one uppercase letter"
416416+ }
417417+ OnRegisterPasswordNotValid::MissingLowercase => {
418418+ "Password must contain at least one lowercase letter"
419419+ }
420420+ OnRegisterPasswordNotValid::MissingNumber => {
421421+ "Password must contain at least one number"
422422+ }
441423 }
442442- OnRegisterPasswordNotValid::MissingNumber => {
443443- "Password must contain at least one number"
444444- }
445445- })
424424+ )
446425 }
447447-}426426+}