refactor(android)!: remove FCM support

This commit is contained in:
2025-09-02 09:08:17 +04:00
parent 5e39fc9acc
commit 8d59e37976
12 changed files with 56 additions and 895 deletions

View File

@@ -4,7 +4,7 @@ pub mod users {
use crate::database::schema::users::dsl::*;
use crate::state::AppState;
use actix_web::web;
use diesel::{ExpressionMethods, QueryResult, insert_into};
use diesel::{insert_into, ExpressionMethods, QueryResult};
use diesel::{QueryDsl, RunQueryDsl};
use diesel::{SaveChangesDsl, SelectableHelper};
use std::ops::DerefMut;
@@ -146,19 +146,3 @@ pub mod users {
.execute(state.get_database().await.deref_mut())
}
}
pub mod fcm {
use crate::database::models::{FCM, User};
use crate::state::AppState;
use actix_web::web;
use diesel::QueryDsl;
use diesel::RunQueryDsl;
use diesel::{BelongingToDsl, QueryResult, SelectableHelper};
use std::ops::DerefMut;
pub async fn from_user(state: &web::Data<AppState>, user: &User) -> QueryResult<FCM> {
FCM::belonging_to(&user)
.select(FCM::as_select())
.get_result(state.get_database().await.deref_mut())
}
}

View File

@@ -1,13 +1,13 @@
use crate::database::driver;
use crate::database::models::{User, FCM};
use crate::extractors::base::{AsyncExtractor, FromRequestAsync};
use crate::database::models::User;
use crate::extractors::base::FromRequestAsync;
use crate::state::AppState;
use crate::utility::jwt;
use actix_macros::MiddlewareError;
use actix_web::body::BoxBody;
use actix_web::dev::Payload;
use actix_web::http::header;
use actix_web::{web, FromRequest, HttpRequest};
use actix_web::{web, HttpRequest};
use derive_more::Display;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
@@ -95,48 +95,3 @@ impl FromRequestAsync for User {
.map_err(|_| Error::NoUser.into())
}
}
pub struct UserExtractor<const FCM: bool> {
user: User,
fcm: Option<FCM>,
}
impl<const FCM: bool> UserExtractor<{ FCM }> {
pub fn user(&self) -> &User {
&self.user
}
pub fn fcm(&self) -> &Option<FCM> {
if !FCM {
panic!("FCM marked as not required, but it has been requested")
}
&self.fcm
}
}
/// Extractor of user and additional parameters from request with Bearer token.
impl<const FCM: bool> FromRequestAsync for UserExtractor<{ FCM }> {
type Error = actix_web::Error;
async fn from_request_async(
req: &HttpRequest,
payload: &mut Payload,
) -> Result<Self, Self::Error> {
let user = AsyncExtractor::<User>::from_request(req, payload)
.await?
.into_inner();
let app_state = req.app_data::<web::Data<AppState>>().unwrap();
Ok(Self {
fcm: if FCM {
driver::fcm::from_user(&app_state, &user).await.ok()
} else {
None
},
user,
})
}
}

View File

@@ -1,13 +1,13 @@
use crate::middlewares::authorization::JWTAuthorization;
use crate::middlewares::content_type::ContentTypeBootstrap;
use crate::state::{AppState, new_app_state};
use crate::state::{new_app_state, AppState};
use actix_web::dev::{ServiceFactory, ServiceRequest};
use actix_web::{App, Error, HttpServer};
use dotenvy::dotenv;
use log::info;
use std::io;
use utoipa_actix_web::AppExt;
use utoipa_actix_web::scope::Scope;
use utoipa_actix_web::AppExt;
use utoipa_rapidoc::RapiDoc;
mod state;
@@ -51,11 +51,6 @@ pub fn get_api_scope<
.service(routes::schedule::teacher)
.service(routes::schedule::teacher_names);
let fcm_scope = utoipa_actix_web::scope("/fcm")
.wrap(JWTAuthorization::default())
.service(routes::fcm::update_callback)
.service(routes::fcm::set_token);
let flow_scope = utoipa_actix_web::scope("/flow")
.wrap(JWTAuthorization {
ignore: &["/telegram-auth"],
@@ -70,7 +65,6 @@ pub fn get_api_scope<
.service(auth_scope)
.service(users_scope)
.service(schedule_scope)
.service(fcm_scope)
.service(flow_scope)
.service(vk_id_scope)
}

View File

@@ -1,5 +0,0 @@
mod set_token;
mod update_callback;
pub use set_token::*;
pub use update_callback::*;

View File

@@ -1,94 +0,0 @@
use crate::database;
use crate::database::models::FCM;
use crate::extractors::authorized_user::UserExtractor;
use crate::extractors::base::AsyncExtractor;
use crate::state::AppState;
use actix_web::{HttpResponse, Responder, patch, web};
use diesel::{RunQueryDsl, SaveChangesDsl};
use firebase_messaging_rs::topic::TopicManagementSupport;
use serde::Deserialize;
use std::ops::DerefMut;
#[derive(Debug, Deserialize)]
struct Params {
pub token: String,
}
async fn get_fcm(
app_state: &web::Data<AppState>,
user_data: &UserExtractor<true>,
token: String,
) -> Result<FCM, diesel::result::Error> {
match user_data.fcm() {
Some(fcm) => {
let mut fcm = fcm.clone();
fcm.token = token;
Ok(fcm)
}
None => {
let fcm = FCM {
user_id: user_data.user().id.clone(),
token,
topics: vec![],
};
match diesel::insert_into(database::schema::fcm::table)
.values(&fcm)
.execute(app_state.get_database().await.deref_mut())
{
Ok(_) => Ok(fcm),
Err(e) => Err(e),
}
}
}
}
#[utoipa::path(responses((status = OK)))]
#[patch("/set-token")]
pub async fn set_token(
app_state: web::Data<AppState>,
web::Query(params): web::Query<Params>,
user_data: AsyncExtractor<UserExtractor<true>>,
) -> impl Responder {
let user_data = user_data.into_inner();
// If token not changes - exit.
if let Some(fcm) = user_data.fcm() {
if fcm.token == params.token {
return HttpResponse::Ok();
}
}
let fcm = get_fcm(&app_state, &user_data, params.token.clone()).await;
if let Err(e) = fcm {
eprintln!("Failed to get FCM: {e}");
return HttpResponse::Ok();
}
let mut fcm = fcm.ok().unwrap();
// Add default topics.
if !fcm.topics.contains(&Some("common".to_string())) {
fcm.topics.push(Some("common".to_string()));
}
fcm.save_changes::<FCM>(app_state.get_database().await.deref_mut())
.unwrap();
let fcm_client = app_state.get_fcm_client().await.unwrap();
for topic in fcm.topics.clone() {
if let Some(topic) = topic {
if let Err(error) = fcm_client
.register_token_to_topic(&*topic, &*fcm.token)
.await
{
eprintln!("Failed to subscribe token to topic: {:?}", error);
return HttpResponse::Ok();
}
}
}
HttpResponse::Ok()
}

View File

@@ -1,24 +0,0 @@
use crate::database::driver::users::UserSave;
use crate::database::models::User;
use crate::extractors::base::AsyncExtractor;
use crate::state::AppState;
use actix_web::{HttpResponse, Responder, post, web};
#[utoipa::path(responses(
(status = OK),
(status = INTERNAL_SERVER_ERROR)
))]
#[post("/update-callback/{version}")]
async fn update_callback(
app_state: web::Data<AppState>,
version: web::Path<String>,
user: AsyncExtractor<User>,
) -> impl Responder {
let mut user = user.into_inner();
user.android_version = Some(version.into_inner());
user.save(&app_state).await.unwrap();
HttpResponse::Ok()
}

View File

@@ -1,5 +1,4 @@
pub mod auth;
pub mod fcm;
pub mod flow;
pub mod schedule;
mod schema;

View File

@@ -131,7 +131,7 @@ pub mod user {
use serde::Serialize;
//noinspection SpellCheckingInspection
/// Используется для скрытия чувствительных полей, таких как хеш пароля или FCM
/// Используется для скрытия чувствительных полей, таких как хеш пароля
#[derive(Serialize, utoipa::ToSchema, ResponderJson, OkResponse)]
#[serde(rename_all = "camelCase")]
pub struct UserResponse {

View File

@@ -1,15 +0,0 @@
use firebase_messaging_rs::FCMClient;
use std::env;
use tokio::sync::Mutex;
#[derive(Clone)]
pub struct FCMClientData;
impl FCMClientData {
pub async fn new() -> Option<Mutex<FCMClient>> {
match env::var("GOOGLE_APPLICATION_CREDENTIALS") {
Ok(_) => Some(Mutex::new(FCMClient::new().await.unwrap())),
Err(_) => None,
}
}
}

View File

@@ -1,11 +1,8 @@
mod env;
mod fcm_client;
pub use crate::state::env::AppEnv;
use crate::state::fcm_client::FCMClientData;
use actix_web::web;
use diesel::{Connection, PgConnection};
use firebase_messaging_rs::FCMClient;
use providers::base::{ScheduleProvider, ScheduleSnapshot};
use std::collections::HashMap;
use std::sync::Arc;
@@ -18,7 +15,6 @@ pub struct AppState {
database: Mutex<PgConnection>,
providers: HashMap<String, Arc<dyn ScheduleProvider>>,
env: AppEnv,
fcm_client: Option<Mutex<FCMClient>>,
}
impl AppState {
@@ -62,7 +58,6 @@ impl AppState {
),
env,
providers,
fcm_client: FCMClientData::new().await,
};
if this.env.schedule.auto_update {
@@ -92,13 +87,6 @@ impl AppState {
pub fn get_env(&self) -> &AppEnv {
&self.env
}
pub async fn get_fcm_client(&'_ self) -> Option<MutexGuard<'_, FCMClient>> {
match &self.fcm_client {
Some(client) => Some(client.lock().await),
None => None,
}
}
}
/// Create a new object web::Data<AppState>.