Files
msgs/src/utils.rs
T
2025-11-28 01:38:59 +07:00

165 lines
4.9 KiB
Rust

use crate::model;
use crate::state;
use axum::extract::ws::Message;
use rand::distributions::Alphanumeric;
use rand::{Rng, thread_rng};
use sqlx::{Row, SqlitePool};
use std::io::Cursor;
// Utility functions
pub fn generate_token() -> String {
thread_rng()
.sample_iter(&Alphanumeric)
.take(32)
.map(char::from)
.collect()
}
pub async fn verify_token(pool: &SqlitePool, token: &str) -> Result<Option<String>, sqlx::Error> {
let result = sqlx::query(
"SELECT username FROM sessions WHERE token = ? AND expires_at > datetime('now')",
)
.bind(token)
.fetch_optional(pool)
.await?;
Ok(result.map(|row| row.get("username")))
}
pub async fn mark_messages_as_read(
pool: &SqlitePool,
from_user: &str,
to_user: &str,
) -> Result<(), sqlx::Error> {
let result = sqlx::query(
"UPDATE messages SET read = TRUE WHERE from_user = ? AND to_user = ? AND read = FALSE AND deleted_for_everyone = FALSE"
)
.bind(from_user)
.bind(to_user)
.execute(pool)
.await?;
println!(
"✅ Marked {} messages as read from {} to {}",
result.rows_affected(),
from_user,
to_user
);
Ok(())
}
// Process and resize avatar
pub async fn process_avatar(data: &[u8]) -> Result<Vec<u8>, String> {
// Load image
let img = match image::load_from_memory(data) {
Ok(img) => img,
Err(_) => return Err("Invalid image format".to_string()),
};
// Resize image to 150x150 pixels
let resized = img.resize_to_fill(150, 150, image::imageops::FilterType::Lanczos3);
// Convert to JPEG
let mut jpeg_data = Vec::new();
match resized.write_to(&mut Cursor::new(&mut jpeg_data), image::ImageFormat::Jpeg) {
Ok(_) => Ok(jpeg_data),
Err(_) => Err("Error processing image".to_string()),
}
}
pub async fn get_message_history(
pool: &SqlitePool,
username: &str,
other_user: &str,
) -> Result<Vec<model::ChatMessage>, sqlx::Error> {
// Если указан конкретный собеседник, помечаем сообщения как прочитанные
if !other_user.is_empty() {
mark_messages_as_read(pool, other_user, username).await?;
}
let query = if other_user.is_empty() {
// Получаем все сообщения пользователя (исключая удаленные)
r#"
SELECT id, from_user, to_user, content, timestamp, message_type
FROM messages
WHERE (from_user = ? OR to_user = ?)
AND deleted_for_everyone = FALSE
AND ((from_user = ? AND deleted_for_sender = FALSE) OR (to_user = ? AND deleted_for_receiver = FALSE))
ORDER BY timestamp
"#
} else {
// Получаем сообщения только с конкретным пользователем (исключая удаленные)
r#"
SELECT id, from_user, to_user, content, timestamp, message_type
FROM messages
WHERE ((from_user = ? AND to_user = ?) OR (from_user = ? AND to_user = ?))
AND deleted_for_everyone = FALSE
AND ((from_user = ? AND deleted_for_sender = FALSE) OR (to_user = ? AND deleted_for_receiver = FALSE))
ORDER BY timestamp
"#
};
let rows = if other_user.is_empty() {
sqlx::query(query)
.bind(username)
.bind(username)
.bind(username)
.bind(username)
.fetch_all(pool)
.await?
} else {
sqlx::query(query)
.bind(username)
.bind(other_user)
.bind(other_user)
.bind(username)
.bind(username)
.bind(username)
.fetch_all(pool)
.await?
};
let mut messages = Vec::new();
for row in rows {
let message = model::ChatMessage {
id: row.get("id"),
from_user: row.get("from_user"),
to_user: row.get("to_user"),
content: row.get("content"),
timestamp: row.get("timestamp"),
message_type: row.get("message_type"),
};
messages.push(message);
}
Ok(messages)
}
pub async fn broadcast_user_list(users: &state::UserMap) {
let user_list: Vec<String> = users.read().await.keys().cloned().collect();
let user_list_msg = serde_json::json!({
"type": "user_list",
"users": user_list
});
for tx in users.read().await.values() {
let _ = tx.send(Message::Text(user_list_msg.to_string()));
}
}
pub async fn save_message(pool: &SqlitePool, msg: &model::ChatMessage) -> Result<(), sqlx::Error> {
sqlx::query(
"INSERT OR REPLACE INTO messages (id, from_user, to_user, content, timestamp, message_type) VALUES (?, ?, ?, ?, ?, ?)",
)
.bind(&msg.id)
.bind(&msg.from_user)
.bind(&msg.to_user)
.bind(&msg.content)
.bind(msg.timestamp)
.bind(&msg.message_type)
.execute(pool)
.await?;
Ok(())
}