228 lines
7.4 KiB
Rust
228 lines
7.4 KiB
Rust
use diesel::prelude::*;
|
|
use gritea::client::Gritea;
|
|
use juniper::{graphql_object, EmptySubscription, FieldResult, IntoFieldError, RootNode};
|
|
use uuidv7::Uuid;
|
|
|
|
use crate::{db, gritea_ext::GriteaExt, worker, CONFIG};
|
|
|
|
pub mod context;
|
|
pub mod error;
|
|
pub mod loaders;
|
|
pub mod models;
|
|
pub mod scalars;
|
|
|
|
pub use context::Context;
|
|
pub use error::{AsyncResultIntoFieldResult, Error, QueryResultIntoFieldResult};
|
|
|
|
use loaders::TryOptionLoad;
|
|
|
|
pub struct Query;
|
|
|
|
#[graphql_object(context = Context)]
|
|
impl Query {
|
|
fn ping() -> &'static str {
|
|
"pong"
|
|
}
|
|
|
|
fn verify_login(username: String, password: String) -> bool {
|
|
username == *CONFIG.user && password == *CONFIG.password
|
|
}
|
|
|
|
async fn user(context: &Context, id: scalars::Uuid) -> FieldResult<models::user::User> {
|
|
match context.loaders.user.try_option_load(*id).await {
|
|
Ok(Some(user)) => Ok(user),
|
|
Ok(None) => Err(Error::DoesNotExist.into_field_error()),
|
|
Err(_) => Err(Error::Internal.into_field_error()),
|
|
}
|
|
}
|
|
|
|
async fn user_by_name(context: &Context, name: String) -> FieldResult<models::user::User> {
|
|
let db_conn = &mut context.get_db_conn()?;
|
|
let id = match db::schema::users::table
|
|
.select(db::schema::users::id)
|
|
.filter(db::schema::users::name.eq(name))
|
|
.first::<Uuid>(db_conn)
|
|
.optional()
|
|
.into_field_result()?
|
|
{
|
|
Some(x) => x,
|
|
None => return Err(Error::DoesNotExist.into_field_error()),
|
|
};
|
|
|
|
context
|
|
.loaders
|
|
.user
|
|
.try_load(id)
|
|
.await
|
|
.map_err(|_| Error::Internal.into_field_error())
|
|
}
|
|
|
|
async fn users(context: &Context) -> FieldResult<Vec<models::user::User>> {
|
|
let db_conn = &mut context.get_db_conn()?;
|
|
let ids = db::schema::users::table
|
|
.select(db::schema::users::id)
|
|
.load::<Uuid>(db_conn)
|
|
.into_field_result()?;
|
|
|
|
context.loaders.user.try_load_many(ids).await.map_or_else(
|
|
|_| Err(Error::Internal.into_field_error()),
|
|
|x| Ok(x.into_values().collect()),
|
|
)
|
|
}
|
|
|
|
async fn repository(
|
|
context: &Context,
|
|
id: scalars::Uuid,
|
|
) -> FieldResult<models::repository::Repository> {
|
|
match context.loaders.repository.try_option_load(*id).await {
|
|
Ok(Some(user)) => Ok(user),
|
|
Ok(None) => Err(Error::DoesNotExist.into_field_error()),
|
|
Err(_) => Err(Error::Internal.into_field_error()),
|
|
}
|
|
}
|
|
|
|
async fn repositories(context: &Context) -> FieldResult<Vec<models::repository::Repository>> {
|
|
let db_conn = &mut context.get_db_conn()?;
|
|
let ids = db::schema::repositories::table
|
|
.select(db::schema::repositories::id)
|
|
.load::<Uuid>(db_conn)
|
|
.into_field_result()?;
|
|
|
|
context
|
|
.loaders
|
|
.repository
|
|
.try_load_many(ids)
|
|
.await
|
|
.map_or_else(
|
|
|_| Err(Error::Internal.into_field_error()),
|
|
|x| Ok(x.into_values().collect()),
|
|
)
|
|
}
|
|
}
|
|
|
|
pub struct Mutation;
|
|
|
|
#[graphql_object(context = Context)]
|
|
impl Mutation {
|
|
async fn create_repository(
|
|
context: &Context,
|
|
input: models::repository::CreateRepositoryInput,
|
|
) -> FieldResult<models::repository::Repository> {
|
|
if !context.logged_in {
|
|
return Err(Error::Unauthenticated.into_field_error());
|
|
}
|
|
|
|
let db_conn = &mut context.get_db_conn()?;
|
|
|
|
let user_id = db::schema::users::table
|
|
.select(db::schema::users::id)
|
|
.filter(db::schema::users::name.eq(&input.user))
|
|
.first::<Uuid>(db_conn)
|
|
.optional()
|
|
.into_field_result()?;
|
|
if let Some(id) = user_id {
|
|
if diesel::select(diesel::dsl::exists(
|
|
db::schema::repositories::table
|
|
.filter(db::schema::repositories::user_id.eq(id))
|
|
.filter(db::schema::repositories::name.eq(&input.name)),
|
|
))
|
|
.get_result::<bool>(db_conn)
|
|
.into_field_result()?
|
|
{
|
|
return Err(Error::RepoAlreadyExists.into_field_error());
|
|
}
|
|
}
|
|
|
|
let escaped_user = urlencoding::encode(&input.user);
|
|
let escaped_name = urlencoding::encode(&input.name);
|
|
|
|
match Gritea::builder(&CONFIG.gitea_url)
|
|
.token(&*CONFIG.gitea_api_token)
|
|
.build()
|
|
{
|
|
Ok(client) => {
|
|
let repo = match client.get_repo(&escaped_user, &escaped_name).await {
|
|
Ok(x) => x,
|
|
Err(_) => return Err(Error::ExternalRepoDoesNotExist.into_field_error()),
|
|
};
|
|
if repo.private {
|
|
return Err(Error::ExternalRepoDoesNotExist.into_field_error());
|
|
}
|
|
|
|
let branches = match client.get_repo_branches(&escaped_user, &escaped_name).await {
|
|
Ok(x) => x,
|
|
Err(_) => return Err(Error::Internal.into_field_error()),
|
|
};
|
|
if !branches
|
|
.into_iter()
|
|
.any(|x| x.name == CONFIG.gitea_pull_branch)
|
|
{
|
|
return Err(Error::RepoPullBranchDoesNotExist.into_field_error());
|
|
}
|
|
}
|
|
Err(_) => return Err(Error::Internal.into_field_error()),
|
|
}
|
|
|
|
let user_id = match user_id {
|
|
Some(x) => x,
|
|
None => diesel::insert_into(db::schema::users::table)
|
|
.values(db::models::NewUser { name: &input.user })
|
|
.returning(db::schema::users::id)
|
|
.get_result::<Uuid>(db_conn)
|
|
.into_field_result()?,
|
|
};
|
|
|
|
let id = diesel::insert_into(db::schema::repositories::table)
|
|
.values(db::models::NewRepository {
|
|
user_id,
|
|
name: &input.name,
|
|
})
|
|
.returning(db::schema::repositories::id)
|
|
.get_result::<Uuid>(db_conn)
|
|
.into_field_result()?;
|
|
|
|
let worker_conn = context.get_worker_conn().await?;
|
|
worker_conn
|
|
.send_task(worker::get_repo::get_repo::new(id))
|
|
.await
|
|
.into_field_result()?;
|
|
|
|
context
|
|
.loaders
|
|
.repository
|
|
.try_load(id)
|
|
.await
|
|
.map_err(|_| Error::Internal.into_field_error())
|
|
}
|
|
|
|
async fn delete_repository(context: &Context, id: scalars::Uuid) -> FieldResult<bool> {
|
|
if !context.logged_in {
|
|
return Err(Error::Unauthenticated.into_field_error());
|
|
}
|
|
|
|
let db_conn = &mut context.get_db_conn()?;
|
|
if diesel::select(diesel::dsl::not(diesel::dsl::exists(
|
|
db::schema::repositories::table.filter(db::schema::repositories::id.eq(*id)),
|
|
)))
|
|
.get_result::<bool>(db_conn)
|
|
.into_field_result()?
|
|
{
|
|
return Err(Error::DoesNotExist.into_field_error());
|
|
}
|
|
|
|
let worker_conn = context.get_worker_conn().await?;
|
|
worker_conn
|
|
.send_task(worker::delete_repo::delete_repo::new(*id))
|
|
.await
|
|
.into_field_result()?;
|
|
|
|
Ok(true)
|
|
}
|
|
}
|
|
|
|
pub type Schema = RootNode<'static, Query, Mutation, EmptySubscription<Context>>;
|
|
|
|
pub fn schema() -> Schema {
|
|
Schema::new(Query, Mutation, EmptySubscription::new())
|
|
}
|