diff --git a/Cargo.toml b/Cargo.toml index a1bb35f..f564bf0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,4 @@ diesel = { version = "1.4.8", features= ["sqlite", "chrono"] } chrono = { version="0.4", features=["serde"] } rand = "0.8.5" dotenv = "0.15.0" +sha2 = "0.10.2" diff --git a/src/api/mod.rs b/src/api/mod.rs index 5cc46a4..73654d9 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -14,28 +14,46 @@ pub fn catch_401_error() -> Value { } pub struct CurrentUser { + id: Option, // tmp user has no id, only for block namehash: String, is_admin: bool, + custom_title: String, } #[rocket::async_trait] impl<'r> FromRequest<'r> for CurrentUser { type Error = (); async fn from_request(request: &'r Request<'_>) -> request::Outcome { + let rh = request.rocket().state::().unwrap(); + let mut cu: Option = None; + if let Some(token) = request.headers().get_one("User-Token") { - let conn = establish_connection(); - if let Some(user) = User::get_by_token(&conn, token) { - return request::Outcome::Success(CurrentUser { - namehash: request - .rocket() - .state::() - .unwrap() - .hash_with_salt(&user.name), - is_admin: user.is_admin, + let sp = token.split('_').collect::>(); + if sp.len() == 2 && sp[0] == rh.get_tmp_token() { + let namehash = rh.hash_with_salt(sp[1]); + cu = Some(CurrentUser { + id: None, + custom_title: format!("TODO: {}", &namehash), + namehash: namehash, + is_admin: false, }); + } else { + let conn = establish_connection(); + if let Some(user) = User::get_by_token(&conn, token) { + let namehash = rh.hash_with_salt(&user.name); + cu = Some(CurrentUser { + id: Some(user.id), + custom_title: format!("TODO: {}", &namehash), + namehash: namehash, + is_admin: user.is_admin, + }); + } } } - request::Outcome::Failure((Status::Unauthorized, ())) + match cu { + Some(u) => request::Outcome::Success(u), + None => request::Outcome::Failure((Status::Unauthorized, ())), + } } } @@ -77,6 +95,13 @@ impl<'r> Responder<'r, 'static> for APIError { } } +macro_rules! look { + ($s:expr) => { + format!("{}...{}", &$s[..2], &$s[$s.len() - 2..]) + }; +} + pub type API = Result; pub mod post; +pub mod systemlog; diff --git a/src/api/systemlog.rs b/src/api/systemlog.rs new file mode 100644 index 0000000..584dff7 --- /dev/null +++ b/src/api/systemlog.rs @@ -0,0 +1,15 @@ +use crate::api::{CurrentUser, API}; +use crate::random_hasher::RandomHasher; +use chrono::SubsecRound; +use rocket::serde::json::{json, Value}; +use rocket::State; + +#[get("/systemlog")] +pub fn get_systemlog(user: CurrentUser, rh: &State) -> API { + Ok(json!({ + "tmp_token": rh.get_tmp_token(), + "salt": look!(rh.salt), + "start_time": rh.start_time.round_subsecs(0), + "custom_title": user.custom_title, + })) +} diff --git a/src/main.rs b/src/main.rs index db81e5e..10b2f64 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,10 +4,10 @@ extern crate rocket; #[macro_use] extern crate diesel; -mod random_hasher; +mod api; mod models; +mod random_hasher; mod schema; -mod api; use random_hasher::RandomHasher; @@ -15,14 +15,16 @@ use random_hasher::RandomHasher; fn rocket() -> _ { load_env(); rocket::build() - .mount("/_api", routes![ - api::post::get_list, - api::post::get_one, - api::post::publish_post - ]) - .register("/_api", catchers![ - api::catch_401_error - ]) + .mount( + "/_api/v1", + routes![ + api::post::get_list, + api::post::get_one, + api::post::publish_post, + api::systemlog::get_systemlog, + ], + ) + .register("/_api", catchers![api::catch_401_error]) .manage(RandomHasher::get_random_one()) } diff --git a/src/random_hasher.rs b/src/random_hasher.rs index e88c51e..bdafda6 100644 --- a/src/random_hasher.rs +++ b/src/random_hasher.rs @@ -1,7 +1,10 @@ +use chrono::{offset::Local, DateTime}; use rand::{distributions::Alphanumeric, thread_rng, Rng}; +use sha2::{Digest, Sha256}; pub struct RandomHasher { - salt: String, + pub salt: String, + pub start_time: DateTime, } impl RandomHasher { @@ -12,11 +15,23 @@ impl RandomHasher { .take(16) .map(char::from) .collect(), + start_time: Local::now(), } } pub fn hash_with_salt(&self, text: &str) -> String { - // TODO - format!("hash({}+{})", self.salt, text) + let mut h = Sha256::new(); + h.update(text); + h.update(&self.salt); + format!("{:X}", h.finalize())[5..21].to_string() + } + + pub fn get_tmp_token(&self) -> String { + // 每15分钟变化一次 + self.hash_with_salt(&format!( + "{}_{}", + Local::now().timestamp() / 60 / 15, + self.start_time.timestamp_subsec_nanos() + )) } }