Подключение FCM.

This commit is contained in:
2025-04-15 14:34:53 +04:00
parent 057dac5b09
commit 4c5e0761eb
8 changed files with 706 additions and 64 deletions

725
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,7 @@ diesel = { version = "2.2.8", features = ["postgres"] }
diesel-derive-enum = { git = "https://github.com/Havunen/diesel-derive-enum.git", features = ["postgres"] } diesel-derive-enum = { git = "https://github.com/Havunen/diesel-derive-enum.git", features = ["postgres"] }
dotenvy = "0.15.7" dotenvy = "0.15.7"
env_logger = "0.11.7" env_logger = "0.11.7"
firebase-messaging-rs = { git = "ssh://git@github.com/i10416/firebase-messaging-rs.git" }
futures-util = "0.3.31" futures-util = "0.3.31"
fuzzy-matcher = "0.3.7" fuzzy-matcher = "0.3.7"
jsonwebtoken = { version = "9.3.1", features = ["use_pem"] } jsonwebtoken = { version = "9.3.1", features = ["use_pem"] }

View File

@@ -4,6 +4,7 @@ use crate::xls_downloader::basic_impl::BasicXlsDownloader;
use actix_web::web; use actix_web::web;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use diesel::{Connection, PgConnection}; use diesel::{Connection, PgConnection};
use firebase_messaging_rs::FCMClient;
use sha1::{Digest, Sha1}; use sha1::{Digest, Sha1};
use std::env; use std::env;
use std::hash::Hash; use std::hash::Hash;
@@ -55,10 +56,11 @@ pub struct AppState {
pub schedule: Mutex<Option<Schedule>>, pub schedule: Mutex<Option<Schedule>>,
pub database: Mutex<PgConnection>, pub database: Mutex<PgConnection>,
pub vk_id: VkId, pub vk_id: VkId,
pub fcm_client: Mutex<FCMClient>,
} }
impl AppState { impl AppState {
pub fn new() -> Self { pub async fn new() -> Self {
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
Self { Self {
@@ -69,11 +71,12 @@ impl AppState {
.unwrap_or_else(|_| panic!("Error connecting to {}", database_url)), .unwrap_or_else(|_| panic!("Error connecting to {}", database_url)),
), ),
vk_id: VkId::new(), vk_id: VkId::new(),
fcm_client: Mutex::new(FCMClient::new().await.expect("FCM client must be created")),
} }
} }
} }
/// Создание нового объекта web::Data<AppState> /// Создание нового объекта web::Data<AppState>
pub fn app_state() -> web::Data<AppState> { pub async fn app_state() -> web::Data<AppState> {
web::Data::new(AppState::new()) web::Data::new(AppState::new().await)
} }

View File

@@ -58,6 +58,7 @@ pub struct User {
#[derive( #[derive(
Debug, Debug,
Clone,
Serialize, Serialize,
Identifiable, Identifiable,
Queryable, Queryable,

View File

@@ -70,7 +70,7 @@ async fn main() {
unsafe { std::env::set_var("RUST_LOG", "debug") }; unsafe { std::env::set_var("RUST_LOG", "debug") };
env_logger::init(); env_logger::init();
let app_state = app_state(); let app_state = app_state().await;
HttpServer::new(move || { HttpServer::new(move || {
let (app, api) = App::new() let (app, api) = App::new()

View File

@@ -150,7 +150,7 @@ mod tests {
use std::fmt::Write; use std::fmt::Write;
async fn sign_in_client(data: Request) -> ServiceResponse { async fn sign_in_client(data: Request) -> ServiceResponse {
let app = test_app(test_app_state(), sign_in).await; let app = test_app(test_app_state().await, sign_in).await;
let req = test::TestRequest::with_uri("/sign-in") let req = test::TestRequest::with_uri("/sign-in")
.method(Method::POST) .method(Method::POST)
@@ -160,7 +160,7 @@ mod tests {
test::call_service(&app, req).await test::call_service(&app, req).await
} }
fn prepare(username: String) { async fn prepare(username: String) {
let id = { let id = {
let mut sha = Sha1::new(); let mut sha = Sha1::new();
sha.update(&username); sha.update(&username);
@@ -178,7 +178,7 @@ mod tests {
test_env(); test_env();
let app_state = static_app_state(); let app_state = static_app_state().await;
driver::users::insert_or_ignore( driver::users::insert_or_ignore(
&app_state, &app_state,
&User { &User {
@@ -197,7 +197,7 @@ mod tests {
#[actix_web::test] #[actix_web::test]
async fn sign_in_ok() { async fn sign_in_ok() {
prepare("test::sign_in_ok".to_string()); prepare("test::sign_in_ok".to_string()).await;
let resp = sign_in_client(Request { let resp = sign_in_client(Request {
username: "test::sign_in_ok".to_string(), username: "test::sign_in_ok".to_string(),
@@ -210,7 +210,7 @@ mod tests {
#[actix_web::test] #[actix_web::test]
async fn sign_in_err() { async fn sign_in_err() {
prepare("test::sign_in_err".to_string()); prepare("test::sign_in_err".to_string()).await;
let invalid_username = sign_in_client(Request { let invalid_username = sign_in_client(Request {
username: "test::sign_in_err::username".to_string(), username: "test::sign_in_err::username".to_string(),

View File

@@ -258,7 +258,7 @@ mod tests {
} }
async fn sign_up_client(data: SignUpPartial) -> ServiceResponse { async fn sign_up_client(data: SignUpPartial) -> ServiceResponse {
let app = test_app(test_app_state(), sign_up).await; let app = test_app(test_app_state().await, sign_up).await;
let req = test::TestRequest::with_uri("/sign-up") let req = test::TestRequest::with_uri("/sign-up")
.method(Method::POST) .method(Method::POST)
@@ -280,7 +280,7 @@ mod tests {
test_env(); test_env();
let app_state = static_app_state(); let app_state = static_app_state().await;
driver::users::delete_by_username(&app_state, &"test::sign_up_valid".to_string()); driver::users::delete_by_username(&app_state, &"test::sign_up_valid".to_string());
// test // test
@@ -301,7 +301,7 @@ mod tests {
test_env(); test_env();
let app_state = static_app_state(); let app_state = static_app_state().await;
driver::users::delete_by_username( driver::users::delete_by_username(
&app_state, &app_state,
&"test::sign_up_multiple".to_string(), &"test::sign_up_multiple".to_string(),

View File

@@ -1,16 +1,16 @@
#[cfg(test)] #[cfg(test)]
pub(crate) mod tests { pub(crate) mod tests {
use crate::app_state::{app_state, AppState, Schedule}; use crate::app_state::{AppState, Schedule, app_state};
use crate::parser::tests::test_result; use crate::parser::tests::test_result;
use actix_web::{web}; use actix_web::web;
use std::sync::LazyLock; use tokio::sync::OnceCell;
pub fn test_env() { pub fn test_env() {
dotenvy::from_path(".env.test").expect("Failed to load test environment file"); dotenvy::from_path(".env.test").expect("Failed to load test environment file");
} }
pub fn test_app_state() -> web::Data<AppState> { pub async fn test_app_state() -> web::Data<AppState> {
let state = app_state(); let state = app_state().await;
let mut schedule_lock = state.schedule.lock().unwrap(); let mut schedule_lock = state.schedule.lock().unwrap();
*schedule_lock = Some(Schedule { *schedule_lock = Some(Schedule {
@@ -24,9 +24,9 @@ pub(crate) mod tests {
state.clone() state.clone()
} }
pub fn static_app_state() -> web::Data<AppState> { pub async fn static_app_state() -> web::Data<AppState> {
static STATE: LazyLock<web::Data<AppState>> = LazyLock::new(|| test_app_state()); static STATE: OnceCell<web::Data<AppState>> = OnceCell::const_new();
STATE.clone() STATE.get_or_init(|| test_app_state()).await.clone()
} }
} }