feat: add POST api route for creating mirror repos
ci/woodpecker/push/build Pipeline failed Details
ci/woodpecker/push/lint Pipeline failed Details

feat/mirror-api
Jef Roosens 2024-07-21 13:40:06 +02:00
parent cbb04a40e0
commit d39205b653
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
3 changed files with 84 additions and 7 deletions

View File

@ -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<T> = std::result::Result<T, sea_orm::DbErr>;
@ -17,6 +21,61 @@ pub struct FullPackage {
files: Vec<String>,
}
#[derive(Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum RepoType {
Regular,
FullMirror { mirrors: Vec<String> },
}
#[derive(Serialize, Deserialize)]
pub struct NewRepo {
distro_id: i32,
name: String,
description: Option<String>,
#[serde(flatten)]
r#type: RepoType,
}
impl NewRepo {
pub async fn insert(self, conn: &DbConn) -> crate::Result<entity::repo::Model> {
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<DbConn> {
match conn {
DbConfig::Sqlite {

View File

@ -30,9 +30,16 @@ impl Handle {
})
}
pub async fn get_or_create_repo(&self, distro: &str, repo: &str) -> crate::Result<i32> {
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<i32> {
let distro_id: Option<i32> = 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
};

View File

@ -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<crate::Global> {
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<crate::Global>,
Json(repo): Json<crate::db::NewRepo>,
) -> crate::Result<(StatusCode, Json<entity::repo::Model>)> {
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<crate::Global>,
Query(pagination): Query<pagination::Query>,