feat: implement repo fetching, add sentry
This commit is contained in:
@@ -4,13 +4,15 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{AppState, account::AccountRepository, error::Result};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct Repository {
|
||||
id: u64,
|
||||
name: String,
|
||||
full_name: String,
|
||||
description: String,
|
||||
language: String,
|
||||
stars: usize,
|
||||
description: Option<String>,
|
||||
language: Option<String>,
|
||||
#[serde(alias = "stargazers_count")]
|
||||
stars: Option<usize>,
|
||||
updated_at: DateTime<Utc>,
|
||||
private: bool,
|
||||
}
|
||||
@@ -25,11 +27,13 @@ pub async fn get_repos(
|
||||
|
||||
let response = app_state
|
||||
.reqwest_client
|
||||
.get("https://api.github.com/user/repos")
|
||||
.get("https://api.github.com/user/repos?affiliation=owner")
|
||||
.bearer_auth(token)
|
||||
.send()
|
||||
.await?;
|
||||
response.error_for_status_ref()?;
|
||||
let data = response.json::<Vec<Repository>>().await?;
|
||||
tracing::debug!(github_response = ?data.iter().filter(|r| r.private == true).collect::<Vec<&Repository>>(), "received repos");
|
||||
|
||||
Ok(HttpResponse::Ok().json(response.json::<Vec<Repository>>().await?))
|
||||
Ok(HttpResponse::Ok().json(data))
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ struct ErrorResponse {
|
||||
impl ResponseError for Error {
|
||||
fn error_response(&self) -> actix_web::HttpResponse<actix_web::body::BoxBody> {
|
||||
match self {
|
||||
Error::AccessToken => HttpResponse::Unauthorized().finish(),
|
||||
Error::AccessToken => HttpResponse::BadRequest().finish(),
|
||||
Error::Unauthorized => HttpResponse::Unauthorized().finish(),
|
||||
Error::TokenExpired => HttpResponse::Unauthorized().json(ErrorResponse {
|
||||
error: "token expired".to_string(),
|
||||
|
||||
@@ -6,8 +6,12 @@ mod middleware;
|
||||
|
||||
use std::env;
|
||||
|
||||
use actix_web::{App, HttpServer, middleware::from_fn, web};
|
||||
use actix_web::{App, HttpServer, middleware::from_fn, rt::System, web};
|
||||
use sqlx::PgPool;
|
||||
use tracing::level_filters::LevelFilter;
|
||||
use tracing_subscriber::{
|
||||
EnvFilter, fmt::format::FmtSpan, layer::SubscriberExt, util::SubscriberInitExt,
|
||||
};
|
||||
|
||||
use crate::auth::{Auth, JWT};
|
||||
|
||||
@@ -17,9 +21,15 @@ struct AppState {
|
||||
auth: Auth,
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
let reqwest_client = reqwest::Client::new();
|
||||
async fn run() -> std::io::Result<()> {
|
||||
let reqwest_client = reqwest::Client::builder()
|
||||
.user_agent(concat!(
|
||||
env!("CARGO_PKG_NAME"),
|
||||
"/",
|
||||
env!("CARGO_PKG_VERSION")
|
||||
))
|
||||
.build()
|
||||
.expect("failed to create reqwest client");
|
||||
let app_data = web::Data::new(AppState {
|
||||
reqwest_client: reqwest_client.clone(),
|
||||
pool: PgPool::connect(
|
||||
@@ -33,21 +43,74 @@ async fn main() -> std::io::Result<()> {
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
.app_data(app_data.clone())
|
||||
.wrap(
|
||||
sentry::integrations::actix::Sentry::builder()
|
||||
.capture_server_errors(true)
|
||||
.start_transaction(true)
|
||||
.finish(),
|
||||
)
|
||||
.wrap(tracing_actix_web::TracingLogger::default())
|
||||
.route(
|
||||
"/",
|
||||
web::get()
|
||||
.to(async || concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"))),
|
||||
)
|
||||
.service(
|
||||
web::scope("/api")
|
||||
.route(
|
||||
"/{user_id}/repos",
|
||||
web::get().to(endpoints::get_repos::get_repos),
|
||||
)
|
||||
.wrap(from_fn(middleware::protected)),
|
||||
web::scope("/api").service(
|
||||
web::scope("/v0").service(
|
||||
web::scope("/user")
|
||||
.route(
|
||||
"/{user_id}/repos",
|
||||
web::get().to(endpoints::get_repos::get_repos),
|
||||
)
|
||||
.wrap(from_fn(middleware::protected)),
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
.bind(("127.0.0.1", 8080))?
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
|
||||
// cant use #[actix_web::main] because of Sentry
|
||||
// Note: "Macros like #[tokio::main] and #[actix_web::main] are not supported. The Sentry client must be initialized before the async runtime is started."
|
||||
// https://docs.sentry.io/platforms/rust/guides/actix-web/
|
||||
fn main() -> std::io::Result<()> {
|
||||
let tracing_env_filter = EnvFilter::builder()
|
||||
.with_default_directive(LevelFilter::INFO.into())
|
||||
.from_env_lossy()
|
||||
.add_directive("reqwest=info".parse().unwrap())
|
||||
.add_directive("hyper=info".parse().unwrap())
|
||||
.add_directive("h2=info".parse().unwrap())
|
||||
.add_directive("rustls=info".parse().unwrap());
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(tracing_env_filter)
|
||||
.with(
|
||||
tracing_subscriber::fmt::layer()
|
||||
.compact()
|
||||
.with_span_events(FmtSpan::CLOSE),
|
||||
)
|
||||
.with(sentry::integrations::tracing::layer())
|
||||
.init();
|
||||
|
||||
let guard = sentry::init((
|
||||
env::var("SENTRY_DSN").ok(),
|
||||
sentry::ClientOptions {
|
||||
release: sentry::release_name!(),
|
||||
traces_sample_rate: 1.0,
|
||||
session_mode: sentry::SessionMode::Request,
|
||||
debug: true,
|
||||
..Default::default()
|
||||
},
|
||||
));
|
||||
|
||||
if guard.is_enabled() {
|
||||
tracing::info!("sentry initialized");
|
||||
} else {
|
||||
tracing::info!("sentry **NOT** initialized")
|
||||
};
|
||||
|
||||
System::new().block_on(run())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user