From 0a1e8b579fd9994af83a32277bca5153c069e0d8 Mon Sep 17 00:00:00 2001 From: RocSun <710989028@qq.com> Date: Fri, 12 Jan 2024 11:34:42 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=81=E8=A3=85=E9=83=A8=E5=88=86openApi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/ing/comment.rs | 77 +++++++++++++++ src/apis/ing/mod.rs | 197 +++++++++++++++++++++++++++++++++++++++ src/apis/mod.rs | 7 +- src/apis/statuses/mod.rs | 13 --- src/apis/token/mod.rs | 134 ++++++++++++++++++++++++++ 5 files changed, 414 insertions(+), 14 deletions(-) create mode 100644 src/apis/ing/comment.rs create mode 100644 src/apis/ing/mod.rs delete mode 100644 src/apis/statuses/mod.rs create mode 100644 src/apis/token/mod.rs diff --git a/src/apis/ing/comment.rs b/src/apis/ing/comment.rs new file mode 100644 index 0000000..163debc --- /dev/null +++ b/src/apis/ing/comment.rs @@ -0,0 +1,77 @@ +//! 闪存评论相关 +//! + +use anyhow::{Ok, Result}; +use reqwest::{Client, Response}; +use serde::{Deserialize, Serialize}; + +use crate::{infra::http::RequestBuilderExt, openapi}; + +/// 闪存评论及评论回复 +/// +/// replay_to: 在web端有一个ReplyToUserId,这里盲猜是这个 +/// parent_comment_id: 0 是对某条闪存评论,如果对闪存评论要回应,这里则是闪存评论的id +/// content: 评论内容。 如果是对闪存评论回应,则应加上`@用户名称` +/// +#[derive(Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "PascalCase")] +#[serde(default)] +pub struct StatusComment { + #[serde(skip)] + pub status_id: String, + pub replay_to: u64, + pub parent_comment_id: u64, + pub content: String, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct IngCommentEntry { + pub id: usize, + pub content: String, + #[serde(rename = "DateAdded")] + pub create_time: String, + pub status_id: usize, + pub user_alias: String, + #[serde(rename = "UserDisplayName")] + pub user_name: String, + pub user_icon_url: String, + pub user_id: usize, + pub user_guid: String, +} + +/// 根据闪存ID发表一个评论 +pub async fn post(token: String, sc: StatusComment) -> Result { + let r = Client::new() + .post(openapi!("/statuses/{}/comments", sc.parent_comment_id)) + .pat_auth(token.as_str()) + .form(&sc) + .send() + .await? + .error_for_status()?; + Ok(r) +} + +/// 根据闪存ID获取评论 +pub async fn get(token: &str, status_id: &str) -> Result> { + let r = Client::new() + .get(openapi!("/statuses/{}/comments", status_id)) + .pat_auth(token) + .send() + .await? + .error_for_status()? + .json() + .await?; + Ok(r) +} + +/// 根据闪存ID和commentid删除评论 +pub async fn delete(token: &str, status_id: &str, comment_id: &str) -> Result<()> { + Client::new() + .delete(openapi!("/statuses/{}/comments/{}", status_id, comment_id)) + .pat_auth(token) + .send() + .await? + .error_for_status()?; + Ok(()) +} diff --git a/src/apis/ing/mod.rs b/src/apis/ing/mod.rs new file mode 100644 index 0000000..7f76ffe --- /dev/null +++ b/src/apis/ing/mod.rs @@ -0,0 +1,197 @@ +//! cnblogs 闪存接口模块 +//! +//! 实现封装[cnblogs Api](https://api.cnblogs.com/Help#0aee001a01835c83a3277a500ffc9040)中的`Statuses`。 +//! +//! - 获取自己最新一条闪存内容 https://api.cnblogs.com/api/statuses/recent +//! - 发布闪存评论 https://api.cnblogs.com/api/statuses/{statusId}/comments +//! - 获取闪存评论 https://api.cnblogs.com/api/statuses/{statusId}/comments +//! - 删除闪存评论 https://api.cnblogs.com/api/statuses/{statusId}/comments/{id} +//! - 发布闪存 https://api.cnblogs.com/api/statuses +//! - 删除闪存 https://api.cnblogs.com/api/statuses/{id} +//! - 根据类型获取闪存列表 https://api.cnblogs.com/api/statuses/@{type}?pageIndex={pageIndex}&pageSize={pageSize}&tag={tag} +//! - 根据Id获取闪存 https://api.cnblogs.com/api/statuses/{id} +//! + +pub mod comment; + +use anyhow::{Ok, Result}; +use reqwest::{Client, Response}; +use serde::{Deserialize, Serialize}; +use serde_repr::{Deserialize_repr, Serialize_repr}; + +use crate::{infra::http::RequestBuilderExt, openapi}; + +#[derive(Debug, Default, Serialize, Deserialize)] +#[serde(rename_all = "PascalCase")] +#[serde(default)] +pub struct IngContent { + pub content: String, + pub is_private: bool, + pub client_type: IngSendFrom, +} + +#[derive(Clone, Debug, Serialize_repr, Deserialize_repr)] +#[repr(u8)] +pub enum IngSendFrom { + None = 0, + Ms = 1, + GTalk = 2, + Qq = 3, + Sms = 5, + CellPhone = 6, + Web = 8, + VsCode = 9, + Cli = 13, +} + +impl Default for IngSendFrom { + fn default() -> Self { + return IngSendFrom::Cli; + } +} + +/// 查询条件,用于根据类别查询 +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[serde(default)] +pub struct QeurySet { + #[serde(skip)] + pub types: QueryIngType, + pub page_index: u64, + pub page_size: u64, + #[serde(skip_serializing_if = "String::is_empty")] + pub tag: String, +} + +impl Default for QeurySet { + fn default() -> Self { + return Self { + types: QueryIngType::default(), + page_index: 1, + page_size: 30, + tag: "".to_string(), + }; + } +} + +/// +/// Follow = 1, 关注 +/// Myself = 4, 我的 +/// Public = 5, +/// RecentComment = 6, //新回应 +/// MyComment = 7, 我回应 +/// Tag = 10, tag 必填 +/// Comment = 13 回复我 +/// Mention = 14, +#[derive(Debug)] +pub enum QueryIngType { + Following, + My, + MyComment, + RecentComment, + Mention, + Comment, + All, +} + +impl Default for QueryIngType { + fn default() -> Self { + return Self::All; + } +} + +impl QueryIngType { + fn as_u8(&self) -> u8 { + match self { + QueryIngType::Following => 1, + QueryIngType::My => 4, + QueryIngType::All => 5, + QueryIngType::RecentComment => 6, + QueryIngType::MyComment => 7, + QueryIngType::Mention => 14, + QueryIngType::Comment => 13, + } + } +} + +/// 闪存详细内容。 +/// +/// 用于根据ID查询闪存的结果解析。 +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct IngEntry { + pub id: u64, + pub content: String, + pub is_private: bool, + pub is_lucky: bool, + pub comment_count: u64, + pub date_added: String, + pub user_alias: String, + pub user_display_name: String, + pub user_icon_url: String, + pub user_id: u64, + pub user_guid: String, + pub send_from: u8, + pub icons: String, +} + +pub async fn lastest(token: &str) -> Result { + let c = Client::new() + .get(openapi!("/statuses/recent")) + .pat_auth(token) + .send() + .await? + .error_for_status()?; + Ok(c) +} + +/// 根据条件查询 +/// +/// 如果是tag是,一定要传入Tag,tag是自己想查询的比如Linux,Debian,Python等等。 +/// 页数是从1开始的 +pub async fn query(token: &str, q: &QeurySet) -> Result> { + let r = Client::new() + .get(openapi!("/statuses/@{}", q.types.as_u8())) + .pat_auth(token) + .query(&q) + .send() + .await? + .error_for_status()? + .json::>() + .await?; + Ok(r) +} + +/// 根据ID查询 +pub async fn query_by_id(token: &str, id: &str) -> Result { + let r = Client::new() + .get(openapi!("/statuses/{}", id)) + .pat_auth(token) + .send() + .await? + .error_for_status()? + .json::() + .await?; + Ok(r) +} + +/// 发布一条闪存 +pub async fn post(token: &str, c: &IngContent) -> Result { + let r = Client::new() + .post(openapi!("/statuses")) + .pat_auth(token) + .json(c) + .send() + .await?; + Ok(r) +} + +/// 删除一条闪存 +pub async fn delete(token: &str, id: String) -> Result { + let r = Client::new() + .post(openapi!("/statuses/{}", id)) + .pat_auth(token) + .send() + .await?; + Ok(r) +} diff --git a/src/apis/mod.rs b/src/apis/mod.rs index b2fa28a..67572a9 100644 --- a/src/apis/mod.rs +++ b/src/apis/mod.rs @@ -11,4 +11,9 @@ //! - token: 认证相关 //! - marks: 收藏相关 -pub mod statuses; +pub mod ing; +pub mod token; + +pub const OAUTH_CLIENT: &str = "https://api.cnblogs.com/token"; +pub const OAUTH_TOKEN: &str = "https://oauth.cnblogs.com/connect/token"; +pub const OAUTHORIZE: &str = "https://oauth.cnblogs.com/connect/authorize"; diff --git a/src/apis/statuses/mod.rs b/src/apis/statuses/mod.rs deleted file mode 100644 index 2347f22..0000000 --- a/src/apis/statuses/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! cnblogs 闪存接口模块 -//! -//! 实现封装[cnblogs Api](https://api.cnblogs.com/Help#0aee001a01835c83a3277a500ffc9040)中的`Statuses`。 -//! -//! - 获取最新一条闪存内容 https://api.cnblogs.com/api/statuses/recent -//! - 发布闪存评论 https://api.cnblogs.com/api/statuses/{statusId}/comments -//! - 获取闪存评论 https://api.cnblogs.com/api/statuses/{statusId}/comments -//! - 删除闪存评论 https://api.cnblogs.com/api/statuses/{statusId}/comments/{id} -//! - 发布闪存 https://api.cnblogs.com/api/statuses -//! - 删除闪存 https://api.cnblogs.com/api/statuses/{id} -//! - 根据类型获取闪存列表 https://api.cnblogs.com/api/statuses/@{type}?pageIndex={pageIndex}&pageSize={pageSize}&tag={tag} -//! - 根据Id获取闪存 https://api.cnblogs.com/api/statuses/{id} -//! diff --git a/src/apis/token/mod.rs b/src/apis/token/mod.rs new file mode 100644 index 0000000..d41ff78 --- /dev/null +++ b/src/apis/token/mod.rs @@ -0,0 +1,134 @@ +//! Token +//! +//! TokenApi的封装 +//! +//! OAuth认证,提供两种方式接口。 +//! +//! 1. Client_Credentials +//! 2. Authorization_Code +//! + +use super::{OAUTH_CLIENT, OAUTH_TOKEN}; +use anyhow::Result; +use reqwest::Client; +use serde::{Deserialize, Serialize}; + +/// 认证授权后的授权 +/// +/// # Filed +/// +/// - access_token: Token String +/// - expires_in: 过期时间 +/// - token_type: Token认证方式 +/// - refresh_token: 过期后刷新Token,如果是ClientCredentials,此字段无用。 +/// - id_token: id,如果是ClientCredentials,此字段无用 +/// - scope: 客户端权限 +/// +#[derive(Debug, Serialize, Deserialize, Default)] +#[serde(default)] +pub struct OAuthToken { + pub id_token: String, + pub access_token: String, + pub expires_in: u64, + pub token_type: String, + pub refresh_token: String, + pub scope: String, +} + +#[derive(Serialize, Deserialize, Debug, Default)] +#[serde(default)] +pub struct ClientCredentialsReq { + pub client_id: String, + pub client_secret: String, + pub grant_type: String, +} + +pub async fn client_credentials(req: ClientCredentialsReq) -> Result { + let c = Client::new().post(OAUTH_CLIENT); + let r = c + .form(&req) + .send() + .await? + .error_for_status()? + .json::() + .await?; + Ok(r) +} + +/// OAuth 获取Token结构体 +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct OauthTokenReq { + // pub client_id: String, // string 是 授权ID client_id + // pub client_secret: String, // string 是 密钥 client_secret + // pub grant_type: String, // string 是 授权模式 authorization_code + #[serde(flatten)] + pub cc: ClientCredentialsReq, + pub code: String, // string 是 授权码 code + pub redirect_uri: String, // string 是 回调地址(默认) https://oauth.cnblogs.com/auth/callback +} + +impl OauthTokenReq { + pub fn new(client_id: String, client_secret: String, code: String) -> Self { + OauthTokenReq { + cc: ClientCredentialsReq { + client_id, + client_secret, + grant_type: "authorization_code".to_string(), + }, + code, + redirect_uri: "https://oauth.cnblogs.com/auth/callback".to_string(), + } + } +} + +/// 获取令牌 +pub async fn authorization_code(req: OauthTokenReq) -> Result { + let c = Client::new().post(OAUTH_TOKEN); + let r = c + .form(&req) + .send() + .await? + .error_for_status()? + .json::() + .await?; + Ok(r) +} + +/// OAuth 获取Token结构体 +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct RefreshTokenReq { + // pub client_id: String, + // pub client_secret: String, + // pub grant_type: String, + #[serde(flatten)] + pub cc: ClientCredentialsReq, + pub refresh_token: String, +} + +impl RefreshTokenReq { + pub fn new(client_id: String, client_secret: String, refresh_token: String) -> Self { + return RefreshTokenReq { + cc: ClientCredentialsReq { + client_id, + client_secret, + grant_type: "refresh_token".to_string(), + }, + refresh_token, + }; + } +} + +/// 刷新令牌 +/// +/// 令牌过期后重新获取。 +pub async fn refresh_token(req: RefreshTokenReq) -> Result { + let c = Client::new().post(OAUTH_TOKEN); + let r = c + .form(&req) + .send() + .await? + .error_for_status()? + .json::() + .await?; + Ok(r) +}