133 lines
3.9 KiB
Rust
133 lines
3.9 KiB
Rust
use axum::{
|
|
Json,
|
|
extract::{Path, State},
|
|
};
|
|
use chrono::Utc;
|
|
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
|
|
use std::sync::Arc;
|
|
use uuid::Uuid;
|
|
|
|
use crate::entities::{push_subscription, task_subscription};
|
|
use crate::error::{AppError, AppResult};
|
|
use crate::server::AppState;
|
|
use crate::server::auth::AuthenticatedUser;
|
|
use base64::Engine;
|
|
|
|
#[derive(serde::Deserialize)]
|
|
pub struct RegisterPushRequest {
|
|
pub endpoint: String,
|
|
pub p256dh: String,
|
|
pub auth: String,
|
|
}
|
|
|
|
pub async fn register_push(
|
|
State(state): State<Arc<AppState>>,
|
|
user: AuthenticatedUser,
|
|
Json(payload): Json<RegisterPushRequest>,
|
|
) -> AppResult<Json<serde_json::Value>> {
|
|
let user_sub = user.0.sub;
|
|
tracing::info!(
|
|
"Registering push subscription for user: {} with endpoint: {}",
|
|
user_sub,
|
|
payload.endpoint
|
|
);
|
|
|
|
// Check if subscription exists
|
|
let existing = push_subscription::Entity::find()
|
|
.filter(push_subscription::Column::UserSub.eq(user_sub.clone()))
|
|
.filter(push_subscription::Column::Endpoint.eq(payload.endpoint.clone()))
|
|
.one(&state.db)
|
|
.await
|
|
.map_err(AppError::Database)?;
|
|
|
|
if existing.is_none() {
|
|
let new_sub = push_subscription::ActiveModel {
|
|
id: Set(Uuid::new_v4()),
|
|
user_sub: Set(user_sub),
|
|
endpoint: Set(payload.endpoint),
|
|
p256dh: Set(payload.p256dh),
|
|
auth: Set(payload.auth),
|
|
created_at: Set(Utc::now().into()),
|
|
};
|
|
new_sub
|
|
.insert(&state.db)
|
|
.await
|
|
.map_err(AppError::Database)?;
|
|
}
|
|
|
|
Ok(Json(serde_json::json!({ "status": "registered" })))
|
|
}
|
|
|
|
pub async fn subscribe_task(
|
|
State(state): State<Arc<AppState>>,
|
|
user: AuthenticatedUser,
|
|
Path(task_id): Path<Uuid>,
|
|
) -> AppResult<Json<serde_json::Value>> {
|
|
let user_sub = user.0.sub;
|
|
|
|
let existing = task_subscription::Entity::find()
|
|
.filter(task_subscription::Column::UserSub.eq(user_sub.clone()))
|
|
.filter(task_subscription::Column::TaskId.eq(task_id))
|
|
.one(&state.db)
|
|
.await
|
|
.map_err(AppError::Database)?;
|
|
|
|
if existing.is_none() {
|
|
let new_sub = task_subscription::ActiveModel {
|
|
id: Set(Uuid::new_v4()),
|
|
user_sub: Set(user_sub),
|
|
task_id: Set(task_id),
|
|
created_at: Set(Utc::now().into()),
|
|
};
|
|
new_sub
|
|
.insert(&state.db)
|
|
.await
|
|
.map_err(AppError::Database)?;
|
|
}
|
|
|
|
Ok(Json(serde_json::json!({ "status": "subscribed" })))
|
|
}
|
|
|
|
pub async fn unsubscribe_task(
|
|
State(state): State<Arc<AppState>>,
|
|
user: AuthenticatedUser,
|
|
Path(task_id): Path<Uuid>,
|
|
) -> AppResult<Json<serde_json::Value>> {
|
|
let user_sub = user.0.sub;
|
|
|
|
task_subscription::Entity::delete_many()
|
|
.filter(task_subscription::Column::UserSub.eq(user_sub))
|
|
.filter(task_subscription::Column::TaskId.eq(task_id))
|
|
.exec(&state.db)
|
|
.await
|
|
.map_err(AppError::Database)?;
|
|
|
|
Ok(Json(serde_json::json!({ "status": "unsubscribed" })))
|
|
}
|
|
|
|
pub async fn get_vapid_key(
|
|
State(state): State<Arc<AppState>>,
|
|
) -> AppResult<Json<serde_json::Value>> {
|
|
let public_key = state.scheduler.push_sender.get_public_key()?;
|
|
let encoded = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(public_key);
|
|
Ok(Json(serde_json::json!({ "publicKey": encoded })))
|
|
}
|
|
|
|
pub async fn get_subscription_status(
|
|
State(state): State<Arc<AppState>>,
|
|
user: AuthenticatedUser,
|
|
Path(task_id): Path<Uuid>,
|
|
) -> AppResult<Json<serde_json::Value>> {
|
|
let user_sub = user.0.sub;
|
|
|
|
let existing = task_subscription::Entity::find()
|
|
.filter(task_subscription::Column::UserSub.eq(user_sub))
|
|
.filter(task_subscription::Column::TaskId.eq(task_id))
|
|
.one(&state.db)
|
|
.await
|
|
.map_err(AppError::Database)?;
|
|
|
|
Ok(Json(
|
|
serde_json::json!({ "isSubscribed": existing.is_some() }),
|
|
))
|
|
}
|