diff --git a/crates/collab/src/api.rs b/crates/collab/src/api.rs index 7a1e4f2b50..f5902114a1 100644 --- a/crates/collab/src/api.rs +++ b/crates/collab/src/api.rs @@ -1,3 +1,4 @@ +pub mod contributors; pub mod events; pub mod extensions; pub mod ips_file; @@ -5,13 +6,13 @@ pub mod slack; use crate::{ auth, - db::{ContributorSelector, User, UserId}, + db::{User, UserId}, rpc, AppState, Error, Result, }; use anyhow::anyhow; use axum::{ body::Body, - extract::{self, Path, Query}, + extract::{Path, Query}, http::{self, Request, StatusCode}, middleware::{self, Next}, response::IntoResponse, @@ -19,9 +20,8 @@ use axum::{ Extension, Json, Router, }; use axum_extra::response::ErasedJson; -use chrono::{NaiveDateTime, SecondsFormat}; use serde::{Deserialize, Serialize}; -use std::sync::{Arc, OnceLock}; +use std::sync::Arc; use tower::ServiceBuilder; pub use extensions::fetch_extensions_from_blob_store_periodically; @@ -31,8 +31,7 @@ pub fn routes(rpc_server: Option>, state: Arc) -> Rou .route("/user", get(get_authenticated_user)) .route("/users/:id/access_tokens", post(create_access_token)) .route("/rpc_server_snapshot", get(get_rpc_server_snapshot)) - .route("/contributors", get(get_contributors).post(add_contributor)) - .route("/contributor", get(check_is_contributor)) + .merge(contributors::router()) .layer( ServiceBuilder::new() .layer(Extension(state)) @@ -126,107 +125,6 @@ async fn get_rpc_server_snapshot( Ok(ErasedJson::pretty(rpc_server.snapshot().await)) } -async fn get_contributors(Extension(app): Extension>) -> Result>> { - Ok(Json(app.db.get_contributors().await?)) -} - -#[derive(Debug, Deserialize)] -struct CheckIsContributorParams { - github_user_id: Option, - github_login: Option, -} - -impl CheckIsContributorParams { - fn as_contributor_selector(self) -> Result { - if let Some(github_user_id) = self.github_user_id { - return Ok(ContributorSelector::GitHubUserId { github_user_id }); - } - - if let Some(github_login) = self.github_login { - return Ok(ContributorSelector::GitHubLogin { github_login }); - } - - Err(anyhow!( - "must be one of `github_user_id` or `github_login`." - ))? - } -} - -#[derive(Debug, Serialize)] -struct CheckIsContributorResponse { - signed_at: Option, -} - -async fn check_is_contributor( - Extension(app): Extension>, - Query(params): Query, -) -> Result> { - let params = params.as_contributor_selector()?; - - if RenovateBot::is_renovate_bot(¶ms) { - return Ok(Json(CheckIsContributorResponse { - signed_at: Some( - RenovateBot::created_at() - .and_utc() - .to_rfc3339_opts(SecondsFormat::Millis, true), - ), - })); - } - - Ok(Json(CheckIsContributorResponse { - signed_at: app - .db - .get_contributor_sign_timestamp(¶ms) - .await? - .map(|ts| ts.and_utc().to_rfc3339_opts(SecondsFormat::Millis, true)), - })) -} - -/// The Renovate bot GitHub user (`renovate[bot]`). -/// -/// https://api.github.com/users/renovate[bot] -struct RenovateBot; - -impl RenovateBot { - const LOGIN: &'static str = "renovate[bot]"; - const USER_ID: i32 = 29139614; - - /// Returns the `created_at` timestamp for the Renovate bot user. - fn created_at() -> &'static NaiveDateTime { - static CREATED_AT: OnceLock = OnceLock::new(); - CREATED_AT.get_or_init(|| { - chrono::DateTime::parse_from_rfc3339("2017-06-02T07:04:12Z") - .expect("failed to parse 'created_at' for 'renovate[bot]'") - .naive_utc() - }) - } - - /// Returns whether the given contributor selector corresponds to the Renovate bot user. - fn is_renovate_bot(contributor: &ContributorSelector) -> bool { - match contributor { - ContributorSelector::GitHubLogin { github_login } => github_login == Self::LOGIN, - ContributorSelector::GitHubUserId { github_user_id } => { - github_user_id == &Self::USER_ID - } - } - } -} - -async fn add_contributor( - Extension(app): Extension>, - extract::Json(params): extract::Json, -) -> Result<()> { - let initial_channel_id = app.config.auto_join_channel_id; - app.db - .add_contributor( - ¶ms.github_login, - params.github_user_id, - params.github_email.as_deref(), - initial_channel_id, - ) - .await -} - #[derive(Deserialize)] struct CreateAccessTokenQueryParams { public_key: String, diff --git a/crates/collab/src/api/contributors.rs b/crates/collab/src/api/contributors.rs new file mode 100644 index 0000000000..5d899dbd46 --- /dev/null +++ b/crates/collab/src/api/contributors.rs @@ -0,0 +1,121 @@ +use std::sync::{Arc, OnceLock}; + +use anyhow::anyhow; +use axum::{ + extract::{self, Query}, + routing::get, + Extension, Json, Router, +}; +use chrono::{NaiveDateTime, SecondsFormat}; +use serde::{Deserialize, Serialize}; + +use crate::api::AuthenticatedUserParams; +use crate::db::ContributorSelector; +use crate::{AppState, Result}; + +pub fn router() -> Router { + Router::new() + .route("/contributors", get(get_contributors).post(add_contributor)) + .route("/contributor", get(check_is_contributor)) +} + +async fn get_contributors(Extension(app): Extension>) -> Result>> { + Ok(Json(app.db.get_contributors().await?)) +} + +#[derive(Debug, Deserialize)] +struct CheckIsContributorParams { + github_user_id: Option, + github_login: Option, +} + +impl CheckIsContributorParams { + fn as_contributor_selector(self) -> Result { + if let Some(github_user_id) = self.github_user_id { + return Ok(ContributorSelector::GitHubUserId { github_user_id }); + } + + if let Some(github_login) = self.github_login { + return Ok(ContributorSelector::GitHubLogin { github_login }); + } + + Err(anyhow!( + "must be one of `github_user_id` or `github_login`." + ))? + } +} + +#[derive(Debug, Serialize)] +struct CheckIsContributorResponse { + signed_at: Option, +} + +async fn check_is_contributor( + Extension(app): Extension>, + Query(params): Query, +) -> Result> { + let params = params.as_contributor_selector()?; + + if RenovateBot::is_renovate_bot(¶ms) { + return Ok(Json(CheckIsContributorResponse { + signed_at: Some( + RenovateBot::created_at() + .and_utc() + .to_rfc3339_opts(SecondsFormat::Millis, true), + ), + })); + } + + Ok(Json(CheckIsContributorResponse { + signed_at: app + .db + .get_contributor_sign_timestamp(¶ms) + .await? + .map(|ts| ts.and_utc().to_rfc3339_opts(SecondsFormat::Millis, true)), + })) +} + +/// The Renovate bot GitHub user (`renovate[bot]`). +/// +/// https://api.github.com/users/renovate[bot] +struct RenovateBot; + +impl RenovateBot { + const LOGIN: &'static str = "renovate[bot]"; + const USER_ID: i32 = 29139614; + + /// Returns the `created_at` timestamp for the Renovate bot user. + fn created_at() -> &'static NaiveDateTime { + static CREATED_AT: OnceLock = OnceLock::new(); + CREATED_AT.get_or_init(|| { + chrono::DateTime::parse_from_rfc3339("2017-06-02T07:04:12Z") + .expect("failed to parse 'created_at' for 'renovate[bot]'") + .naive_utc() + }) + } + + /// Returns whether the given contributor selector corresponds to the Renovate bot user. + fn is_renovate_bot(contributor: &ContributorSelector) -> bool { + match contributor { + ContributorSelector::GitHubLogin { github_login } => github_login == Self::LOGIN, + ContributorSelector::GitHubUserId { github_user_id } => { + github_user_id == &Self::USER_ID + } + } + } +} + +async fn add_contributor( + Extension(app): Extension>, + extract::Json(params): extract::Json, +) -> Result<()> { + let initial_channel_id = app.config.auto_join_channel_id; + app.db + .add_contributor( + ¶ms.github_login, + params.github_user_id, + params.github_email.as_deref(), + initial_channel_id, + ) + .await +}