diff --git a/server/src/db/mod.rs b/server/src/db/mod.rs index d81df72..69691fb 100644 --- a/server/src/db/mod.rs +++ b/server/src/db/mod.rs @@ -2,8 +2,12 @@ pub mod query; use crate::config::DbConfig; -use sea_orm::{ConnectionTrait, Database, DbConn}; -use serde::Serialize; +use sea_orm::{ + ActiveModelTrait, + ActiveValue::{NotSet, Set}, + ConnectionTrait, Database, DbConn, EntityTrait, TransactionTrait, +}; +use serde::{Deserialize, Serialize}; type Result = std::result::Result; @@ -17,6 +21,61 @@ pub struct FullPackage { files: Vec, } +#[derive(Serialize, Deserialize)] +#[serde(tag = "type")] +pub enum RepoType { + Regular, + FullMirror { mirrors: Vec }, +} + +#[derive(Serialize, Deserialize)] +pub struct NewRepo { + distro_id: i32, + name: String, + description: Option, + #[serde(flatten)] + r#type: RepoType, +} + +impl NewRepo { + pub async fn insert(self, conn: &DbConn) -> crate::Result { + let txn = conn.begin().await?; + + let repo_type = match self.r#type { + RepoType::Regular => entity::RepoType::Regular, + RepoType::FullMirror { .. } => entity::RepoType::FullMirror, + }; + + let repo = entity::repo::ActiveModel { + id: NotSet, + distro_id: Set(self.distro_id), + name: Set(self.name), + description: Set(self.description), + r#type: Set(repo_type), + }; + let model = repo.insert(conn).await?; + + match self.r#type { + RepoType::Regular => {} + RepoType::FullMirror { mirrors } => { + entity::RepoMirror::insert_many(mirrors.into_iter().map(|url| { + entity::repo_mirror::ActiveModel { + id: NotSet, + repo_id: Set(model.id), + url: Set(url), + } + })) + .on_empty_do_nothing() + .exec(&txn) + .await?; + } + } + + txn.commit().await?; + Ok(model) + } +} + pub async fn connect(conn: &DbConfig) -> crate::Result { match conn { DbConfig::Sqlite { diff --git a/server/src/repo/handle.rs b/server/src/repo/handle.rs index 1dd34cb..6f07a7a 100644 --- a/server/src/repo/handle.rs +++ b/server/src/repo/handle.rs @@ -30,9 +30,16 @@ impl Handle { }) } - pub async fn get_or_create_repo(&self, distro: &str, repo: &str) -> crate::Result { - let mut repos = self.state.repos.write().await; + pub async fn register_repo(&self, repo_id: i32) -> crate::Result<()> { + tokio::fs::create_dir(self.state.repos_dir.join(repo_id.to_string())).await?; + let mut repos = self.state.repos.write().await; + repos.insert(repo_id, Default::default()); + + Ok(()) + } + + pub async fn get_or_create_repo(&self, distro: &str, repo: &str) -> crate::Result { let distro_id: Option = entity::Distro::find() .filter(entity::distro::Column::Name.eq(distro)) .select_only() @@ -74,8 +81,7 @@ impl Handle { }; let id = new_repo.insert(&self.state.conn).await?.id; - tokio::fs::create_dir(self.state.repos_dir.join(id.to_string())).await?; - repos.insert(id, Default::default()); + self.register_repo(id).await?; id }; diff --git a/server/src/web/api/mod.rs b/server/src/web/api/mod.rs index 81494b3..dc99e53 100644 --- a/server/src/web/api/mod.rs +++ b/server/src/web/api/mod.rs @@ -1,17 +1,19 @@ mod pagination; +mod repo; use crate::db; use pagination::PaginatedResponse; use axum::{ extract::{Path, Query, State}, + http::StatusCode, routing::get, Json, Router, }; pub fn router() -> Router { Router::new() - .route("/repos", get(get_repos)) + .route("/repos", get(get_repos).post(post_repo)) .route("/repos/:id", get(get_single_repo)) .route("/packages", get(get_packages)) .route("/packages/:id", get(get_single_package)) @@ -39,6 +41,16 @@ async fn get_single_repo( Ok(Json(repo)) } +async fn post_repo( + State(global): State, + Json(repo): Json, +) -> crate::Result<(StatusCode, Json)> { + let model = repo.insert(&global.db).await?; + global.repo.register_repo(model.id).await?; + + Ok((StatusCode::CREATED, Json(model))) +} + async fn get_packages( State(global): State, Query(pagination): Query,