feat: add repo validation
This commit is contained in:
@@ -1,19 +1,15 @@
|
||||
use crate::{auth::User, error::Result, user::RepositorySchema};
|
||||
use crate::{auth::User, error::Result, user::RepositorySchema, validate::validate_repo};
|
||||
use actix_web::{HttpResponse, web};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::AppState;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct AddResponse {
|
||||
id: String,
|
||||
}
|
||||
|
||||
pub async fn add_repo(
|
||||
app_state: web::Data<AppState>,
|
||||
user: web::ReqData<User>,
|
||||
payload: web::Json<RepositorySchema>,
|
||||
) -> Result<HttpResponse> {
|
||||
let repo = payload.into_inner();
|
||||
validate_repo(app_state.clone(), &repo).await?;
|
||||
app_state.user.add_repository(&user.id, repo).await
|
||||
}
|
||||
|
||||
8
api/src/endpoints/global_repos.rs
Normal file
8
api/src/endpoints/global_repos.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
use crate::error::Result;
|
||||
use actix_web::{HttpResponse, web};
|
||||
|
||||
use crate::AppState;
|
||||
|
||||
pub async fn global_repos(app_state: web::Data<AppState>) -> Result<HttpResponse> {
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod add_repo;
|
||||
pub mod get_repos;
|
||||
pub mod global_repos;
|
||||
pub mod search_repos;
|
||||
|
||||
@@ -23,6 +23,8 @@ pub enum Error {
|
||||
DynamoDB(String),
|
||||
#[error("item already exists")]
|
||||
AlreadyExists,
|
||||
#[error("validation failed: {0}")]
|
||||
ValidationFailed(String),
|
||||
}
|
||||
|
||||
impl<E: std::fmt::Debug> From<SdkError<E>> for Error {
|
||||
@@ -47,6 +49,9 @@ impl ResponseError for Error {
|
||||
Error::AlreadyExists => HttpResponse::BadRequest().json(ErrorResponse {
|
||||
error: "item already exists".to_string(),
|
||||
}),
|
||||
Error::ValidationFailed(msg) => HttpResponse::BadRequest().json(ErrorResponse {
|
||||
error: msg.clone(),
|
||||
}),
|
||||
_ => HttpResponse::InternalServerError().finish(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ mod endpoints;
|
||||
mod error;
|
||||
mod middleware;
|
||||
mod user;
|
||||
mod validate;
|
||||
|
||||
use std::env;
|
||||
|
||||
@@ -72,18 +73,23 @@ async fn run() -> std::io::Result<()> {
|
||||
)
|
||||
.service(
|
||||
web::scope("/api").service(
|
||||
web::scope("/v0").service(
|
||||
web::scope("/user")
|
||||
.route("/repos", web::get().to(endpoints::get_repos::get_repos))
|
||||
.wrap(from_fn(middleware::protected))
|
||||
.route(
|
||||
"/repos/search",
|
||||
web::get().to(endpoints::search_repos::search_repos),
|
||||
)
|
||||
.wrap(from_fn(middleware::protected))
|
||||
.route("/repo/add", web::post().to(endpoints::add_repo::add_repo))
|
||||
.wrap(from_fn(middleware::protected)),
|
||||
),
|
||||
web::scope("/v0")
|
||||
.service(
|
||||
web::scope("/user")
|
||||
.route("/repos", web::get().to(endpoints::get_repos::get_repos))
|
||||
.wrap(from_fn(middleware::protected))
|
||||
.route(
|
||||
"/repos/search",
|
||||
web::get().to(endpoints::search_repos::search_repos),
|
||||
)
|
||||
.wrap(from_fn(middleware::protected))
|
||||
.route("/repo/add", web::post().to(endpoints::add_repo::add_repo))
|
||||
.wrap(from_fn(middleware::protected)),
|
||||
)
|
||||
.route(
|
||||
"/repos",
|
||||
web::get().to(endpoints::global_repos::global_repos),
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -17,7 +17,7 @@ pub struct UserRepository {
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct RepositorySchema {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub full_name: String,
|
||||
}
|
||||
|
||||
impl UserRepository {
|
||||
@@ -58,7 +58,7 @@ impl UserRepository {
|
||||
.ok()?
|
||||
.strip_prefix("REPO#")?
|
||||
.to_string(),
|
||||
name: item.get("full_name")?.as_s().ok()?.to_string(),
|
||||
full_name: item.get("full_name")?.as_s().ok()?.to_string(),
|
||||
})
|
||||
})
|
||||
.collect::<Vec<RepositorySchema>>();
|
||||
@@ -66,6 +66,20 @@ impl UserRepository {
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub async fn global_repositories(&self) -> Result<HttpResponse> {
|
||||
let response = self
|
||||
.dynamodb_client
|
||||
.query()
|
||||
.key_condition_expression("pk = :pk")
|
||||
.expression_attribute_values(":pk", AttributeValue::S("REPOS".into()))
|
||||
.send()
|
||||
.await?
|
||||
.items()
|
||||
.iter()
|
||||
.filter_map(|item| Some(item.get("full_name")?.as_s().ok()?.to_string()))
|
||||
.collect::<Vec<String>>();
|
||||
}
|
||||
|
||||
pub async fn add_repository(
|
||||
&self,
|
||||
user_id: &str,
|
||||
@@ -79,7 +93,7 @@ impl UserRepository {
|
||||
.condition_expression("attribute_not_exists(sk)")
|
||||
.item("pk", AttributeValue::S(format!("USER#{user_id}")))
|
||||
.item("sk", AttributeValue::S(format!("REPO#{}", repo.id)))
|
||||
.item("full_name", AttributeValue::S(repo.name.to_string()))
|
||||
.item("full_name", AttributeValue::S(repo.full_name.to_string()))
|
||||
.item("gsi1pk", AttributeValue::S("REPOS".into()))
|
||||
.item("gsi1sk", AttributeValue::S(now.clone()))
|
||||
.item("imported_at", AttributeValue::S(now))
|
||||
|
||||
27
api/src/validate.rs
Normal file
27
api/src/validate.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use actix_web::web;
|
||||
|
||||
use crate::AppState;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::user::RepositorySchema;
|
||||
|
||||
pub async fn validate_repo(app_state: web::Data<AppState>, repo: &RepositorySchema) -> Result<()> {
|
||||
let response = app_state
|
||||
.reqwest_client
|
||||
.get(format!(
|
||||
"https://raw.githubusercontent.com/{}/HEAD/dist/index.html",
|
||||
repo.full_name
|
||||
))
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
match response.status() {
|
||||
reqwest::StatusCode::OK => Ok(()),
|
||||
reqwest::StatusCode::NOT_FOUND => Err(Error::ValidationFailed(
|
||||
"dist/index.html not found in repository".to_string(),
|
||||
)),
|
||||
status => Err(Error::ValidationFailed(format!(
|
||||
"failed to validate repository: HTTP {}",
|
||||
status
|
||||
))),
|
||||
}
|
||||
}
|
||||
@@ -73,11 +73,14 @@
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: repo.id.toString(),
|
||||
name: repo.full_name
|
||||
full_name: repo.full_name
|
||||
})
|
||||
});
|
||||
const data = await response.json();
|
||||
if (!response.ok) {
|
||||
toast.warning('Repository already imported');
|
||||
toast.warning(data.error);
|
||||
adding = null;
|
||||
return;
|
||||
} else {
|
||||
toast.success('Successfully added repository');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user