165 lines
4.9 KiB
Rust
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(())
|
|
}
|