feat: prepare for push notification II
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,6 @@
|
|||||||
|
# private key
|
||||||
|
keys
|
||||||
|
|
||||||
# --> sqlite3
|
# --> sqlite3
|
||||||
*.db
|
*.db
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ license = "WTFPL-2.0"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["mastlogin"]
|
default = ["mastlogin"]
|
||||||
mastlogin = ["url", "reqwest"]
|
mastlogin = ["reqwest"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rocket = { version = "=0.5.0-rc.1", features = ["json"] }
|
rocket = { version = "=0.5.0-rc.1", features = ["json"] }
|
||||||
@@ -26,6 +26,7 @@ dotenv = "0.15.0"
|
|||||||
sha2 = "0.10.2"
|
sha2 = "0.10.2"
|
||||||
log = "0.4.16"
|
log = "0.4.16"
|
||||||
env_logger = "0.9.0"
|
env_logger = "0.9.0"
|
||||||
|
web-push = "0.9.2"
|
||||||
|
url = "2.2.2"
|
||||||
|
|
||||||
url = { version="2.2.2",optional = true }
|
|
||||||
reqwest = { version = "0.11.10", features = ["json"], optional = true }
|
reqwest = { version = "0.11.10", features = ["json"], optional = true }
|
||||||
|
|||||||
@@ -9,6 +9,13 @@ use crate::schema;
|
|||||||
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
|
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||||
use rocket::form::Form;
|
use rocket::form::Form;
|
||||||
use rocket::serde::json::json;
|
use rocket::serde::json::json;
|
||||||
|
use rocket::serde::json::serde_json;
|
||||||
|
use rocket::serde::Serialize;
|
||||||
|
use std::fs::File;
|
||||||
|
use url::Url;
|
||||||
|
use web_push::{
|
||||||
|
ContentEncoding, SubscriptionInfo, VapidSignatureBuilder, WebPushClient, WebPushMessageBuilder,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(FromForm)]
|
#[derive(FromForm)]
|
||||||
pub struct AttentionInput {
|
pub struct AttentionInput {
|
||||||
@@ -76,3 +83,57 @@ pub async fn get_attention(user: CurrentUser, db: Db, rconn: RdsConn) -> JsonApi
|
|||||||
|
|
||||||
code0!(ps_data)
|
code0!(ps_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(FromForm)]
|
||||||
|
pub struct NotificatinInput {
|
||||||
|
enable: bool,
|
||||||
|
endpoint: String,
|
||||||
|
auth: String,
|
||||||
|
p256dh: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
struct PushData {
|
||||||
|
title: String,
|
||||||
|
pid: i32,
|
||||||
|
text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/post/<pid>/notification", data = "<ni>")]
|
||||||
|
pub async fn set_notification(pid: i32, ni: Form<NotificatinInput>, _user: CurrentUser) -> JsonApi {
|
||||||
|
let url_host = Url::parse(&ni.endpoint)
|
||||||
|
.map_err(|_| UnknownPushEndpoint)?
|
||||||
|
.host()
|
||||||
|
.ok_or(UnknownPushEndpoint)?
|
||||||
|
.to_string();
|
||||||
|
(url_host.ends_with("googleapis.com") || url_host.ends_with("mozilla.com"))
|
||||||
|
.then(|| ())
|
||||||
|
.ok_or(UnknownPushEndpoint)?;
|
||||||
|
|
||||||
|
if ni.enable {
|
||||||
|
let subscription_info = SubscriptionInfo::new(&ni.endpoint, &ni.p256dh, &ni.auth);
|
||||||
|
|
||||||
|
let file = File::open("keys/private.pem").unwrap();
|
||||||
|
let sig_builder = VapidSignatureBuilder::from_pem(file, &subscription_info)
|
||||||
|
.unwrap()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut builder = WebPushMessageBuilder::new(&subscription_info).unwrap();
|
||||||
|
let data = PushData {
|
||||||
|
title: "测试".to_owned(),
|
||||||
|
pid,
|
||||||
|
text: format!("#{} 开启提醒测试成功,消息提醒功能即将正式上线", &pid),
|
||||||
|
};
|
||||||
|
let content = serde_json::to_string(&data).unwrap();
|
||||||
|
builder.set_payload(ContentEncoding::Aes128Gcm, content.as_bytes());
|
||||||
|
builder.set_vapid_signature(sig_builder);
|
||||||
|
|
||||||
|
let client = WebPushClient::new()?;
|
||||||
|
|
||||||
|
client.send(builder.build()?).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
code0!()
|
||||||
|
}
|
||||||
|
|||||||
@@ -119,12 +119,14 @@ pub enum PolicyError {
|
|||||||
YouAreTmp,
|
YouAreTmp,
|
||||||
NoReason,
|
NoReason,
|
||||||
OldApi,
|
OldApi,
|
||||||
|
UnknownPushEndpoint,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ApiError {
|
pub enum ApiError {
|
||||||
Db(diesel::result::Error),
|
Db(diesel::result::Error),
|
||||||
Rds(redis::RedisError),
|
Rds(redis::RedisError),
|
||||||
|
WebPush(web_push::WebPushError),
|
||||||
Pc(PolicyError),
|
Pc(PolicyError),
|
||||||
IO(std::io::Error),
|
IO(std::io::Error),
|
||||||
}
|
}
|
||||||
@@ -134,6 +136,7 @@ impl<'r> Responder<'r, 'static> for ApiError {
|
|||||||
match self {
|
match self {
|
||||||
ApiError::Db(e) => e2s!(e).respond_to(req),
|
ApiError::Db(e) => e2s!(e).respond_to(req),
|
||||||
ApiError::Rds(e) => e2s!(e).respond_to(req),
|
ApiError::Rds(e) => e2s!(e).respond_to(req),
|
||||||
|
ApiError::WebPush(e) => e2s!(e).respond_to(req),
|
||||||
ApiError::IO(e) => e2s!(e).respond_to(req),
|
ApiError::IO(e) => e2s!(e).respond_to(req),
|
||||||
ApiError::Pc(e) => json!({
|
ApiError::Pc(e) => json!({
|
||||||
"code": -1,
|
"code": -1,
|
||||||
@@ -144,7 +147,8 @@ impl<'r> Responder<'r, 'static> for ApiError {
|
|||||||
PolicyError::TitleUsed => "头衔已被使用",
|
PolicyError::TitleUsed => "头衔已被使用",
|
||||||
PolicyError::YouAreTmp => "临时用户只可发布内容和进入单个洞",
|
PolicyError::YouAreTmp => "临时用户只可发布内容和进入单个洞",
|
||||||
PolicyError::NoReason => "未填写理由",
|
PolicyError::NoReason => "未填写理由",
|
||||||
PolicyError::OldApi => "请使用最新版前端地址并检查更新"
|
PolicyError::OldApi => "请使用最新版前端地址并检查更新",
|
||||||
|
PolicyError::UnknownPushEndpoint => "未知的浏览器推送地址",
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.respond_to(req),
|
.respond_to(req),
|
||||||
@@ -152,6 +156,12 @@ impl<'r> Responder<'r, 'static> for ApiError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<web_push::WebPushError> for ApiError {
|
||||||
|
fn from(err: web_push::WebPushError) -> ApiError {
|
||||||
|
ApiError::WebPush(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<diesel::result::Error> for ApiError {
|
impl From<diesel::result::Error> for ApiError {
|
||||||
fn from(err: diesel::result::Error) -> ApiError {
|
fn from(err: diesel::result::Error) -> ApiError {
|
||||||
ApiError::Db(err)
|
ApiError::Db(err)
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ async fn main() -> Result<(), rocket::Error> {
|
|||||||
api::comment::add_comment,
|
api::comment::add_comment,
|
||||||
api::upload::local_upload,
|
api::upload::local_upload,
|
||||||
cors::options_handler,
|
cors::options_handler,
|
||||||
|
api::attention::set_notification,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.mount(
|
.mount(
|
||||||
|
|||||||
Reference in New Issue
Block a user